feat(okf): derive a cost baseline from a priced schedule, or refuse

MAJOR-4 (misjonsreview v2 section 7), owner B = the consumer. Neither existing
projection into ir.CostBaseline can serve an ingested tender corpus:
cost-baseline.json is hand-written per project and baseline_from_project belongs
to the road domain, so a K2-shaped bundle could be navigated and never anchored.
okf.derive_cost_baseline reads the numbers already in the bundle.

The premise was MEASURED before anything was built on it, and the order's two
pointers named two different forms. examples/*/expected-bundle/ carry pipe
tables, but every one of them is csv- or sql-sourced via render.render_table --
the xlsx path never reaches render_table at all. Measured with pandoc 3.10.2
under the producer's own writer and arguments: extract._extract_office converts,
and inbox.py hands that text to render_inbox_concept untouched, so an
xlsx-sourced concept file carries a pandoc SIMPLE table whose dash rule defines
the column spans. A pipe-only reader would have been inert on exactly the corpus
this exists for. Both forms are read, by two scanners over one role mapping and
one number grammar.

No judgement anywhere: the header vocabulary and the number grammar are closed,
and every ambiguity refuses -- no candidate table, more than one, two columns
claiming one role, two rows sharing a cost code, a row that prices nothing. A
partly-priced schedule refuses in full, because a half-derived baseline anchors
some codes while cost_baseline_anchored reports True.

Wired behind --derive-cost-baseline and never silently: one resolution in
run.py's bundle arm serves both the full run and the dry run, and the refusal
propagates rather than degrading to the file loader.

The two fixtures are pandoc's output verbatim, not hand-typed. The unpriced one
is K2's actual pre-award shape, and the columns survive as blanks -- so the
table IS a candidate and the refusal is the sharp one.

Load-bearing MEASURED: 18 mutations all red against the WHOLE suite, green
control 1230 passed / 5 skipped (from 1208/5, superset, 0 removed), golden
demo-transcript.stdout byte-unchanged (ea8c534773acdbe41ae68f2c55724d69aaf8be4f).
M17 is the one that matters for arm (b): dropping the positivity guard makes the
mutant raise pydantic ValidationError, which IS a ValueError but is NOT the named
class -- so pytest.raises(ValueError) would have stayed green against exactly the
mutation the arm exists to catch. Verified directly, not argued.

Honesty limits stated in the invariant row: NS 3451 section rows are not
classified (K2 itself was not available to measure), the stamp records that a run
was anchored and never which projection anchored it, the hosted surface is
deliberately untouched, and no live K2 file was read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-03 03:15:39 +02:00
commit 0add73531b
11 changed files with 870 additions and 7 deletions

View file

@ -601,6 +601,11 @@ async def run_project(
docs_dir: str,
verdict_input: dict[str, str] | None = None,
bundle_dir: str | None = None,
#: Derive the validator's cost baseline from a priced schedule IN the bundle
#: (``okf.derive_cost_baseline``) instead of loading a hand-written ``cost-baseline.json``.
#: Bundle path only, and OPT-IN by construction: the default leaves every existing run on the
#: file loader, byte-identically.
derive_cost_baseline: bool = False,
dimension: Dimension | None = None,
store: VerdictStore | None = None,
verdict_dir: str | None = None,
@ -691,7 +696,17 @@ async def run_project(
okf.reconcile_bundle_id(bundle_dir)
bundle = okf.navigate_bundle(bundle_dir)
project = _project_from_bundle(bundle_dir, project_id, bundle=bundle)
baseline = okf.load_optional_cost_baseline(bundle_dir)
# The THIRD projection into ``CostBaseline`` (MAJOR-4), behind an EXPLICIT commission and
# never silent. The refusal PROPAGATES rather than degrading to the file loader: a caller
# who asked for derivation and got an un-anchored run instead would have been answered by a
# silently downgraded order, which is what ``load_mandate`` fail-fasts against. This one
# resolution serves BOTH the full run and the ``live_dry_run`` report below, so the dry-run
# arm cannot drift away from what a real run would anchor on.
baseline = (
okf.derive_cost_baseline(bundle, project_id=project_id)
if derive_cost_baseline
else okf.load_optional_cost_baseline(bundle_dir)
)
# §4.1a context-scope: agents read ONLY dimension-scoped bundle knowledge (Step-3 filter);
# dimension=None keeps the full context, byte-identical to before.
context = okf.bundle_context(bundle, dimension=dimension.id if dimension else None)
@ -1956,6 +1971,15 @@ def main(argv: list[str] | None = None) -> int:
action="store_true",
help="offline drill: build contracts/clients/budget, STOP before the first model call",
)
parser.add_argument(
"--derive-cost-baseline",
action="store_true",
help=(
"derive the validator's cost baseline from a priced schedule inside --bundle-dir "
"instead of loading a hand-written cost-baseline.json (MAJOR-4). Refuses rather than "
"guesses: an unpriced or ambiguous schedule stops the run"
),
)
parser.add_argument(
"--scripted-replies",
default=None,
@ -2033,6 +2057,9 @@ def main(argv: list[str] | None = None) -> int:
report_forbidden = {
"--portfolio": args.portfolio,
"--live-dry-run": args.live_dry_run,
# Report mode returns before the run dispatch, so an omission here is a SILENT DROP,
# not a refusal — the gap F4 measured on --plan-review.
"--derive-cost-baseline": args.derive_cost_baseline,
"PROJECT_ID": args.project_id is not None,
"--goals": args.goals is not None,
"--docs-dir": args.docs_dir is not None,
@ -2101,6 +2128,11 @@ def main(argv: list[str] | None = None) -> int:
"--outbox-dir": args.outbox_dir,
"--run-id": args.run_id,
"--live-dry-run": args.live_dry_run,
# BY NAME, not by falling through to "--derive-cost-baseline requires --bundle-dir":
# --bundle-dir is already single-project-only, so that message would tell an operator
# who wrote --portfolio --derive-cost-baseline to add the one flag this mode also
# refuses. Same reason --explore is listed here rather than left to fall through.
"--derive-cost-baseline": args.derive_cost_baseline,
# One exploration shapes ONE mandate against ONE knowledge base, and --bundle-dir (its
# only source of bases here) is already single-project-only. Refusing it by NAME beats
# letting it fall through to the --bundle-dir requirement below: an operator who wrote
@ -2149,6 +2181,19 @@ def main(argv: list[str] | None = None) -> int:
)
return 1
# The third projection reads a table INSIDE a bundle, so without one there is nothing to derive
# from: the road path's baseline comes from ``Project.cost_items`` and is anchored by
# construction. Refused by NAME here rather than left to surface later as a project-lookup
# failure, which names neither the flag nor what it needs.
if not args.portfolio and args.derive_cost_baseline and args.bundle_dir is None:
print(
"run refused: --derive-cost-baseline requires --bundle-dir (the schedule it derives "
"from is a concept file in the knowledge base; the road path is already anchored by "
"its own cost_items)",
file=sys.stderr,
)
return 1
# --semantic-retrieval is refused, never silently ignored (the repo's flag contract). In
# single-project mode it can only do observable work with BOTH of these: the Step-1 fold is
# gated on ``bundle_dir``, and ``--verdict-dir`` is the only route by which ``main()`` can hand
@ -2761,6 +2806,7 @@ def main(argv: list[str] | None = None) -> int:
outbox_dir=args.outbox_dir,
run_id=args.run_id,
verdict_input=_verdict_input_from_args(args),
derive_cost_baseline=args.derive_cost_baseline,
mcp_servers=mcp_servers,
live_dry_run=True,
)
@ -2820,6 +2866,7 @@ def main(argv: list[str] | None = None) -> int:
run_id=args.run_id,
verdict_input=_verdict_input_from_args(args),
semantic_retrieval=args.semantic_retrieval,
derive_cost_baseline=args.derive_cost_baseline,
client_factory=scripted_client_factory,
mandate=mandate,
mcp_servers=mcp_servers,