Skip to content

release(0.69): batch — 15 receipted PRs + fleet-pv andon fix, one CI run, one queue slot - #3669

Merged
noahgift merged 128 commits into
mainfrom
release/0.69-batch
Sep 21, 2026
Merged

noahgift merged 128 commits into
mainfrom
release/0.69-batch

Conversation

@noahgift

@noahgift noahgift commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Release batch for 0.69: the cut-blockers in one CI run and one queue slot

Operator: "work smarter … batching PRs". The pool is the bottleneck and every fragment-minting PR conflicts on roadmap.yaml with the one ahead of it (#3650). This PR integrates the four receipted cut-blockers plus the andon fix, so we get one CI run, one queue entry, and roadmap.yaml resolved once.

constituent what review receipt (in this tree)
#3655 release scripts take the version as their one input; milestone/epic/last tag and AP derived (0.68.2 history proof) docs/audits/quorum-PMAT-3618.json, 3/3, cross-inspected
#3653 guard_tree surfaces a passing guard's UNMEASURED / SUMMARY line docs/audits/quorum-PMAT-3651.json, 3/3, cross-inspected
#3600 the 7 logit-parity receipts migrated under contract (parity-receipt-v2) docs/audits/quorum-PMAT-3577.json, 3/3, cross-inspected
#3664 merge groups stop paying tier=full (HEAD^2 gate vs a SQUASH queue); docs-only → tier=none; --no-renames docs/audits/quorum-PMAT-3658.json, 3/3; aprender-b3's independent self-test 93/93
andon (branch andon/fleet-pv-unmeasured, aprender-b3) check_fleet_pv_shapes_gate.sh: fleet state (pin mismatch, no JSON, no judge, no binary, incapable) is UNMEASURED, never FAIL. Main's guard-tree went red on every intel runner because another repo's CI rewrites intel's pin (paiml-implement#315). A Fail verdict from the pinned binary and a control that does not fire still go RED. self-test 15/15; batch quorum below

New content beyond the constituents: the andon commit, and merge commits whose only resolutions are the generated docs/roadmaps/roadmap.yaml, regenerated with make roadmap-aggregate. The contract derivatives were regenerated under the pinned in-tree pv and are byte-identical (census.json, contracts.nt: no diff).

#3567 evidence, host-side on the batch tree (lambda, fleet-pinned ~/.cargo/bin/pv 0.68.2): PASS verdict=Pass focus_nodes=2329 corpus={…"parity-receipt":7,"pv-contract":1738} planted=3 pc_shape=fired. On origin/main the same binary returns rc 2 on the un-migrated records; #3600 is what makes the verdict possible. The runner-side row appears in this PR's own guard-tree log via #3653's SUMMARY line.

After this merges, the four constituent PRs close as "landed in #".

Closes #3618
Closes #3651
Refs #3658, #3567, #3577

keep-open: #3658 done_when item 7 is observed after merge (5 merge-group shard-1 tier= lines); #3567 closes only on a runner-side SUMMARY PASS verdict= row from a fleet runner; #3577's ONT-4c3 ledger binding is infra's row. #3655 #3653 #3600 #3664 are the constituent PRs, closed by hand once this lands.

Batch 2 folded in (operator 08:00Z: "most PRs can be batched")

Eleven more receipted, cross-inspected PRs, merged in order. roadmap.yaml, census.json, contracts.nt, shapes.ttl and the README count were then regenerated once under the tree-built pv (extract --check rc 0, readme_sync --check rc 0, aggregate idempotent over 97 fragments):

PR change closes
#3634 F2 hybrid guard runs once per (model, apr version, device), receipted #3604
#3613 removed_by gets a shape: refusal-receipt-v1 #3605
#3638 apr run --gpu reports a CPU fallback as a refusal, not a success refs #3602 (item 2 owed to #3606)
#3637 registers PMAT-3604 (roadmap only)
#3647 meta-guard reads a step NAME as a dispatch; pathonly guard ENV-classified #3644
#3665 three hand-typed ggml id→name tables read TRAITS #3662
#3350 ModelConstraints keeps the gated-DeltaNet shape keys refs #3346
#3351 pv L2 column counts links, not an index; --table byte-cut panic #3338
#3395 AutoGluon as a CRUX competitor (24 contracts) #3401
#3496 VERIFY-001 registered on 0.71.0 (roadmap only)
#3660 aprender-core's three hand-typed ggml tables read TRAITS; IQ dequant refuses instead of inventing weights (Alfredo's #3601) #3601, #3656

Left out: #3271 (its three new printf | grep -q sites took check_no_pipe_into_grep_q from 74 to 77) and #3622 (it adds to run_shapes_gate, which is over the complexity threshold); both go to the next batch. Also #3606 (a real code conflict with #3634 in run_entry.rs and forward_qwen35.rs; the reconciliation is on #3606) and #3642 (its contracts/apr-verb-classification-v1.yaml fails pv census: "missing field metadata"). Both stay open for their owners.

Closes #3604
Closes #3605
Closes #3644
Closes #3662
Closes #3338
Closes #3401
Closes #3601
Closes #3656
Refs #3602, #3346, #3495

keep-open: #3602 item 2 is owed to #3606 (not in this batch); #3346 and #3495 stay open on their own later rows. #3634 #3613 #3638 #3637 #3647 #3665 #3271 #3350 #3351 #3395 #3496 #3660 #3271 #3622 #3606 #3642 are PR numbers (batch constituents or left out), closed or kept by hand.

ont-delta: none — integration of receipted changes; the parity-receipt entity type rides #3600's own receipt

🤖 Generated with Claude Code

Round 2: reds found by the first CI run on 55b123f2f (run 35579370078), fixed in one push

Four reds, all fold interactions between constituents. None of them was visible in a constituent's own CI.

red where fix kind
G2.1 freshness guard-tree check_dogfood_coverage.sh #3604 added +4 lines to commands_enum.rs, and 55 ledger rows cite it by line. Re-audited the moved rows (measured_commit intentionally unmoved: partial re-audit rule, PERF-046/#2742) data
ONT ratchet guard-tree check_ont_ratchet.sh contracts_anchored 3 → 6, restamped via make ont-ratchet. Proved under the runner's condition: a stub pv without census PASSes on the new stamp and REDs on the old. The root cause (bare pv probe) is #3679, 0.70 data
shapes_n 6 → 9 shard 1/3 ont4b_shapes_gate The ratchet raised as its own comment instructs, all nine shapes named ratchet
"tree holds 0" shard 3/3 ont4c3_parity_receipts on gx10-pool1 (docker) parity_receipt_denominator.sh turned git's container refusal into a count of 0. It now reads git with safe.directory, and a git failure is an ENV exit 2 that names git. Stub-git row, mutation-proved logic, quorum PMAT-3682

Also in this push: #3681's fragment and receipt, covering b3's two complexity extractions (1f0929f04, 55b123f2f), which were re-judged under the ticket that describes them.

Closes #3681
Closes #3682

noahgift and others added 30 commits September 16, 2026 09:56
…YTE index (#3338)

`pv proof-status contracts/ --table` exits 101 on `contracts/`:

    byte index 40 is not a char boundary; it is inside '∈' (bytes 39..42)
    of `for Q4_K_M Qwen2.5-Coder, quantization ∈ {Q4_K, Q6_K}`

The column width is a byte count (`property.len()`, capped at 40) and
`truncate` sliced `&s[..max]`, so any property whose byte 40 lands inside a
multi-byte char panics. Eight contracts in `contracts/` do; the first one
walked is `apr-inspect-quantization-v1.yaml`.

The budget stays a byte budget — the table is laid out in bytes — and the
cut now walks back to the nearest char boundary.

Two tests, both RED before this commit (each panicked at
obligation_matrix.rs:169): the helper row, and one through
`format_obligation_table` because that is the path the operator hit. The
helper fixture asserts `!s.is_char_boundary(40)` first, so it cannot
silently stop proving anything.

Pmat-Ticket: PMAT-3347
Refs #3347, #3338

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…obligations ticked (#3347)

`obligation_matrix` computed `l2_tested` as `idx < falsification_tests.len()`.
Obligation 3 was "tested" because the contract happened to hold 4 tests,
whoever those tests were about. All 7 obligations of
`qwen35-e2e-verification-v1` showed ✓ before a single test existed. The
fallback was a substring match between the obligation's `property` prose and a
test's `rule` prose, which is an inference, not a claim.

WHICH LINK, decided by counting the corpus (1,842 files, 3,792 obligations,
4,691 falsification tests), not by preference:

  proof_obligations[].discharged_by   89  (62 `falsification_tests[N]`, 13 a
                                           test id, 1 a YAML sequence, rest
                                           comma lists / prose / a kani id)
  falsification_tests[].binds_to      38
  falsification_tests[].obligation    26  (12 name an obligation id, 6 the
                                           exact property text, 8 dangle)
  proof_obligations[].id             180 of 3,792
  kani_harnesses[].obligation      1,918 — that is the L3 column, not this one

All three L2 spellings are DECLARATIONS by the contract author, so all three
are read. `binds_to` is a serde alias of `obligation`, which is safe only
because no entry carries both keys — checked, because serde turns that into a
`duplicate field` parse error rather than a silent pick.

Not read: `applies_to`. 12 `binds_to` values match one, but `AppliesTo` is an
enum with `#[serde(other)] Other`, so the string is discarded at parse and
there is nothing left to compare. Those 12 report `?`, not a false ✗.

WHAT THE COLUMN NOW SAYS. Three values, because "no test covers this" and
"nothing here says which test covers what" are different facts:

  ✓ Tested   a test in this contract cites this obligation
  ✗ Untested the contract's links resolve and none names it — or it ships
             no falsification test at all, which is a reading, not a gap
  ? Unknown  no readable link; not measured. An unread window is Unknown,
             never a tick

MEASURED over `contracts/`, and the drop IS the point — it is what the old
column was hiding:

              before   after
  L2 ✓         3,573      86
  L2 ✗           180      65
  L2 ?             0   3,602
                       (3,753 obligation rows, 873 contracts)

Nothing was adjusted to keep the number up, and no threshold was added.

The two link fields did not exist on the structs, so both keys were written to
disk and silently dropped on parse — the shape of #3314 (`id`) and #2465
(`test_harness`). `discharged_by` is typed `Citation` (scalar | comma list |
YAML sequence) because `Option<String>` failed the WHOLE corpus on
`publish-manifest-v1`: `invalid type: sequence, expected a string`.

RED first: `l2_does_not_tick_for_an_obligation_no_test_cites` — two
obligations, two tests, both citing OB-A. It asserted through the rendered
table (the surface that was lying, and API-stable), so it is the SAME test
before and after: on the old code `Beta holds | ✓`. Its OB-A arm keeps a fix
that merely stopped ticking everything from passing.

Out of scope_paths, and forced: `lint/strict_test_binding.rs` holds the only
EXHAUSTIVE `FalsificationTest` literal in the tree (no `..Default::default()`),
so no schema field can compile without that one line.

Pmat-Ticket: PMAT-3347
Refs #3347, #3091, #3114

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… — it now refuses (#3347)

The gate resolves cited test names against a source index rooted at the
contract path's PARENT. The directory form gets the repo root and finds
`crates/`; the single-file form gets `contracts/`, which holds no source, so
every cited ref resolves to nothing and all of them are reported missing.

Measured on `contracts/pv-artifact-kinds-v1.yaml`, a control whose eight refs
all resolve:

    pv lint contracts/pv-artifact-kinds-v1.yaml --strict-test-binding
        total_refs 8, existing 0, missing 8
    pv lint contracts/ --strict-test-binding
        total_refs 548, existing 521, missing 27  <- none of the 27 is this one

A control contract failing identically to a broken one is a gate that cannot
discriminate, and it fails SILENTLY: `passed` is true in non-strict mode, so
the run still says `Result: PASS` while printing eight false findings.

REFUSED rather than repaired. The scan root is computed in
`provable_contracts::lint::run_lint`, outside this ticket's scope; a refusal
lives in the caller, is honest, and cannot be mistaken for a clean bill. If
the root is later made explicit (a `LintConfig` field the CLI can feed, which
is what `--crate-dir` does NOT do today), this refusal is what should be
deleted.

Exit 1, not the exit-2 `decline:` class: exit 2 belongs to `ZeroContracts`,
whose message ("0 contracts under ...") would be false here — there IS a
contract; it is the gate that cannot run over it.

Two tests, in the CI-wired `cli_integration` target: the refusal names the
flag and prints no PASS and no findings, and a directory-form control proves
the refusal is specific to the single-FILE form rather than to the flag.

Pmat-Ticket: PMAT-3347
Refs #3347

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…racts, 25 tickets, registry edit + mutation proof

Research of ../autogluon (1.6.3 @ 77946149) as a competitive-research
source for aprender, landed the way category N landed linfa and burn
(#3169): the competitor is admitted to the CLOSED registry, every story
is a real contract with falsification gates, every story is a registry
row, and every missing/partial story has a GitHub issue and a roadmap
fragment.

What AutoGluon is, measured from the tree rather than recalled: three
predictors. TabularPredictor (65 public methods, 11 presets, 24 model
families of which 8 are tabular foundation models added in 1.4-1.6),
TimeSeriesPredictor (Chronos-2/Toto-2 pretrained, 30+ local/deep models,
16 metrics incl. WQL/MASE/RMSSE, auto backtesting since 1.5) and
MultiModalPredictor. Evidence under evidence/crux/autogluon/.

What aprender has, measured at eb262f8: automl/ is a single-estimator
hyperparameter tuner (TPE, grid, random, DE, TimeBudget, EarlyStopping);
time_series/ is one univariate ARIMA; encoders, calibration, SHAP/LIME/
permutation importance and KFold/cross_validate exist as building
blocks. No predictor-level fit(label), no leaderboard, no bagging,
stacking or greedy weighted-ensemble selection, no panel forecasting,
no quantile forecast metrics. The gap is the AutoML UX, not the
algorithms.

Category O — AutoML Parity — 24 stories: 9 P0 (the README hello-world:
fit(label), problem-type inference, presets, leaderboard, feature
pipeline, weighted ensemble, time budget, panel forecaster, quantile
metrics), 9 P1 (bagging, stacking, importance, threshold calibration,
deployment artifact, tabular foundation model, backtesting, local
baselines, pretrained forecaster), 6 P2 (refit_full, distill,
infer_limit, fit diagnostics, memory-aware fit, covariates).
MultiModalPredictor, autogluon.cloud, MLZero and Ray-parallel fits are
CUT on the epic with reasons.

  CRUX_COMPETITORS: [&str; 14] -> [&str; 15]   + autogluon

Not a BEAT pillar: aprender claims no pinned-benchmark win over
AutoGluon. Both registry tests that keep BEAT_INCUMBENTS and
CRUX_COMPETITORS apart are extended, not worked around.

Tickets: epic #3370, stories #3371-#3394, label pareto-autogluon.
Roadmap: 25 fragments under docs/roadmaps/entries/, roadmap.yaml
regenerated by the aggregator (idempotent check passes).

Spec: docs/specifications/crux-competitive-research-ux-workflows.md
v2.2 -> v2.3 — §3 gains rows for linfa+burn (category N, which #3169
never recorded there) and AutoGluon; §5 gains Category O; §6 notes
that coverage_intake in the YAML is the source of truth.
coverage_intake 267 -> 291 (partial 72 -> 77, missing 152 -> 171).

Verification:
- pv built from THIS tree validates 25/25 (24 new + master). The stale
  ~/.cargo/bin/pv rejects crux-O-01 with CRUX-002 — the behavioural
  delta proves the registry edit engaged.
- Mutation-verified: deleting "autogluon" from CRUX_COMPETITORS turns
  competitor_registry_covers_the_corpus_vocabulary RED with "autogluon
  is used by contracts/ and must stay in CRUX_COMPETITORS" and
  the_real_crux_registry_rows_are_all_in_domain RED. Restored: 20/20.
- cargo test -p aprender-contracts --lib: 1526 passed, 0 failed.
- Every falsification gate is LIVE-PENDING prose (no `::`), so
  strict-test-binding has nothing to refuse; the obligations are
  RECORDED as unfalsifiable-by-absence, not satisfied.
- README CONTRACT_COUNT regenerated 1835 -> 1866 by readme_sync.sh.
- Guards: roadmap fragment/ids/sorted/additive/completion, contract
  test-binding and enforcement, shell-lint ratchet, hardcoded paths,
  readme claims, grep -q ratchet — all rc=0.

Pmat-Ticket: PMAT-3370

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…epic's acceptance criteria describe the programme, not this diff

Round 2 of the quorum read the epic (PMAT-3370) as the ticket and refused
the admission for not implementing the 24 stories. The admission is its
own unit of work with its own done-when; this fragment says so.

Closes #3401
Pmat-Ticket: PMAT-3401

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nts (its own included) and the scaffold CATEGORY_NAMES edit

Quorum round 3 lane 1 refused on two literal mismatches between the
ticket and the diff: '25 roadmap fragments' (there are 26 once this
ticket's own fragment lands) and an unlisted edit to
scripts/crux_scaffold_contracts.py. The title now lists every path.

Pmat-Ticket: PMAT-3401

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… in CI (proof credit from runs, not declarations), then a Verus pilot on one dequant/parser function (#3495)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…, measured; author claude-fable-5-1

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…v2, extract:parity-receipt, and the 7 back-filled

Until this row the logit-parity records under evidence/parity/** had NO validator
of any kind. Not a weak one — none. That is why seven of them sat in the tree
carrying no comparator for months: there was nothing that could have noticed.

WHAT LANDS
  · contracts/parity-receipt-v2.yaml — three shapes: parity-receipt-complete
    (closed, ignoredProperties empty), parity-comparator-self, -oracle. The
    subset has no sh:or, so the comparator split is two shapes over two
    subclasses the extractor assigns by kind.
  · contracts/parity-receipt-v1.yaml — the retired layout, recorded with NO
    shape: all instances were migrated, and a shape whose target class nothing
    instantiates passes vacuously. The enforcement that replaces it fires — the
    extractor refuses an unmigrated record BY NAME, and so does the predicate.
  · ontology/extract/parity_receipt.rs — record / unmigrated / other, and
    skipping is never silent.
  · scripts/parity_receipt_denominator.sh + evidence/parity/EXPECTED_RECEIPTS —
    the count is pinned by an INDEPENDENT predicate. An extractor checked
    against a number the extractor produced proves nothing.
  · Unknown{ExtractorMiss}, exit 2 — a new element of the verdict lattice. An
    extractor that silently saw the wrong corpus reports the same "no
    violations" as one that saw all of it.
  · the 7 records migrated to v2 and back-filled with comparator {kind: self,
    reason}, in this commit, as the row requires.

THE DENOMINATOR IS 7, NOT 8. The #3574 receipt is in PR #3575, still open; it is
not on main. Measured: 113 files under evidence/parity, 7 parity records, 0 with
a comparator. #3575 bumps it to 8 when it lands — this row's own falsifier on its
first real use.

check_parity_receipt.sh IS NOT TOUCHED, and that is the finding, not an omission.
It validates the THROUGHPUT family (instrument, protocol_ref, lanes[],
decode_tok_per_sec, the #2696 cross-class defect); a logit record has never
carried one of those keys. Folding it in — as item 6 asked — would have deleted
the #2696 validator from a family nobody was watching. One validator per artifact
family, and the discriminator is the artifact's required keys, never its filename.

THE BACK-FILL IS A RELABEL. `raw` is the original apr parity --json document key
for key; every envelope field is quoted from committed evidence named in each
record's provenance.record. model_sha256 is carried only by the one record that
measured it: hashing the files today and attaching that to a receipt about
2026-09-06 would be a claim about a different world wearing a witness's clothes.

One derived field was wrong first time and is worth recording: result.verdict
copied raw.parity — apr's own per-position flag — which says PASS for the two
1.5B cells their own RECORD.md calls RED. It now resolves the threshold from
thresholds.yaml and reproduces all seven readings the RECORD.md files state,
both REDs included. No threshold is ever typed into a shape.

NOTHING IS ARMED. armed_shapes lives in lint-baseline.json, a shared file this
row may not touch (decision 7); the shapes are computed and reported, as
ladder-green was at ONT-4c1. Arming is a follow-up with the label.

CONTROLS, both directions: 7 violations with the comparator stripped from all
seven → 0 as committed; exactly 1 for a single plant, naming focus node and
property; widening sh:in to accept `oracle` turns ont4c3_parity_receipts RED, and
mutating only the real contract turns the fixture-drift test RED; an unmigrated
record declines at exit 2 naming the file; 2 receipts against a denominator of 1
declines naming both numbers; pass / fail / decline are 0 / 1 / 2.

  cargo test -p aprender-contracts --lib ontology::extract::parity_receipt  10 ok
  cargo test -p aprender-contracts-cli --test ont4c3_parity_receipts         8 ok
  bash scripts/parity_receipt_denominator.sh --self-test                     4 ok
  pv lint contracts --gate {sigma,relations,shapes}          Pass, 0 violations
  pv extract contracts --check                                            rc 0

ONT-4c3 is NOT bound in the ONT-001 ledger: that ledger is paiml/infra's
paiml-ontology.md, where v4.8 still defines ONT-4c3 as KERNEL receipts. The
re-scope is an infra PR, not this one — raised with the cop rather than left as a
checked box. Receipt: docs/audits/impl-PMAT-3577-receipt.md.

Refs #3577, #3576, #3269, #3575, #3567

Pmat-Ticket: PMAT-3577

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…pposite is a defect

`ExtractorMissing` already meant the opposite thing: an extractor that does not
exist. `ExtractorMiss` would have sat beside it in the same 18-element lattice,
one letter apart, with the shorter a PREFIX of the longer — `grep ExtractorMiss`
matches both, and any substring test over the reasons merges them silently. That
is the defect class this repo keeps paying for (#3573 today), so the name now says
what happened: the extractor RAN and read a corpus the tree does not declare.

Renamed before it reached main, when a rename is a sed and not a migration.
Caught in review by the cop.

Refs #3577, #3573

Pmat-Ticket: PMAT-3577

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ft in a thread

Two repos holding different definitions of one identifier is a row, not a flag in
a message: nothing collides where a tool would see it, so it collides in a
person's head months later when they implement the ledger's meaning and find
their correct work unusable.

Refs #3577, paiml/infra#814

Pmat-Ticket: PMAT-3577

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ntinel set so the required field manufactures nothing

`removed_by` had 0 occurrences tree-wide, no schema and no validator. Every
refusal #3597 writes would have minted an unenforced convention, and a tree full
of consistent-looking `removed_by:` lines reads as validated when it is
decoration.

PHASE 0 — ITS OWN CONTRACT, AND NOT BECAUSE IT IS EASIER TO WRITE

Option B is refused on a fact: `parity-receipt-v1` DOES NOT EXIST AT HEAD. It is
in unmerged #3600, and there it is deliberately the RETIRED layout with no shape
and no instances. A live validated field on a superseded contract with zero focus
nodes is a field nothing can carry. Independently, by the discriminator this tree
has now used twice — an artifact family is named by its REQUIRED KEYS, never by
its filename — a parity receipt requires host, backend, comparator,
threshold_source and per-position metrics; a refusal requires a verb, a reason,
an exit code and removed_by. They share no required key.

A THIRD option was considered and refused, and it is the more attractive one:
put removed_by on apr-cli-commands-v1.yaml, where the verb already IS the focus
node and the universe is already the trustworthy 111. Refused because the
registry is the UNIVERSE and #3597's method is to DIFF the registry against the
buckets. If the buckets live in the registry, the denominator and the numerator
are the same artifact and the diff is vacuous by construction. The registry says
what EXISTS; a refusal says what was TRIED. Keeping them apart is what lets that
diff be a real diff.

THE FORCED-BINDING TRAP, AND WHY minCount 1 IS SAFE HERE

A required field with no escape manufactures false data: `pv validate` requires
kani_harnesses so authors fabricate one, and apex#57 had lean_theorem copied
verbatim into twelve contracts resolving to nothing, gate green throughout. The
escape is a closed sentinel set, so "there is legitimately nothing here" is
SAYABLE and still CHECKABLE:

  v<major>.<minor>   the release that removes it
  never              refused permanently by design
  unscheduled        a defect with no release chosen

tbd, soon, n/a, pending, a bare `0.70`, a patch-level `v0.70.1` and a git sha are
all RED. A version rather than a sha because a refusal answers "which release do
I need?" and a sha is precise about the tree and silent about the boundary; the
sha form is made INVALID rather than discouraged, because a shape that permits
two spellings gets both.

BOTH ARMS, and the second is the point: 9 cases over 8 fixtures. refusal-ok
carries all three declared forms and passes with 3 focus nodes;
refusal-undeclared-sentinel plants the PLAUSIBLE `tbd` and must go red. Mutation
proved red-capable: widening the pattern to accept tbd fails exactly
a_plausible_but_undeclared_sentinel_is_refused and
accept_and_refuse_are_distinct_answers, and restoring makes 9/9 green. Fixtures
carry the real contract byte for byte, so widening it without them is caught too.

No new extractor: entity {type: json, ref} + vocabulary is the existing machinery
for "validate this document", and a second reader for one more family is the
thing this tree keeps filing against.

done_when 6 — the interim re-check list is EMPTY: `removed_by` still has 0
occurrences at HEAD, so no refusal written under the interim needs revisiting.

The ledger is seeded with the one refusal already measured (`apr bench` refuses
qwen35) so the shape has a real focus node instead of passing vacuously over an
empty list. WHICH verbs land there is #3597's bucket, not this row's.

Refs #3605, #3597, #3600, #3080

Pmat-Ticket: PMAT-3605

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…robe reads ABSENT otherwise

The spec's probe asks `by_entity_type["parity-receipt"] == 7`. Measured on this
branch before the change: the map carried pv-contract, gguf, apr-model, code and
lean, and NO parity-receipt key. The seven records were there — by_shape showed
parity-receipt-complete=7 — but the entity-type map did not carry them, so the
probe would have read ABSENT.

AN ABSENT KEY IS NOT ZERO. A consumer treating it as one measures nothing and
calls it a pass — the same shape as #3610, one map over.

Registering the entity type in Sigma was not enough; it has to be counted where
the probe looks. A test now asserts the key exists AND that it equals the shape's
own focus-node count, so the two numbers cannot drift apart.

Refs #3577, #3610, paiml/infra#831

Pmat-Ticket: PMAT-3577

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…der-contract

# Conflicts:
#	contracts/census.json
… — the shared target dir served another branch's binary

The merge commit's contracts.nt was 107 triples short: every parity-receipt and
parity-comparator node was missing, because `cargo metadata`'s target_directory
is shared across worktrees and the pv on PATH had been built from a different
branch minutes earlier.

AND `pv extract contracts --check` PASSED ON IT, because the check re-derives the
graph with the same binary. A stale tool comparing an artifact against its own
re-derivation agrees with itself about nothing being there — the derived file and
the checker were wrong in the same direction, which is the only way that gate can
fail to fire.

Rebuilt with CARGO_TARGET_DIR pinned to this worktree; the 107 triples return and
by_entity_type[parity-receipt] reads 7 rather than absent.

Refs #3577

Pmat-Ticket: PMAT-3577

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The registry is derived from the tree and drifts the moment a test that reads the
tree is added without listing it. Each of the three new tests failed this guard on
its OWN branch — not a shared commit, as first read.

Pmat-Ticket: PMAT-3598

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The registry is derived from the tree and drifts the moment a test that reads the
tree is added without listing it. Each of the three new tests failed this guard on
its OWN branch — not a shared commit, as first read.

Pmat-Ticket: PMAT-3598

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…release judge

I asked "who else reads this?" for the <unk> assumption and did not ask it for my
own migration. Two consumers read the legacy top-level metrics:

  crates/apr-cli/src/commands/parity_admission.rs  -> mac-check RED
  scripts/check_model_parity.sh                    -> C14, the RELEASE judge

The second is the one that matters: C14 is the pre-publish dogfood's parity gate,
and on the migrated records it reported "no per-position metrics in the output"
for a record that is fine. The migration would have taken the release gate down.

THE RULE, STATED ONCE IN BOTH READERS: a v2 receipt EMBEDS the raw
`apr parity --json` document under `raw`, so look inside the envelope when there
is one. A fresh `apr parity` run is the raw document itself and carries the
readings at the top level. Those are two different INPUTS — a tool's output and an
archived receipt quoting it — not two spellings of one, which is the distinction
that makes this a rule rather than the permissiveness #3613 refuses.

The self-test's fixture builder read the same way, so the must-RED twin was being
built from a KeyError and that control could not have fired.

Verified: 7B sentinel PASS, 1.5B sentinel RED (unchanged from pre-migration),
check_model_parity.sh --self-test 26/26, parity_admission 19 tests.

Refs #3600, #3577

Pmat-Ticket: PMAT-3577

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…on, device) and leaves a receipt

f2_validate_qwen35 proves the CUDA hybrid forward against a 64-position CPU
reference before it will serve a token, on EVERY apr run. Measured (#3598
row 1): 67 % of a 14 s time-to-first-token, 90-93 % of it the CPU forward.
The guard is right to exist and wrong to run per call.

Now: the first run of a (model sha256, apr version, device) triple validates
and writes a receipt; a later run whose triple matches reads it and skips the
forward; `apr run --revalidate` forces a fresh run and rewrites it.

MEASURED HERE, RTX 4090, Qwen3.5-4B-Q4_K_M, the 144-word row, --max-tokens 1,
GPU occupancy recorded before every run, binary built from this tree under a
PINNED target dir (the shared one handed me another worktree's binary first):

  --revalidate, warm cache   17.18 s wall   guard 9,747 ms on 65 positions
  receipt hit, warm cache     7.39 s wall   guard 0 ms      sha256 1,173 ms

  -9.8 s wall; guard 9,747 -> 0; the key costs 1.17 s/run (2.74 GB at
  2.3 GB/s) and is printed separately so it cannot hide in either number.

THE RECEIPT IS THE VALIDATION, WHICH IS WHY IT IS STRICT. Every path that is
not "three keys match" validates, and the three planted-receipt falsifiers
were run END TO END in the real binary, not only as unit tests:

  wrong model sha256   -> re-validated: "receipt is for model bbbb…, this file is 00fe…"
  wrong apr version    -> re-validated: "receipt written by apr 0.61.0, this is 0.68.2"
  wrong device         -> re-validated: "receipt written for NVIDIA GB10, this device is …4090"
  corrupt file         -> re-validated: "receipt unreadable (…: not a receipt)"
  missing file         -> re-validated: "no receipt for this model"

Absence is never consent, and absence and unreadability are told apart.

A RECEIPT IS WRITTEN ONLY AFTER A VALIDATION THAT JUDGED SOMETHING. The guard
has three early exits that let the GPU serve without comparing a position —
SKIP_PARITY_GATE=1, a probe under two tokens, a CPU reference that would not
run. f2_validate_qwen35 now returns F2Verdict {Accepted{positions_judged},
Rejected, NotJudged} instead of bool, and only Accepted writes; otherwise a
one-token prompt would "validate" the triple for every prompt after it.

The decision table (f2_receipt.rs) is pure and CUDA-free, so its 13 tests run
on every build. --revalidate reaches the guard through the same env seam the
guard already reads SKIP_PARITY_GATE from, rather than a 38th positional
parameter on run_entry::run and six forward signatures #3606 is changing.

done_when 5 is partial and says so: [source=receipt|fresh] is on the guard's
stderr line; the `apr run --json` field lands with #3606's StageTimings, and
F2Outcome{source, validate_ms, sha256_ms, receipt_path} is returned to the call
site for exactly that. #3606 and this PR both edit f2_validate_qwen35's return
path; whichever lands second reconciles ~10 lines, and #3606's lane is told.

Evidence: evidence/perf/3604/MEASUREMENT.md + the stderr of all nine runs.

Refs #3604, #3596, #3598, #3606

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pmat work status PMAT-3604 refused ('Item not found'): #3604 was minted as a
GitHub issue with a done_when but never as a roadmap entry, and the quorum
script hard-requires the work item. Fragment + aggregate, nothing else.

Refs #3604, #3634
ont-delta: none — a roadmap entry; no ontology entity, shape, reason or resolution

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…had no caller (#3602)

`apr run --gpu` on a model whose GPU attempt is rejected at runtime printed a
result and exited 0. Measured on lambda (RTX 4090 sm_89, apr 0.68.2 e6f77c9,
qwen2.5-coder-0.5b-instruct-q4_k_m):

  exit=0
  stderr: warning: GPU output diverges from CPU at position 1 (cosine 0.4153)
  stdout: { "used_gpu": false, "inference_time_ms": 33646.13 }

33.6 seconds on the CPU, reported as a successful --gpu run, with `used_gpu:
false` as the only signal — the same value a deliberate CPU run reports.

The decision was already made, recorded and unit-tested. `registry::after_generation`
implements it (R-0b, #3002/#3042): a FORCED accelerator that fell to CPU is a
refusal, a DEFAULT selection that fell to CPU gets a corrective line. A git grep
found its only callers were its own tests. `registry::announce` and
`registry::parity_line` are in the same state, which is why a real run prints
zero `selected:` and zero `parity:` lines.

So this is not a new policy. It is the recorded one, reached from `apr run`:

- `dispatch.rs` classifies the request ONCE via `registry::Request::wanted()`
  rather than re-deriving "forced" — two spellings of one rule drift apart.
- `run_entry::run` calls `reconcile_accelerator` before any success output.
- `--json` gains a `backend` object: `requested` / `ran` / `fell_back`, because
  `used_gpu: false` alone collapses "ran on CPU deliberately" with "asked for
  the GPU and was refused it".

`accel::tests::every_accelerator_surface_calls_the_refusal` passed throughout:
it guards `ensure_available`, the BUILD-time refusal, not the RUNTIME one. A
guard over one of two refusals reads as coverage of both.

Falsifier, both directions (`run_tests_accel_reconcile.rs`, 6 cases). Planting
the pre-fix behaviour (`reconcile_accelerator` → `Ok(None)`) turns
`a_forced_accelerator_that_ran_on_cpu_is_refused` and
`the_json_distinguishes_a_deliberate_cpu_run_from_a_rejected_gpu_run` RED while
`a_forced_accelerator_that_actually_ran_on_gpu_says_nothing` stays green — so
the tests discriminate rather than just failing. `used_gpu: None` is Unknown,
not a fallback: refusing on it would make every non-reporting path a hard error.

NOT included, deliberately: the rejection's REASON (cosine, position) is on
stderr but not in the JSON. It is produced inside realizar's F2 gate and no
channel carries it to the CLI; adding one is a #3606-shaped follow-up, not
something to approximate with a guess here.

Refs #3602, #3483
Pmat-Ticket: PMAT-3602
…annot reproduce

QE2E-INV-001 could not be judged because nothing in the tree held a MEASURED
Qwen3.5 tensor inventory to judge against. This adds one: the 320 tensors of
~/models/Qwen3.5-0.8B-Q4_K_M.gguf (sha256 bd258782...dc517), read straight from
the GGUF header rather than from a model card.

It already falsifies the current arithmetic. Dense/GQA accounting applied to
that file gives 644,400,128 against a measured 752,393,024 — short by
107,992,896, 14.4% of the model, because 18 of the 24 layers are Gated DeltaNet
and no term here counts their conv, gate, state or output projections.

Two shapes in the file are also not what dense accounting predicts, and both are
pinned: attn_q is [1024, 4096] = 2 * num_heads * head_dim (the q projection
emits the attention output gate alongside the query; attn_output [2048, 1024]
confirms num_heads * head_dim = 2048), and attn_q_norm/attn_k_norm are present
at head_dim. The file is also TIED — it has no output.weight.

Refs #3346

Pmat-Ticket: PMAT-3346
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… a hybrid model can be counted

contracts/model-families/qwen3_5.yaml declares inner_size, state_size,
conv_kernel, group_count and full_attention_interval under constraints:, and
ModelConstraints carried none of them. A Gated DeltaNet layer's parameters live
entirely in those dimensions, so every consumer of the descriptor counted
Qwen3.5 as if three quarters of its layers did not exist.

Carried through as ModelConstraints::deltanet: Option<DeltaNetShape> — the
runtime YAML loader (parsing.rs) and the compiled-in registry (build_parsing.rs
+ build_codegen.rs) both populate it, and FALSIFY-MF-QWEN35-010 pins that the
declared values survive the trip and that no other family acquires a shape it
never declared. qwen3_5.yaml is the only descriptor with these keys, so every
other family keeps byte-identical accounting.

model_arithmetic gains gated_deltanet_layer_params (one term per GGUF tensor:
attn_qkv, attn_gate, ssm_conv1d, ssm_alpha/beta, ssm_a, ssm_dt.bias, ssm_norm,
ssm_out) and hybrid_layers (the interleaved schedule). attention_layer_params
gained two terms the real file has and dense accounting did not model: the
gated q projection (2*n_h*d_k) and the q/k norm vectors.

Falsified against a real model, not against itself: fed the 0.8B configuration,
the equation now reproduces the 320-tensor inventory of
Qwen3.5-0.8B-Q4_K_M.gguf EXACTLY — 752,393,024, both layer kinds matching
tensor for tensor.

QE2E-INV-001 is still NOT asserted, and no range was widened to make it pass.
The 9b descriptor now yields 8,344,907,136, up from 8,208,519,168 but still
0.655B below [9.0B, 9.2B]. The remaining gap looks like descriptor drift rather
than missing arithmetic: 9b keeps inner_size 2048 — the value the 0.8B uses at
hidden_dim 1024 — while quadrupling hidden_dim, and its group_count 8 fails
8 * 128 == 2048, a consistency the measured 0.8B satisfies at 16 * 128. Only a
real Qwen3.5-9B file can settle it; none is on this box.

Refs #3346, #3347

Pmat-Ticket: PMAT-3346
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…osed

The note said the obligation was undischarged because ModelConstraints does not carry the DeltaNet shape keys. It does now, and the 0.8B count reproduces the real GGUF exactly. What actually blocks the obligation is descriptor drift at the 9b variant.

Pmat-Ticket: PMAT-3346
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nd pv extract shrank the graph by 356 triples instead of refusing

d3cc76f rewrote the note on the QE2E-INV-001 binding and dropped the
trailing `"` — contracts/binding.yaml stopped being valid YAML at line 764
(`found unexpected end of stream`). Nothing in the PR noticed because
`pv extract contracts` does not refuse a binding registry that will not
parse: it emitted a graph with 15,244 triples where main has 15,600 —
every bound symbol AFTER the broken entry (prune::run, distill::run,
harness_ir::*, ptx_explain::run, …) silently gone — and `--check`
would have agreed with itself. Found while regenerating the derivative
for this adoption, by the drop, not by any gate.

One character. With it, binding.yaml parses (156 entries, same as main)
and the extraction is byte-identical to main's committed contracts.nt, so
this PR owes no graph change after all.

Refs #3346, #3350

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…gfood G2.1, ONT ratchet)

RED 1: check_dogfood_coverage.sh G2.1 freshness. #3604 (69aff21) inserted
4 lines (`--revalidate`) after line 138 of crates/apr-cli/src/commands_enum.rs,
and the ledger was not touched. 55 rows carry 64 citations of that file. All
of them were re-derived: 42 at lines >= 139 shifted by +4, and 22 at <= 138 are
unchanged. For every citation, the script asserted that the cited line's text
at HEAD equals the old line's text at 79a3af7, and it asserted that the diff
is a pure +4 insertion at 138 before touching anything.
`measured_commit` does NOT move. The contract's own rule for a partial
re-audit (the PERF-046/#2742 precedent) says so, and a batch-branch SHA would
not survive the squash merge. The partial re-audit is recorded beside that rule.
  bash scripts/check_dogfood_coverage.sh
    G2.1 freshness       PASS  78 cited evidence files, 1 changed by this branch
    G2.2 reconciliation  PASS  no row lost (837 comparand rows all present)
    DOGFOOD COVERAGE GATE: PASS

RED 2: check_ont_ratchet.sh "contracts_anchored rose 3 -> 6 while
consumer_present=false" on gx10-pool2, whose bare pv is the fleet-clobbered
one with no `census` (root cause #3679, 0.70, not fixed here). This is a
restamp through the sanctioned path, with the repo-pinned pv first on PATH:
  . scripts/pv_bin.sh && PATH="$(dirname "$PV"):$PATH" make ont-ratchet
  PV=target/debug/pv, pv 0.68.2 (aprender provable-contracts verifier), census present
  stamp ont: anchored 3->6, shaped 3->5, total 1852->1881, entity_types 9->10,
             extractors 6->7, shapes_unarmed 3->6, bindable 297 (unchanged)
  bash scripts/check_ont_ratchet.sh --check              PASS (pinned pv)
  runner condition, a stub pv with no census on PATH:
    new stamp  --check  rc=0 PASS  (consumer_present=false, 6 == 6)
    old stamp  --check  rc=1 "FAIL  contracts_anchored rose 3 -> 6" (control)

Local gates, all at this commit:
  bash scripts/guard_tree.sh --no-cargo      76 checks, 0 failed
  bash scripts/check_complexity_ratchet.sh   rc=0
  cargo fmt --all -- --check                 rc=0
  pv validate contracts/apr-dogfood-coverage-v1.yaml   0 errors
  cargo test -p aprender-contracts --lib     1684 passed

Refs #3669

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…2 adds three shapes, as the test predicted

workspace-test shard 1/3 (gx10-pool2, job 106269529690) failed on
ont4b_shapes_gate::the_tracked_repo_graph_is_fresh: `shapes_n == 6` on a
folded tree that has 9. The test's own comment predicted this: "a sibling
PR that also adds a shape (#3600's parity-receipt-v2) will need the number
raised again at merge -- that is the ratchet working". The count stays
hardcoded and all nine shapes are named in the assertion message.

  ./target/debug/pv extract contracts --check    (pv 0.68.2, built from this tree)
    {"triples": 15851, "shapes_n": 9, "written": [], "check": []}   rc=0
  shapes.ttl: ont-shapes-v1, ladder-measured, ladder-green,
    bound-symbols-resolve, lean-statements-grounded, refusal-receipt-v1,
    parity-receipt-complete, parity-comparator-self, parity-comparator-oracle
    (the last three from contracts/parity-receipt-v2.yaml, #3600)
  cargo test -p aprender-contracts-cli --test ont4b_shapes_gate   11 passed

Refs #3669

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ntainer runners — a git failure is ENV exit 2 naming git (PMAT-3682)

workspace-test shard 3/3 (gx10-pool1, job 106269529629) failed
ont4c3_parity_receipts::the_committed_tree_agrees_with_its_own_denominator:
"EXPECTED_RECEIPTS says 7; the tree holds 0". On the docker runners git
refuses the bind-mounted checkout ("detected dubious ownership", exit 128).
The listing `(cd "$root" && git ls-files … 2>/dev/null | sort -u)` ran inside
a process substitution, so both git's message and its exit status were
discarded, and the loop counted an empty universe as zero.

- The listing is now `git -c safe.directory="$root" -C "$root" ls-files …`
  captured into a variable, with git's stderr kept.
- A git failure returns ENV 2 with a message naming git, never a count, and
  `verify` propagates it (`|| return $?`, not `|| return 1`).
- Self-test row: a PATH `git` stub that exits 128 with the dubious-ownership
  message must give exit 2 and a message naming git, and must NOT say "holds 0".

Proof:
  bash scripts/parity_receipt_denominator.sh --self-test    5/5 ok, rc=0
  bash scripts/parity_receipt_denominator.sh                PASS 7 receipt(s) ... says 7
  MUTANT (pre-fix count_and_check from 55b123f) --self-test   rc=1
    "FAIL git refused and the result was exit 1: FAIL evidence/parity/
     EXPECTED_RECEIPTS says 2; the tree holds 0."   <- CI's exact shape
  cargo test -p aprender-contracts-cli --test ont4c3_parity_receipts   9 passed
  bashrs lint: 0 errors · guard_tree --no-cargo 76 checks, 0 failed (incl.
  check_no_pipe_into_grep_q) · complexity rc=0 · fmt rc=0

Sibling sweep (git grep 'git ls-files' and git calls with 2>/dev/null in
scripts/ and crates/*/tests/), on lines the batch ADDED:
  ci_test_tier.sh:180, :252            fail closed (not docs-only / tier=full)
  lib_baseline_ratchet.sh:300, :302    fail closed, but misattribute the cause
                                       ("this branch WROTE it") -- filed
  release/lib_release_params.sh:39     not a test path; a git failure silently
                                       falls back to the tree root -- filed
No other zero-count site. Pre-existing sites outside the batch are not in
this change.

Refs #3682 #3669 #3581

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
noahgift and others added 2 commits September 21, 2026 11:36
Lane 1 (pro-high) measured in the judged worktree. Lane 2 (pro-low) is a
static PASS. Lane 3 (3.6-flash) worked in stale scratch clones and is VOID
under the cop's rule (paiml-implement#317); its seat goes to the non-author
cross-inspection.

Refs #3682

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

Seat-fill, PMAT-3682 (lane 3 VOID: its store references 0 of the judged worktree, 33 of scratch/wt-aprender-main). Non-author (aprender-04), measured in its own worktree detached at 2237f4b95:

bash scripts/parity_receipt_denominator.sh --self-test   -> rc=0, 5/5 ok (incl. 'git refusing the tree is ENV exit 2 naming git, never a count of 0')
bash scripts/parity_receipt_denominator.sh               -> rc=0, 'PASS 7 receipt(s) under evidence/parity/**, and EXPECTED_RECEIPTS says 7.'
MUTANT: git read reverted to '(cd $root && git ls-files … 2>/dev/null || true)':
  FAIL  git refused and the result was exit 1: FAIL  evidence/parity/EXPECTED_RECEIPTS says 2; the tree holds 0.

The mutant reproduces the shard-3/3 symptom on gx10-pool1 exactly. Seats: lane 1 VALID (measured in-tree), lane 2 static PASS, lane 3 filled by this check. 3 of 3. — cop

@noahgift
noahgift added this pull request to the merge queue Sep 21, 2026
Merged via the queue into main with commit a877fa0 Sep 21, 2026
33 of 34 checks passed
@noahgift
noahgift deleted the release/0.69-batch branch September 21, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

removed_by has no shape, no schema and no validator — 0 occurrences tree-wide; settle it as a pv contract BEFORE any refusal is written (blocks #3597) f2_validate_qwen35 runs a full CPU reference forward on EVERY call (67% of a 14s TTFT) — run it once per (model sha256, apr version, device) and receipt it apr inspect (RosettaStone / core::format::gguf::shape) has no byte-size entry for any IQ-family ggml type — crashes on real Unsloth dynamic quants Admit AutoGluon 1.6.3 to CRUX as category O: 24 DRAFT contracts crux-O-01..24 with LIVE-PENDING gates (no implementation, no CLI, no tests), CRUX_COMPETITORS 14->15 in validator.rs + both crux_intake_tests.rs lists, CATEGORY_NAMES[O] in scripts/crux_scaffold_contracts.py, 24 master-registry rows (coverage_intake 267->291) + evidence_sources.autogluon, spec v2.3 §3/§5/§6, evidence/crux/autogluon/ (4 files), 26 roadmap fragments (PMAT-3370 epic, PMAT-3371..3394 stories, PMAT-3401 this ticket) + regenerated roadmap.yaml, README CONTRACT_COUNT 1842->1866 — child of #3370 pv proof-status --table panics on the real corpus: byte-index truncation inside a multi-byte char (obligation_matrix.rs:169)

1 participant