feat(cli): okf project/consume/check/skill, and a generated skill with no path into a checkout

The reading direction existed only for someone standing in a clone. `consume`,
`contract_check` and `skill` moved from `tools/` into the package and are
reachable as `okf consume`, `okf check` and `okf skill`; `okf project` is new
and does the whole thing in one command.

The red measurement: a consumption skill generated from a checkout carried 4
lines naming that checkout by absolute path, 2 of them the commands the skill
tells a reader to run. It now names `okf consume` and `okf check`, and a test
asserts this repository appears in it nowhere, with a known-positive so the
zero is a measurement rather than a search that could not find.

The `tools/` files stay as ALIASES, not re-exports: a re-export binds copies of
the names into a second module object, so a caller patching one patches a
binding the implementation never reads. Two tests that monkeypatch okf_consume
went green again only under the alias. Every published reproduction block runs
unchanged.

The template and docs/consumption-contract.md (the section 7.4 known-positive)
are force-included into the wheel from the file they are authored in, so both
travel with the commands that cannot run without them and there is still one
authored copy of each.

Step 0, before any of it: okf build's default gained Arm E (--table-grid),
with --no-table-grid as its opt-out. The default moved to D plus F earlier the
same day on Arm F's published 5 of 12 -- a figure measured with Arm E ON.
Without it the fold has no joined table to fold, and the shipped default scored
2 of 12 with docx 0 of 3. Measured on the operator's folder: 30 md / 15
concepts on the new default against 43 / 28 without Arm E.

Install measurement from a fresh uv tool install, empty folder, this repository
nowhere on PYTHONPATH: 5 documents in, 15 concepts out, 0 references to tools/
in the generated skill, okf check conformant (15 rules, 0 findings).

Deviation stated rather than hidden: the order asked that
tests/test_okf_consume.py be left untouched. Two assertions in it read a PATH,
which is the one thing this work changes. Both were moved and the second made
stronger -- it now asserts every command the README recipe names is a
subcommand the CLI registers, which a file existing on disk never proved.

Suite 1414 -> 1427. ruff clean, mypy --strict clean over 21 files.
Record: docs/2026-09-08-o5-okf-project.md

Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-08 21:47:38 +02:00
commit f6fea13299
21 changed files with 4017 additions and 2833 deletions

View file

@ -76,7 +76,7 @@ from .manifest import (
)
from .materialize import IngestResult, materialize_bundle
__version__ = "0.5.0a2"
__version__ = "0.6.0"
__all__ = [
"BlockedFile",

View file

@ -1,4 +1,11 @@
"""`okf` — the installed command. One subcommand today: `build`.
"""`okf` — the installed command. Five subcommands: the whole chain, packaged.
`build` a bundle from a folder, `consume` it for one question, `check` the
payload against the contract, generate a `skill` for it, or do the whole thing
in one step with `project`. Four of the five moved here on 2026-09-08 (O5)
from unpackaged scripts under `tools/`, which a consumer who installs this
library does not have; until then the reading direction existed only for
someone standing in a clone.
## What it replaces
@ -38,9 +45,12 @@ the replay dated differently passes both explicitly.
## What it does not decide
**Every arm is OFF unless the caller asks, and this command does not move a
default.** Which arm should ship as the default is the operator's decision and
is not taken here.
**Which arm ships as the default is the operator's decision and is not taken
here.** Three of the six are ON as of 2026-09-08 -- `--outline-run 3`,
`--table-grid` and `--unit-fold` -- each moved by the operator on a
measurement and each with an explicit opt-out. The remaining three are OFF
unless the caller asks. This module's job is to state the answer, never to
pick it.
The arms are, however, REACHABLE from here, and that is a change of 2026-09-08.
Until then `_propose_plans` called the proposer with no arm flag at all, so
@ -71,6 +81,32 @@ from .propose import run as propose_run
__all__ = ["DEFAULT_STAMP", "build", "main", "measure"]
#: The subcommands whose FLAGS belong to the module that implements them.
#:
#: Dispatched before `argparse` runs rather than rebuilt as subparsers here: a
#: second declaration of `--reserve-top-rank` or `--rarity-weight` is a second
#: place they can drift, and the pre-pass's flag set is the thing four
#: measurement reports are pinned to. Each entry is registered as a subparser
#: below anyway, so `okf --help` lists it and an unknown command is still an
#: error -- the parser knows the NAMES, the modules keep the flags.
#:
#: Imported lazily inside the dispatch: `okf build` should not pay to import
#: the ranker, and `okf consume` should not pay to import the proposer.
DELEGATED = ("consume", "check", "skill", "project")
def _delegate(command: str, argv: list[str]) -> int:
if command == "consume":
from .consume import main as run
elif command == "check":
from .contract_check import main as run
elif command == "skill":
from .skill import main as run
else:
from .project import main as run
return run(argv)
CLI_ID = "okf build"
#: What `okf build` runs when no flag is given. Moved 2026-09-08 by the
@ -89,7 +125,15 @@ CLI_ID = "okf build"
#: Each arm keeps an explicit opt-out: `--outline-run 0` (the number was
#: always its own switch) and `--no-unit-fold`. A default a caller cannot turn
#: off is not a default.
#: Arm E joined the default on 2026-09-08, one round after the other two, on a
#: measurement taken AFTER the first move rather than before it: Arm F's
#: published "5 of 12" was measured with Arm E on, and the round-3 default --
#: D plus F, Arm E off -- scored 2 of 12 with `docx` at 0 of 3. The fold's
#: table clause folds a table back into the heading that introduces it, and
#: with Arm E off a grid table is not one block but one block per rule line,
#: so there was nothing whole to fold. `--no-table-grid` is its opt-out.
DEFAULT_OUTLINE_RUN = 3
DEFAULT_TABLE_GRID = True
DEFAULT_UNIT_FOLD = True
#: The timestamp written when the caller passes none, for the ingest stamp and
@ -166,7 +210,7 @@ def build(
plans_dir: Path | None = None,
okf_type: str = "reference",
outline_run: int = DEFAULT_OUTLINE_RUN,
table_grid: bool = False,
table_grid: bool = DEFAULT_TABLE_GRID,
unit_fold: bool = DEFAULT_UNIT_FOLD,
keep_table_heading: bool = False,
sheet_section_rows: bool = False,
@ -254,9 +298,23 @@ def _write_log(bundle: Path, report: CorpusReport, *, profile: BundleProfile) ->
def parse_args(argv: list[str] | None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="okf",
description="OKF bundle tooling. One folder in, one bundle out.",
description=(
"OKF bundle tooling. One folder in, one bundle out -- and one command "
"from there to a bundle you can ask a question of."
),
)
subcommands = parser.add_subparsers(dest="command", required=True)
# Registered for `okf --help` and for the unknown-command error. Their
# arguments are NOT declared here: `main` hands the rest of `argv` to the
# implementing module, which owns them. `add_help=False` keeps
# `okf consume --help` reaching that module's help rather than this stub's.
for delegated, blurb in (
("consume", "cut a bundle to one contract-conformant payload for one question"),
("check", "check a consumption skill and one payload against the contract"),
("skill", "instantiate the consumption skill template for one bundle"),
("project", "folder in, bundle plus skill out: build and skill in one step"),
):
subcommands.add_parser(delegated, help=blurb, add_help=False)
build_parser = subcommands.add_parser(
"build",
help="build an OKF bundle from a folder of documents",
@ -336,12 +394,24 @@ def parse_args(argv: list[str] | None) -> argparse.Namespace:
build_parser.add_argument(
"--table-grid",
action="store_true",
default=DEFAULT_TABLE_GRID,
help=(
"Arm E, passed to the proposer unchanged: a pandoc grid-table rule "
"line no longer closes an open table block, so one grid table is one "
"concept instead of one per row group. Absent (the default) is OFF. "
"Measured on the K3 sample: it changes a `.docx` experience list from "
"21 concepts to 6"
"concept instead of one per row group. ON by default since "
"2026-09-08 (operator); the flag is kept so a call site that passes "
"it stays valid. Measured on the K3 sample: it changes a `.docx` "
"experience list from 21 concepts to 6, and without it Arm F has no "
"joined table to fold"
),
)
build_parser.add_argument(
"--no-table-grid",
action="store_false",
dest="table_grid",
help=(
"Arm E's explicit opt-out. With --outline-run 0 and --no-unit-fold "
"it reproduces the pre-2026-09-08 default byte for byte"
),
)
build_parser.add_argument(
@ -409,7 +479,10 @@ def parse_args(argv: list[str] | None) -> argparse.Namespace:
def main(argv: list[str] | None = None) -> int:
args = parse_args(argv)
arguments = list(sys.argv[1:] if argv is None else argv)
if arguments and arguments[0] in DELEGATED:
return _delegate(arguments[0], arguments[1:])
args = parse_args(arguments)
if not args.inbox.is_dir():
print(f"{CLI_ID}: FAILED - no such folder: {args.inbox}", file=sys.stderr)
return 2

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,503 @@
"""Check a consumption skill and one pre-pass payload against the contract.
`docs/consumption-contract.md` is normative prose. Prose has no test, so this
command reads the mechanically checkable half of it: the payload shape (SS 3, 5,
6, 7, 8) and the fixed vocabulary a conformant `SKILL.md` must carry (SS 4, 6).
**What it cannot see, said here rather than implied.** The division of labour
(SS 2) and the prohibitions (SS 9) are properties of a RUN -- whether an agent
went looking for context the pre-pass withheld, whether a retrieval tool was
pointed at the verdict layer. No static check reaches them. Conformance here is
the floor, never the proof, and a green run means the payload carries what a
claim would have to rest on -- not that the claim was made honestly.
**Every rule has its own code.** One "invalid" verdict over fourteen different
defects is a diagnostic no caller can act on. The codes are the contract's
paragraphs made addressable.
**The report carries denominators**, because the contract requires them of its
consumers and a checker exempting itself would be stating the rule it breaks:
how many rules ran, over how many excerpts and withheld entries.
Exit codes are three, not two: 0 conformant, 1 non-conformant, 2 the check did
not run. Collapsing 2 into 1 would report an unread file as a failed check.
**It moved into the package on 2026-09-08 (O5).** It lived in `tools/` on the
argument that staying out of the wheel left no consumer's install surface
changed. The generated consumption skill made that argument cost more than it
bought: the skill's own check step named this file by absolute path into a
checkout, so the one command that tells a reader whether their payload
conforms was unreachable from an install. `okf check` is that command, on
PATH. `tools/okf_contract_check.py` remains as a thin wrapper for the
published reproduction blocks.
"""
from __future__ import annotations
import argparse
import json
from collections.abc import Callable, Mapping, Sequence
from dataclasses import dataclass
from pathlib import Path
from typing import Any
#: SS 4.1. Five literals, spelled exactly. `[unverifiable-from-bundle]` is one
#: literal string -- no variants, no translations -- so this is a membership
#: test on the literal, never on a translated concept name.
REQUIRED_MARKINGS = (
"extracted",
"derived",
"[unverifiable-from-bundle]",
"[unread]",
"[sourced-not-sufficient]",
)
#: SS 6.1. A closed set of three, and the third is a real state: `unknown` says
#: the concept carries no `adjudication` key, which is what an older bundle
#: looks like. Collapsing it into `proposed` or into absence reports "not
#: judged" where the truth is "we cannot tell", and only one of those is a fact
#: about the concept.
ADJUDICATION_STATES = ("proposed", "adjudicated", "unknown")
#: SS 6.2, from SPEC SS 5.3: derived from `verified`, lowest to highest.
TRUST_TIERS = ("unverified", "machine-confirmed", "human-reviewed")
#: The level-2 headings a conformant skill carries. Checked by name because the
#: alternative -- searching the prose for the obligation -- passes on any
#: document that happens to use the words.
REQUIRED_SECTIONS = (
"Pre-pass",
"Division of labour",
"Markings",
"States",
"Budget",
"Denominators",
"Prohibitions",
)
@dataclass(frozen=True)
class Finding:
"""One contract paragraph, unmet, named by its code."""
code: str
message: str
@dataclass(frozen=True)
class Report:
"""Findings plus the denominators they were measured over."""
findings: tuple[Finding, ...]
rules_evaluated: int
excerpts_examined: int
withheld_examined: int
def render(self) -> str:
denominator = (
f"{self.rules_evaluated} rules over {self.excerpts_examined} excerpts "
f"and {self.withheld_examined} withheld entries"
)
if not self.findings:
return f"conformant: {denominator}, 0 findings"
lines = [f"NOT conformant: {denominator}, {len(self.findings)} findings"]
lines += [f" {finding.code}: {finding.message}" for finding in self.findings]
return "\n".join(lines)
@dataclass(frozen=True)
class Context:
"""What every rule reads. `payload` is empty when the payload is not a
mapping at all, so each rule stays a total function over its input."""
skill: str
payload: Mapping[str, Any]
payload_is_mapping: bool
def _mapping(value: object) -> Mapping[str, Any]:
return value if isinstance(value, Mapping) else {}
def _sequence(value: object) -> Sequence[Any]:
if isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
return value
return ()
def _text(value: object) -> str:
return value if isinstance(value, str) else ""
def _whole(value: object) -> int | None:
# `bool` is an `int` in Python and would make `True` a budget. Excluded by
# name rather than trusted not to arrive.
return value if isinstance(value, int) and not isinstance(value, bool) else None
# --- Payload rules -----------------------------------------------------------
def rule_payload_shape(ctx: Context) -> list[Finding]:
if ctx.payload_is_mapping:
return []
return [Finding("payload_invalid", "the payload is not a JSON object (SS 8)")]
def rule_contract_version(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping or _text(ctx.payload.get("contract")):
return []
return [
Finding(
"contract_unversioned",
"the payload names no `contract` revision, so a reader cannot tell "
"which revision it is holding (SS 8.2)",
)
]
def rule_bundle_ref(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
bundle = _mapping(ctx.payload.get("bundle"))
findings = []
if not _text(bundle.get("ref")):
findings.append(
Finding(
"ref_missing",
"the payload names no bundle `ref`; a version is the producer's "
"assertion, a ref is a fact about bytes (SS 3.3)",
)
)
if not _text(bundle.get("bundle_id")):
findings.append(Finding("ref_missing", "the payload names no `bundle.bundle_id` (SS 3.1)"))
return findings
def rule_excerpt_source_marking(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
findings = []
for position, raw in enumerate(_sequence(ctx.payload.get("excerpts"))):
excerpt = _mapping(raw)
for key in ("bundle_id", "concept_id", "sha256"):
if not _text(excerpt.get(key)):
findings.append(
Finding(
"source_marking_incomplete",
f"excerpt {position} carries no {key!r}; identity across "
"bundles is the (bundle_id, concept_id) tuple with the "
"digest of the bytes it was taken from (SS 3.1, SS 3.2)",
)
)
return findings
def rule_excerpt_named(ctx: Context) -> list[Finding]:
"""SS 8: every excerpt carries a `title`.
Added 2026-09-08 on a measurement, not a preference: `portfolio-optimiser`
ran three paid arms in which the pre-pass delivered the gold concept at rank
1 of 8 on 3 of 3 bundles and the model answered correctly on 1 of 3, because
the excerpt carried `concept_id` and `text` and nothing a reader could name
the document by. A payload no answer can cite from is not conformant; the
identity fields are what SS 3.1's tuple is FOR.
"""
if not ctx.payload_is_mapping:
return []
return [
Finding(
"excerpt_unnamed",
f"excerpt {position} carries no 'title'; an excerpt a reader cannot "
"name is one an answer cannot cite, whatever its rank (SS 8)",
)
for position, raw in enumerate(_sequence(ctx.payload.get("excerpts")))
if not _text(_mapping(raw).get("title"))
]
def rule_excerpt_states(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
findings = []
closed: tuple[tuple[str, tuple[str, ...]], ...] = (
("adjudication", ADJUDICATION_STATES),
("trust_tier", TRUST_TIERS),
)
for position, raw in enumerate(_sequence(ctx.payload.get("excerpts"))):
excerpt = _mapping(raw)
for key, allowed in closed:
if key not in excerpt:
findings.append(
Finding(
"state_missing",
f"excerpt {position} carries no {key!r}; the third state is "
"written explicitly, never omitted -- omission collapses "
'"we cannot tell" into a fact about the concept (SS 6.1)',
)
)
continue
value = _text(excerpt.get(key))
if value not in allowed:
findings.append(
Finding(
"state_not_in_set",
f"excerpt {position} has {key}={value!r}, outside the closed "
f"set {allowed} (SS 6)",
)
)
return findings
def rule_denominator_identity(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
counts = _mapping(ctx.payload.get("denominators"))
values = {key: _whole(counts.get(key)) for key in ("considered", "withheld", "delivered")}
missing = sorted(key for key, value in values.items() if value is None)
if missing:
return [
Finding(
"denominator_identity",
f"the payload reports no whole-number {', '.join(missing)}; a count "
"that is not reported is unmeasured, not zero (SS 5.1)",
)
]
considered = values["considered"]
withheld = values["withheld"]
delivered = values["delivered"]
assert considered is not None and withheld is not None and delivered is not None
if considered != withheld + delivered:
return [
Finding(
"denominator_identity",
f"considered ({considered}) != withheld ({withheld}) + delivered "
f"({delivered}); a count that does not close is not a denominator "
"(SS 5.2)",
)
]
return []
def rule_denominator_lists(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
counts = _mapping(ctx.payload.get("denominators"))
findings = []
for key, member in (("delivered", "excerpts"), ("withheld", "withheld")):
declared = _whole(counts.get(key))
if declared is None:
continue
actual = len(_sequence(ctx.payload.get(member)))
if declared != actual:
findings.append(
Finding(
"denominator_list_mismatch",
f"denominators.{key} is {declared} but {member} holds {actual}; "
"the count and the list are two statements of one fact (SS 8.1)",
)
)
return findings
def rule_withheld_rules(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
findings = []
for position, raw in enumerate(_sequence(ctx.payload.get("withheld"))):
entry = _mapping(raw)
for key in ("concept_id", "rule"):
if not _text(entry.get(key)):
findings.append(
Finding(
"cut_undeclared",
f"withheld entry {position} carries no {key!r}; a visible "
"drop is worth more than a silent one (SS 5.3)",
)
)
return findings
def rule_budget_declared(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
budget = _mapping(ctx.payload.get("budget"))
findings = []
for key in ("unit", "instrument"):
if not _text(budget.get(key)):
findings.append(
Finding(
"budget_undeclared",
f"the budget names no {key!r}; a number without its instrument "
"and unit is not a measurement (SS 7.1)",
)
)
limit = _whole(budget.get("limit"))
if limit is None or limit <= 0:
findings.append(
Finding(
"budget_undeclared",
'"bounded" without a bound is a denominator failure in prose (SS 7.1)',
)
)
if _whole(budget.get("spent")) is None:
findings.append(Finding("budget_undeclared", "the budget reports no `spent` (SS 7.2)"))
return findings
def rule_budget_gate(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
budget = _mapping(ctx.payload.get("budget"))
limit = _whole(budget.get("limit"))
spent = _whole(budget.get("spent"))
if limit is None or spent is None or spent <= limit:
return []
return [
Finding(
"budget_exceeded",
f"spent ({spent}) exceeds limit ({limit}); the cut strategy is wrong "
"for this bundle, which is a finding requiring a decision and never "
"a retry with a narrower question (SS 7.3)",
)
]
def rule_instrument_validated(ctx: Context) -> list[Finding]:
if not ctx.payload_is_mapping:
return []
known = _mapping(_mapping(ctx.payload.get("budget")).get("known_positive"))
expected = _whole(known.get("expected"))
measured = _whole(known.get("measured"))
if expected is None or measured is None or expected <= 0 or not _text(known.get("case")):
return [
Finding(
"instrument_unvalidated",
"the budget carries no usable `known_positive` (case, expected, "
"measured); an instrument that has not reproduced a known figure "
"has not been shown to count (SS 7.4)",
)
]
if expected != measured:
return [
Finding(
"instrument_unvalidated",
f"the known-positive expected {expected} and the instrument "
f"measured {measured} (SS 7.4)",
)
]
return []
# --- Skill rules -------------------------------------------------------------
def rule_skill_sections(ctx: Context) -> list[Finding]:
return [
Finding(
"skill_section_missing",
f"the skill carries no `## {section}` section (SS 8 of this checker's "
"reading; the headings are fixed so they can be checked by name)",
)
for section in REQUIRED_SECTIONS
if f"## {section}" not in ctx.skill
]
def rule_skill_markings(ctx: Context) -> list[Finding]:
return [
Finding(
"skill_marking_missing",
f"the skill does not carry the required marking {marking!r} verbatim "
"(SS 4.1); one literal string, no variants, no translations",
)
for marking in REQUIRED_MARKINGS
if marking not in ctx.skill
]
def rule_skill_states(ctx: Context) -> list[Finding]:
return [
Finding(
"skill_state_missing",
f"the skill does not name the state {state!r}, so its consumer cannot "
"be held to the closed set (SS 6)",
)
for state in (*ADJUDICATION_STATES, *TRUST_TIERS)
if f"`{state}`" not in ctx.skill
]
#: Every rule, in report order. `len(RULES)` is the denominator the report
#: quotes: "how many rules ran" is the number that makes "0 findings" mean
#: something.
RULES: tuple[Callable[[Context], list[Finding]], ...] = (
rule_payload_shape,
rule_contract_version,
rule_bundle_ref,
rule_excerpt_source_marking,
rule_excerpt_named,
rule_excerpt_states,
rule_denominator_identity,
rule_denominator_lists,
rule_withheld_rules,
rule_budget_declared,
rule_budget_gate,
rule_instrument_validated,
rule_skill_sections,
rule_skill_markings,
rule_skill_states,
)
def check(skill_text: str, payload: object) -> Report:
"""Run every rule. No rule short-circuits another: a caller fixing one
defect should not discover a second only on the next run."""
is_mapping = isinstance(payload, Mapping)
ctx = Context(
skill=skill_text,
payload=payload if isinstance(payload, Mapping) else {},
payload_is_mapping=is_mapping,
)
findings: list[Finding] = []
for rule in RULES:
findings.extend(rule(ctx))
return Report(
findings=tuple(findings),
rules_evaluated=len(RULES),
excerpts_examined=len(_sequence(ctx.payload.get("excerpts"))),
withheld_examined=len(_sequence(ctx.payload.get("withheld"))),
)
def parse_args(argv: list[str] | None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("--skill", type=Path, required=True, help="the SKILL.md to check")
parser.add_argument("--payload", type=Path, required=True, help="one pre-pass payload (JSON)")
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
args = parse_args(argv)
try:
skill_text = args.skill.read_text(encoding="utf-8")
except OSError as exc:
print(f"could not read the skill: {exc}")
return 2
try:
payload = json.loads(args.payload.read_text(encoding="utf-8"))
except OSError as exc:
print(f"could not read the payload: {exc}")
return 2
except json.JSONDecodeError as exc:
print(f"the payload is not readable JSON: {exc}")
return 2
report = check(skill_text, payload)
print(report.render())
return 1 if report.findings else 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,263 @@
"""One folder of documents in, one questionable project out, in one command.
`okf project <folder>` is `okf build` followed by `okf skill`, plus the summary
a person needs in order to know what they just got. It adds no rule of its own
and owns no flag that changes a bundle's bytes: the build runs on THIS
package's default, so a project bundle and an `okf build` bundle of the same
folder at the same stamp are the same bytes.
**Why a third command rather than a documented three-step.** The three-step
existed and was measured on a reader: set `PYTHONPATH`, take a snapshot of a
checkout, run `python3 tools/okf_skill.py`, and know which of three directories
each artefact belongs in. The operator's word for it was "extremely cryptic",
and that is a measurement of the instructions, not of the reader. Everything
this module does was already reachable; what was missing was that it was one
thing.
**The two destinations are conventions, not choices this module invents.**
`<out>/.okf/<id>/` keeps the bundle out of the way of the documents it was
built from -- the drop folder is walked recursively, so a bundle written beside
the documents would be its own input on the next run. `<out>/.claude/skills/`
is where Claude Code looks, and the skill is the whole point: a bundle nobody
can ask a question of is a directory.
Exit codes are three, as everywhere in this chain: 0 the project was written,
1 the run happened and refused, 2 the run did not happen.
"""
from __future__ import annotations
import argparse
import re
import sys
import unicodedata
from pathlib import Path
from . import consume, skill
from .cli import DEFAULT_STAMP, build
from .corpus import CorpusReport
from .errors import IngestError
from .inbox import walk_inbox
from .profiles import SEGMENTED_OKF_V0_2
CLI_ID = "okf project"
#: Where the bundle and the skill go under `--out`. Constants rather than
#: flags: a project whose layout varies per run is one whose summary cannot
#: tell a reader where anything is.
BUNDLE_DIR = ".okf"
SKILLS_DIR = Path(".claude") / "skills"
#: What the bundle declares as its upstream version. A VALUE, and normally the
#: caller's (decision E1) -- but `okf project` has no catalog to ask, and a
#: required flag here would put the one-command form back behind a question
#: nobody standing in front of a folder of PDFs can answer. So it is stated:
#: the version the segmented profile this command builds under was written for.
PROJECT_OKF_VERSION = "0.2"
_ID_SAFE = re.compile(r"[^a-z0-9]+")
def slug(name: str) -> str:
"""A folder name reduced to `[a-z0-9-]`, or a coded refusal.
NFC first, for the reason `materialize.reduce_to_id_grammar` normalises:
macOS hands filenames over decomposed, so the same visible folder name
reduces two different ways depending on which form it arrived in.
"""
reduced = _ID_SAFE.sub("-", unicodedata.normalize("NFC", name).casefold()).strip("-")
if not reduced:
raise IngestError(
f"the folder name {name!r} reduces to nothing in the id grammar; pass --id",
code="manifest_invalid",
)
return reduced
def inventory(folder: Path, bundle: Path) -> tuple[tuple[str, ...], tuple[str, ...]]:
"""Two lists a reader needs and cannot get from a concept count.
The first is the dropped documents NO concept names as its source: they are
in the folder, they are not in the bundle, and no excerpt can quote them.
The second is the documents that landed WHOLE, as one flat concept at the
bundle root -- the ones the mechanical rules found no boundary in. They are
reachable, and reaching them returns the entire document as one excerpt,
which the budget will often refuse outright and which, when it does fit,
frequently does not carry the conclusion at the place a reader asked about.
That is the `[sourced-not-sufficient]` case, and it is the difference
between a bundle that has 15 concepts and a bundle that answers.
Read off the INDEX TREE and not off the directory, the same walk the
pre-pass uses: a summary computed by a rule the pre-pass does not share
could name a document as present that no question will ever reach.
"""
walked, _ = walk_inbox(folder, exclude=bundle)
dropped = {path.relative_to(folder).as_posix() for path in walked}
represented: set[str] = set()
whole: set[str] = set()
root_bundle_id = consume.root_bundle_id_of(bundle, profile=SEGMENTED_OKF_V0_2)
for concept_id in consume.enumerate_concepts(bundle, profile=SEGMENTED_OKF_V0_2):
path = bundle / f"{concept_id}{SEGMENTED_OKF_V0_2.paths.concept_suffix}"
concept = consume.read_concept(path, bundle_root=bundle, root_bundle_id=root_bundle_id)
represented.add(concept.source_file)
# A concept id with no `/` sits at the bundle root rather than under a
# per-document directory, which is what an unsegmented document
# produces. Measured on the artefact rather than read off the run's
# log: the log is a file a caller can delete.
if "/" not in concept_id:
whole.add(concept.source_file)
return tuple(sorted(dropped - represented)), tuple(sorted(whole))
def summarise(
folder: Path,
bundle: Path,
skill_path: Path,
out: Path,
report: CorpusReport,
concepts: int,
missing: tuple[str, ...],
whole: tuple[str, ...],
) -> str:
"""The plain-language summary, with a denominator on every number."""
lines = [
f"Read {report.n} document(s) from {folder}.",
f"Wrote {concepts} concept(s) to {bundle}.",
f"Wrote the skill to {skill_path}.",
"",
]
if missing:
lines.append(
f"{len(missing)} of {report.n} document(s) are in the folder and NOT in "
"the bundle, so no excerpt can quote them. A question about one of "
"these can only be answered `[sourced-not-sufficient]`:"
)
lines.extend(f" - {name}" for name in missing)
if report.codes:
lines.append(
" reason code(s): " + ", ".join(f"{code} x{count}" for code, count in report.codes)
)
else:
lines.append(
f"0 of {report.n} document(s) were left out of the bundle. Every "
"document in the folder is reachable to a question."
)
lines.append("")
if whole:
lines.append(
f"{len(whole)} of {report.n} document(s) landed WHOLE, as one concept "
"each: the rules found no heading, table or numbered outline to cut "
"them on. Asking about one of these returns the entire document as a "
"single excerpt, which is often refused for size and, when it fits, "
"often does not carry the answer at the place you asked about. Expect "
"`[sourced-not-sufficient]` there:"
)
lines.extend(f" - {name}" for name in whole)
else:
lines.append(
f"0 of {report.n} document(s) landed whole; every one was cut into "
"parts a question can reach separately."
)
lines.extend(
[
"",
f"NEXT: start claude again in {out} and ask your question.",
]
)
return "\n".join(lines)
def create(
folder: Path,
*,
out: Path,
bundle_id: str | None = None,
ingested_at: str = DEFAULT_STAMP,
force: bool = False,
) -> tuple[Path, Path, str]:
"""Build the bundle, generate the skill, return both paths and the summary.
Keyword-only with defaults, so a caller taking this as an API keeps a
source-compatible call when a parameter is added.
"""
identity = bundle_id if bundle_id is not None else slug(folder.resolve().name)
bundle = out / BUNDLE_DIR / identity
report = build(
folder,
bundle,
ingested_at=ingested_at,
bundle_id=identity,
okf_version=PROJECT_OKF_VERSION,
)
if report.unaccounted or report.merged + report.rejected != report.n:
raise IngestError(
f"K1b FAILED - merged ({report.merged}) + coded rejections "
f"({report.rejected}) != N ({report.n}). Unaccounted: "
f"{', '.join(report.unaccounted) or '(none named)'}",
code="conservation_failed",
)
skill_dir = out / SKILLS_DIR / f"{identity}-consume"
written = skill.generate(bundle, out=skill_dir, force=force)
concepts = len(consume.enumerate_concepts(bundle, profile=SEGMENTED_OKF_V0_2))
missing, whole = inventory(folder, bundle)
summary = summarise(folder, bundle, written, out, report, concepts, missing, whole)
return bundle, written, summary
def parse_args(argv: list[str] | None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog=CLI_ID,
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("folder", type=Path, help="the folder of documents to make questionable")
parser.add_argument(
"--id",
dest="bundle_id",
default=None,
help="the bundle id. Defaults to the folder's name reduced to [a-z0-9-]",
)
parser.add_argument(
"--out",
type=Path,
default=None,
help="where the project is written. Defaults to the current directory",
)
parser.add_argument(
"--ingested-at",
default=DEFAULT_STAMP,
help=f"stamped verbatim. Default {DEFAULT_STAMP}: deterministic, never the clock",
)
parser.add_argument(
"--force", action="store_true", help="replace an existing SKILL.md at the destination"
)
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
args = parse_args(argv)
if not args.folder.is_dir():
print(f"{CLI_ID}: the run did not happen - no such folder: {args.folder}", file=sys.stderr)
return 2
out = args.out if args.out is not None else Path.cwd()
try:
_, _, summary = create(
args.folder,
out=out,
bundle_id=args.bundle_id,
ingested_at=args.ingested_at,
force=args.force,
)
except (IngestError, consume.ConsumeError, skill.SkillError) as exc:
print(f"{CLI_ID}: refused ({exc.code}) - {exc}", file=sys.stderr)
return 1
except OSError as exc:
print(f"{CLI_ID}: the run did not happen - {exc}", file=sys.stderr)
return 2
print(summary)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -0,0 +1,630 @@
"""Instantiate the consumption skill template for ONE named OKF bundle.
`skills/okf-consume-template/SKILL.md` is a template whose own rule is that a
copy leaving a `<PLACEHOLDER>` unfilled "is not configured, it is unfinished".
Filling it by hand is what produced `skills/okf-consume/` for one corpus. This
command does the same thing for any bundle, from values it measures rather than
values someone remembered.
**Why a generator rather than one generic skill.** Measured 2026-09-08: the
contract checker passes the UNFILLED template against a real payload (exit 0, 15
rules, 0 findings), and passes a skill built for a different bundle against this
one's payload. So the checker cannot tell the two forms apart, and the choice
could not be made on conformance. It was made on what the skill has to state:
§ 5's denominators, § 7.6's breaking point and § 6.4's conditional-field list
are all per-bundle numbers. A generic skill can either leave them as holes -- the
template's own definition of unfinished -- or carry another corpus's numbers,
which is worse, because a stated cost that is false for this bundle is a
measurement failure and not merely a gap. Instantiating is what makes them true.
And with several bundles connected at once, a generic skill has nothing to
select on: each generated skill carries the bundle's id in its own name.
**Zero model calls, zero network, no clock.** The same bundle bytes produce the
same skill bytes.
**It moved into the package on 2026-09-08 (O5), and so did the reason it could
not before.** The old objection was exact: a wheel-installed `okf skill` would
emit a command pointing at `tools/okf_consume.py`, which the wheel does not
contain. That objection was about what the generated skill NAMES, and the
answer was to change what it names. The emitted commands are now `okf consume`
and `okf check` -- names on PATH after an install, resolved by the shell and
not by this repository's layout. The pre-pass and the checker moved into the
package in the same step, so both names exist wherever the generated skill
does. Measured before the move: a skill generated from a checkout carried four
lines with an absolute path into that checkout, two of them the commands a
reader is told to run.
Exit codes are three, as elsewhere in this chain: 0 the skill was written, 1 the
run happened and refused, 2 the run did not happen.
"""
from __future__ import annotations
import argparse
import json
import re
from collections import Counter
from pathlib import Path
from . import consume as okf_consume
from .profiles import BundleProfile
#: The template, resolved to a copy that exists wherever this module does.
#:
#: A wheel carries `_data/okf-consume-template.md`, force-included from the
#: authored file below at build time; a source tree carries only the authored
#: file. Preferring the packaged copy and falling back keeps ONE authored
#: template -- a committed second copy is the drift this generator exists to
#: prevent, restated one directory up.
PACKAGED_TEMPLATE = Path(__file__).resolve().parent / "_data" / "okf-consume-template.md"
AUTHORED_TEMPLATE = (
Path(__file__).resolve().parents[2] / "skills" / "okf-consume-template" / "SKILL.md"
)
#: What the generated skill tells a reader to RUN. Names on PATH after an
#: install, never paths into a checkout: a generated skill is meant to be moved,
#: shared and run by someone who does not have this repository.
PRE_PASS_COMMAND = "okf consume"
CHECKER_COMMAND = "okf check"
#: The contract is prose in this repository and is NOT shipped in the wheel, so
#: the generated skill names it as a document rather than as a path a reader
#: could open. Naming an absolute path here is what made a generated skill
#: unmovable; naming a file the reader may not have is honest about which it is.
CONTRACT = "docs/consumption-contract.md in open/llm-ingestion-okf"
#: The profile the pre-pass reads a bundle under, spelled so the generated skill
#: can name it in § 9.2's sentence. The pre-pass's own default; a bundle built
#: under another profile needs a copy of this tool that says so.
PROFILE_NAME = "SEGMENTED_OKF_V0_2"
#: The conditional frontmatter keys the generated skill reports a denominator
#: for. Every one of them is written by SOME producer and not by others, which
#: is exactly what § 6.4 says a consumer must be told about rather than left to
#: infer from an absence.
CONDITIONAL_FIELDS = (
"adjudication",
"bundle_id",
"verified",
"req_number",
"sources",
)
#: Tokens too short to carry a question. The same floor the pre-pass's own
#: matcher uses, so the derived example question cannot be shorter than what the
#: ranker can see.
MIN_QUESTION_TOKEN = 5
class SkillError(Exception):
"""The generator refused. Carries the code, like the rest of this chain."""
def __init__(self, message: str, *, code: str) -> None:
super().__init__(message)
self.code = code
def template_path() -> Path:
"""The template this generator instantiates, or a coded refusal.
Refuses rather than falling through to a default: a generator that
silently emitted a skill built from no template would produce a file
carrying this bundle's numbers and none of the contract's sections.
"""
for candidate in (PACKAGED_TEMPLATE, AUTHORED_TEMPLATE):
if candidate.is_file():
return candidate
raise SkillError(
f"the consumption skill template was not found at {PACKAGED_TEMPLATE} "
f"or {AUTHORED_TEMPLATE}",
code="template_missing",
)
# --- The blocks the template hands over verbatim ------------------------------
#: Every template block this generator rewrites WHOLE, by exact string. Held to
#: the template by a test: an edit that moves one of these would otherwise
#: produce a skill silently missing that rewrite, which is the drift the
#: instantiated copy exists to avoid.
TEMPLATE_HEADER = """**This file is a template.** Every `<PLACEHOLDER>` is a hole a per-corpus copy
fills; none of them has a default, and a copy that leaves one unfilled is not
configured, it is unfinished. The section headings are fixed:
`okf check` reads them, and a missing one makes the skill
non-conformant rather than merely thin."""
TEMPLATE_PRE_PASS = """```sh
<PRE_PASS_COMMAND> --bundle-root <BUNDLE_ROOT> --ref <REF> --out <PAYLOAD_PATH>
```"""
TEMPLATE_CHECK = """```sh
okf check --skill <SKILL_PATH> --payload <PAYLOAD_PATH>
```"""
TEMPLATE_CONTRACT_LINE = (
"The contract this skill is held to is `docs/consumption-contract.md`. Where this"
)
TEMPLATE_EXTENSIONS = """**Extensions, if this corpus needs any.** `<EXTENSION_MARKINGS: for each, the
literal, what it means here, and which of the five it would otherwise collapse
into. Write "none" if there are none.>`"""
TEMPLATE_CONDITIONAL = """**Conditionally-written fields in this corpus.** `<CONDITIONAL_FIELDS: each
field this profile writes only when a build-time condition held, and what its
absence does and does not mean. Absence is a measurement, not a fact.>`"""
TEMPLATE_SCALING = """**Scaling.** `<COST_SCALING: whether cost tracks the question or the corpus, what
the whole bundle at this ref costs by the same instrument, and the corpus size
at which this strategy stops fitting the budget.>`"""
TEMPLATE_DENOMINATORS = """The payload reports three counts — `considered`, `withheld`, `delivered` — and
`considered == withheld + delivered`. Carry them into your output."""
TEMPLATE_ENUMERATION = (
"- **No directory enumeration** unless `<PROFILE_NAME>` says the index is derived."
)
TEMPLATE_OUTPUT = "Write to `<OUT>`. It must carry: the bundle ref; the findings, each with a"
REPLACED_BLOCKS = (
TEMPLATE_HEADER,
TEMPLATE_PRE_PASS,
TEMPLATE_CHECK,
TEMPLATE_CONTRACT_LINE,
TEMPLATE_EXTENSIONS,
TEMPLATE_CONDITIONAL,
TEMPLATE_SCALING,
TEMPLATE_DENOMINATORS,
TEMPLATE_ENUMERATION,
TEMPLATE_OUTPUT,
)
# --- What the generator measures ----------------------------------------------
def slug(value: str) -> str:
"""A Claude Code skill name from a bundle id: lowercase, hyphen-joined."""
reduced = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
return reduced or "okf"
def example_question(titles: list[str]) -> str:
"""A question this bundle really answers, derived rather than invented.
The most frequent long token across the concepts' own titles, byte-sorted on
a tie. Derived because the shipped payload has to be one this bundle
produces: a question sharing no token with any concept is withheld under
`no_lexical_match` and the pre-pass refuses, so a hand-picked constant would
fail on the first bundle that does not happen to contain it.
"""
counts: Counter[str] = Counter()
for title in titles:
counts.update(
{token for token in okf_consume.normalise(title) if len(token) >= MIN_QUESTION_TOKEN}
)
if not counts:
# Every title is short or empty. Fall back to the longest title as it
# stands, which by construction matches at least its own concept.
longest = max(titles, key=lambda title: (len(title), title), default="")
if not longest:
raise SkillError(
"no concept in this bundle carries a title, so no example "
"question can be derived from it; pass --example-question",
code="no_example_question",
)
return longest
top = min(counts.items(), key=lambda item: (-item[1], item[0]))[0]
return f"Hva sier denne bundelen om {top}?"
def field_counts(concepts: list[okf_consume.Concept]) -> dict[str, int]:
"""How many concepts carry each conditional field. Set membership, never a
guess from two equal totals.
The `source_*` rows are DISCOVERED from the bundle rather than listed here,
for the same reason the excerpt carries them by prefix: a fixed list reports
a denominator for the producers someone thought of, and says nothing about
the locator this producer actually chose.
"""
discovered = sorted(
{
key
for concept in concepts
for key in concept.frontmatter
if key.startswith(okf_consume.SOURCE_KEY_PREFIX)
}
)
fields = (*CONDITIONAL_FIELDS, *discovered)
counts = {field: 0 for field in fields}
for concept in concepts:
for field in fields:
if concept.frontmatter.get(field, "").strip() or (
field == "sources" and concept.sources_present
):
counts[field] += 1
return counts
def whole_bundle_cost(concepts: list[okf_consume.Concept]) -> int:
"""What every concept in this bundle would cost by the gate's own
instrument, if a single answer delivered all of them."""
total = 0
for concept in concepts:
excerpt = okf_consume.excerpt_for(concept)
if excerpt is not None:
total += okf_consume.excerpt_weight(excerpt)
return total
def directory_control(bundle_root: Path, *, profile: BundleProfile) -> tuple[int, int]:
"""The index walk against the method § 9.2 forbids the CONSUMER from using.
Run HERE, once, at generation time -- never on the question path. § 9.2
binds a consumer reaching for context at query time; a build-time control is
what turns "the walk loses nothing" from an assumption into a number the
generated skill can quote.
"""
walked = len(okf_consume.enumerate_concepts(bundle_root, profile=profile))
suffix = profile.paths.concept_suffix
reserved = {profile.index.name, "log.md"}
on_disk = len([path for path in bundle_root.rglob(f"*{suffix}") if path.name not in reserved])
return walked, on_disk
# --- The instantiation --------------------------------------------------------
def render(
bundle_root: Path,
*,
out: Path,
profile: BundleProfile = okf_consume.DEFAULT_PROFILE,
question: str | None = None,
) -> tuple[str, dict[str, object]]:
"""The skill text and the example payload that proves it, for one bundle."""
bundle_root = bundle_root.resolve()
bundle_id = okf_consume.root_bundle_id_of(bundle_root, profile=profile)
ref = okf_consume.bundle_ref(bundle_root, profile=profile)
concept_ids = okf_consume.enumerate_concepts(bundle_root, profile=profile)
concepts = [
okf_consume.read_concept(
bundle_root / f"{concept_id}{profile.paths.concept_suffix}",
bundle_root=bundle_root,
root_bundle_id=bundle_id,
)
for concept_id in concept_ids
]
if not concepts:
raise SkillError(
f"{bundle_root} has an index but no concept under it; a skill for an "
"empty bundle would state denominators of zero it never measured",
code="bundle_empty",
)
total = len(concepts)
asked = question or example_question([concept.title for concept in concepts])
payload = okf_consume.build_payload(bundle_root, question=asked, profile=profile)
counts = field_counts(concepts)
walked, on_disk = directory_control(bundle_root, profile=profile)
cost = whole_bundle_cost(concepts)
denominators = payload["denominators"]
assert isinstance(denominators, dict)
budget = payload["budget"]
assert isinstance(budget, dict)
withheld = payload["withheld"]
assert isinstance(withheld, list)
bookkeeping = okf_consume.measure(json.dumps(withheld, ensure_ascii=False))
per_withheld = bookkeeping / len(withheld) if withheld else 0.0
breaking = int(okf_consume.DEFAULT_LIMIT / per_withheld) if per_withheld else 0
name = f"{slug(bundle_id)}-consume"
text = template_path().read_text(encoding="utf-8")
text = text.split("---\n", 2)[2]
text = _rewrite(
text,
bundle_root=bundle_root,
skill_path=out.resolve() / "SKILL.md",
bundle_id=bundle_id,
ref=ref,
name=name,
total=total,
counts=counts,
walked=walked,
on_disk=on_disk,
cost=cost,
asked=asked,
spent=int(budget["spent"]),
delivered=int(denominators["delivered"]),
bookkeeping=bookkeeping,
breaking=breaking,
)
header = f"---\nname: {name}\ndescription: {_description(bundle_id, total, ref)}\n---\n"
return header + text, payload
def _description(bundle_id: str, total: int, ref: str) -> str:
return (
f"Answer one question about the OKF bundle `{bundle_id}` ({total} concepts, "
f"ref {ref}) from a bounded payload assembled by a deterministic pre-pass, "
"marking every claim with its source, its title and its provenance locator. "
"Use whenever a question is about what that bundle's documents require, say "
"or contain. Generated by `okf skill`; every value below is measured "
"against this bundle at this ref."
)
def _rewrite(
text: str,
*,
bundle_root: Path,
skill_path: Path,
bundle_id: str,
ref: str,
name: str,
total: int,
counts: dict[str, int],
walked: int,
on_disk: int,
cost: int,
asked: str,
spent: int,
delivered: int,
bookkeeping: int,
breaking: int,
) -> str:
replacements: list[tuple[str, str]] = [
(
TEMPLATE_HEADER,
"**This file is an instantiated copy of "
"`skills/okf-consume-template/SKILL.md`,** generated by "
f"`okf skill` for one bundle: `{bundle_id}` at ref\n"
f"`{ref}`. Every value below was measured against those bytes. If the\n"
"bundle moves, the ref moves with it and this file is stale — regenerate\n"
"it rather than editing a number here. The section headings are fixed:\n"
"the contract checker reads them by name.",
),
(
TEMPLATE_PRE_PASS,
"```sh\n"
f"{PRE_PASS_COMMAND} \\\n"
f" {bundle_root} \\\n"
' --question "your question" \\\n'
f" --ref {ref} \\\n"
" --out /tmp/payload.json\n"
"```\n\n"
"`--ref` is an **assertion**, never an override: the identity is computed\n"
"from the bytes either way, and a mismatch refuses. Read the pre-pass's\n"
"own exit status, which carries three values: **0** a payload was written,\n"
"**1** the run happened and refused, **2** the run did not happen at all.",
),
(
TEMPLATE_CHECK,
"```sh\n"
f"{CHECKER_COMMAND} \\\n"
f" --skill {skill_path} \\\n"
" --payload /tmp/payload.json\n"
"```",
),
(
TEMPLATE_CONTRACT_LINE,
f"The contract this skill is held to is `{CONTRACT}`. Where this",
),
(
TEMPLATE_EXTENSIONS,
"**Extensions, if this corpus needs any: none.** This generated skill adds\n"
"no marking to the required five. § 4.3 makes the undeclared extension the\n"
"defect, so the absence is stated rather than left to be inferred — and a\n"
"corpus that does need a sixth needs a hand-edited copy that declares it.",
),
(
TEMPLATE_CONDITIONAL,
_conditional_table(total, counts),
),
(
TEMPLATE_SCALING,
_scaling(
total=total,
cost=cost,
asked=asked,
spent=spent,
delivered=delivered,
bookkeeping=bookkeeping,
breaking=breaking,
),
),
(
TEMPLATE_DENOMINATORS,
_denominators(total, asked=asked, delivered=delivered),
),
(
TEMPLATE_ENUMERATION,
_enumeration(walked, on_disk),
),
(
TEMPLATE_OUTPUT,
"Write to the path the caller names, or to your answer if none was named.\n"
"It must carry: the bundle ref; the findings, each with a",
),
("`<CORPUS>` bundle", f"`{bundle_id}` bundle"),
("# <CORPUS> consumption", f"# {bundle_id} consumption"),
("<BUDGET_LIMIT>", str(okf_consume.DEFAULT_LIMIT)),
("<BUDGET_UNIT>", okf_consume.BUDGET_UNIT),
("<BUDGET_INSTRUMENT>", okf_consume.BUDGET_INSTRUMENT),
("<KNOWN_POSITIVE_CASE>", okf_consume.KNOWN_POSITIVE_CASE),
("<KNOWN_POSITIVE_EXPECTED>", str(okf_consume.KNOWN_POSITIVE_EXPECTED)),
]
for old, new in replacements:
if old not in text:
raise SkillError(
f"the template no longer carries the block this generator rewrites: {old[:70]!r}",
code="template_drift",
)
text = text.replace(old, new)
assert name # kept in the signature so a caller cannot forget to name the skill
return text
def _conditional_table(total: int, counts: dict[str, int]) -> str:
rows = "\n".join(
f"| `{field}` | **{count} of {total}** | "
f"{'the producer wrote none for that concept' if count else 'no concept in this bundle carries it'} | "
"that the source document lacks what the field asserts |"
for field, count in counts.items()
)
return (
"**Conditionally-written fields in this bundle, with what each absence does\n"
"and does not mean.** Every count is over the same denominator — "
f"**{total} concepts**, the set the index walk reaches. § 6.4: absence is a\n"
"measurement about the producer, never a fact about the source.\n\n"
"| Field | Present on | Absence means | Absence does NOT mean |\n"
"|---|---|---|---|\n"
f"{rows}\n\n"
"A field present on **0 of "
f"{total}** is a measured zero, not an unmeasured one: the count was taken\n"
"over every concept, and it is reported so a negative claim resting on it\n"
"carries its denominator."
)
def _scaling(
*,
total: int,
cost: int,
asked: str,
spent: int,
delivered: int,
bookkeeping: int,
breaking: int,
) -> str:
share = (spent / cost * 100) if cost else 0.0
return (
"**Scaling. Cost tracks the question, not the corpus.** Measured on this\n"
f"bundle at generation time, with the question `{asked}`: the delivered set\n"
f"was **{delivered} excerpts** costing **{spent} {okf_consume.BUDGET_UNIT}**,\n"
f"against a whole bundle that would cost **{cost}** by the same instrument if\n"
f"one answer delivered all {total} concepts — so that answer was about\n"
f"**{share:.1f} %** of the corpus. One question is one measurement: a\n"
"different question moves `spent` and this figure with it.\n\n"
"**The breaking point, stated so it can be observed to have been passed.**\n"
"The `withheld` list carries one entry per considered concept and grows\n"
f"linearly: here it is **{bookkeeping} bytes** for {total} concepts. At roughly\n"
f"**{breaking} concepts** the bookkeeping alone reaches the "
f"{okf_consume.DEFAULT_LIMIT}-byte\n"
"limit, and although it is not counted against `spent`, a payload whose\n"
"bookkeeping dwarfs its content has stopped being a cut. The pre-pass also\n"
"reads every concept body on every run, so the same growth is a wall-clock\n"
"cost with no precomputed index behind it."
)
def _denominators(total: int, *, asked: str, delivered: int) -> str:
return (
"The payload reports three counts — `considered`, `withheld`, `delivered` — and\n"
"`considered == withheld + delivered`. Carry them into your output.\n\n"
f"For this bundle `considered` is **{total}**, every concept the index walk\n"
"reaches, never the post-ranking shortlist. A concept dropped at the ranking\n"
"stage is `withheld` **with its rule**, not invisible, and the rules are a\n"
"closed set of six: `verdict_layer_excluded` (a verdict-layer file, § 9.1),\n"
"`verified_unreadable` (a `verified` value this reader cannot decode, so no\n"
"tier can be derived), `no_lexical_match` (the concept shares no token with\n"
"the question), `over_budget_alone` (one excerpt exceeds the whole limit),\n"
"`below_k` (ranked outside the shortlist the cut considers) and\n"
"`over_budget_after_knapsack` (it ranked inside the shortlist and the pack\n"
"had no room). Naming the rule is what makes a drop visible.\n\n"
"**One limitation to carry into every negative claim.** `no_lexical_match` is\n"
'a per-concept relevance drop, not a whole-question "this bundle has no\n'
f'answer" gate: on the generation question `{asked}` it still returned\n'
f"{delivered} excerpts. **An empty `excerpts` list is evidence of absence; a\n"
"full one is not evidence of presence.** When the delivered excerpts do not\n"
"actually answer the question, say `[sourced-not-sufficient]` and report that\n"
"the cut found nothing responsive."
)
def _enumeration(walked: int, on_disk: int) -> str:
agreement = (
f"which costs nothing here: the walk reaches **{walked}** concepts and a\n"
f" directory walk finds **{on_disk}**"
if walked == on_disk
else f"and the two disagree — the walk reaches **{walked}** concepts where a\n"
f" directory walk finds **{on_disk}**, so some concept is unreachable through\n"
" the index and the bundle's producer should be told"
)
return (
"- **No directory enumeration.** This bundle is read under the\n"
f" `{PROFILE_NAME}` profile, whose index policy declares\n"
" `entries_match_directory = False`, so § 9.2's permission does not apply.\n"
f" The pre-pass walks the **index tree** instead, {agreement}\n"
" (controlled once at generation time, never on the question path). Do not\n"
" enumerate a directory yourself either."
)
def generate(
bundle_root: Path,
*,
out: Path,
profile: BundleProfile = okf_consume.DEFAULT_PROFILE,
question: str | None = None,
force: bool = False,
) -> Path:
"""Write `out/SKILL.md` and its reference payload. Returns the skill path."""
target = out / "SKILL.md"
if target.exists() and not force:
raise SkillError(
f"{target} already exists; pass --force to replace it. A silent "
"overwrite would destroy a hand-edited copy whose extra measurements "
"this generator cannot reproduce",
code="target_occupied",
)
text, payload = render(bundle_root, out=out, profile=profile, question=question)
(out / "references").mkdir(parents=True, exist_ok=True)
target.write_text(text, encoding="utf-8")
(out / "references" / "example-payload.json").write_text(
okf_consume.serialise(payload), encoding="utf-8"
)
return target
def parse_args(argv: list[str] | None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("bundle", type=Path, help="the OKF bundle to instantiate a skill for")
parser.add_argument(
"--out", type=Path, required=True, help="the skill directory to write (SKILL.md inside)"
)
parser.add_argument(
"--example-question",
default=None,
help="the question the shipped reference payload answers. Derived from the "
"bundle's own titles when omitted",
)
parser.add_argument(
"--force", action="store_true", help="replace an existing SKILL.md at --out"
)
return parser.parse_args(argv)
def main(argv: list[str] | None = None) -> int:
args = parse_args(argv)
try:
written = generate(
args.bundle, out=args.out, question=args.example_question, force=args.force
)
except okf_consume.ConsumeError as exc:
print(f"refused ({exc.code}): {exc}")
return 1
except SkillError as exc:
print(f"refused ({exc.code}): {exc}")
return 1
except OSError as exc:
print(f"the run did not happen: {exc}")
return 2
print(f"wrote {written}")
return 0
if __name__ == "__main__":
raise SystemExit(main())