Skip to content

docs/plans deletion readiness: assurance repairs, plan-20 criteria, and the migrations the plans required - #81

Draft
kotama7 wants to merge 766 commits into
mainfrom
bfts_compare_plans
Draft

kotama7 wants to merge 766 commits into
mainfrom
bfts_compare_plans

Conversation

@kotama7

@kotama7 kotama7 commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Draft, opened to run CI. Not requesting merge.

Why

docs/plans can only be deleted once each plan's own criteria are met, and one of those criteria — "CI green" — could not be evaluated: no workflow had ever run on the working branch. Nine of the ten workflows trigger on pull_request to main, so a PR is what runs them. This branch exists for that.

The branch is pushed here rather than to the fork it tracks because that fork has no main branch and its workflows therefore cannot fire.

What is in it

  • Harness assurance. All five verified catalog rows now cite the executions that back them; before this, two shipped zero attestations and three cited their own registration report. Both promotion surfaces derive their evidence from what ran instead of writing it as literals, and the one that runs no controls refuses to write evidence at all.
  • Plan 20 acceptance criteria. 10 of the 37 in plan 18's gating band existed under the normative test ID; 34 do now, all passing. The three that do not are named with reasons, not left blank.
  • Migrations the plans required. The T1–T21 transition table, the M1–M10 meta authority matrix, and the governed-rewrite-vs-smuggling distinction now live in the permanent trilingual docs rather than only in a plan.
  • Two production defects closed. A candidate could change the scorer's floating-point environment process-wide through its own ELF constructor; and the declare-time build ran the candidate's declared compiler and flags through neither the compiler allowlist nor the flag screen.

Known risk

dashboard-frontend.yml has never run. It was added on this branch and there is no node toolchain on the machine this was developed on, so npm ci / typecheck / test are unverified. If anything here is red, that is the first place to look.

Everything else was run locally: full suite 6660 passed / 0 failed, readme_sync --check in sync, 0 broken links and 0 broken anchors, doc-sources 0 errors, public-API contracts 0 breaks, prompt snapshots 175 passed.

kotama7 and others added 30 commits August 7, 2026 15:28
Four independent readers went over the capability-binding series, each finding
handed to a skeptic told to default to refuting it. Fifteen findings, ten
refuted, five survived. All five are mine and three are serious.

Authorizing one brokered leaf authorized every leaf in the catalog. Composite
provisions all share the broker's dispatch tool_ref by design, the leaf travels
in the call's arguments, and nothing anywhere compared the two -- decide() never
saw arguments at all. A run bound to the quantum leaf could invoke the EDA leaf,
or a leaf nobody reviewed, through authorization it already held. The provision
now records which argument names the subject, the client threads the call's
arguments into the decision, and a composite refuses a subject it did not bind.
A decision made without arguments cannot fall back on the dispatch tool's
authority either. record_invocation takes the arguments too, so the governance
record cannot disagree with the decision that admitted the call.

lifecycle_tool_refs was the exact defect this series was removing, reintroduced
by me. The authorization view read it; the two places computing the run's bound
tool set did not. Every legal poll of an async subject was scored as an
invocation of an unbound tool -- an audit finding, an adversarial-engine finding,
a charge against unbound_invocation_rate, and CK-CAP-001 from the capability
kernel, on the sequence the binding exists to permit. bound_tool_refs is now the
shared projection and includes the lifecycle surface; the kernel matches a
binding by its lifecycle refs too.

The route probe accepted the kernel's own unreachable ::/0. Matching the
destination alone matched ip6_null_entry -- loopback, RTF_REJECT -- which every
host carries, so an air-gapped node with IPv6 enabled reported a path off the
host and the retrieval capability became bindable there. That is the case my
docstring claimed fails closed. Both families now require RTF_UP, no RTF_REJECT,
and a non-loopback device.

Two smaller, also mine. tool_registry.md still said an operator must grant the
IBM Quantum scope or nothing binds; the presence filter I added later made that
false. And the test named for asserting the witness runs before the device probe
joined a str character by character, so no multi-character substring could match
and it passed for every possible implementation.

The refuted ten are not recorded; a finding that does not hold is noise.
output_schema_digest was the digest of {} for every Provider, for every tool,
for every version -- a field whose entire purpose is detecting schema drift,
carrying the same value everywhere and unable to detect anything. Not because
the shapes were unknowable but because no tool declared one: zero outputSchema
across all seventeen skill packages.

Declaring one is not a documentation change. The MCP library validates
structuredContent against the declared schema and refuses the call outright when
it is missing, and every handler here returned TextContent alone -- so adding a
schema without also returning structured content would have broken every call to
that tool. Both handlers now return the pair: the text keeps the exact bytes ARI
already digests, the structured copy is what gets validated.

Each schema is a union of the tool's success shape and its own failure shape,
and the two packages needed different unions. HPC reports failure as
{"error": {kind, message, retryable}}; coding reports it as {"error": "<string>"}.
A schema describing only success would have converted a scheduler failure into
an output-validation error and thrown away the message saying what went wrong --
the failure would have been reported as a schema problem. Neither union could be
assumed from the other.

The submit tools derive their schema from JobHandleV1 rather than repeating it,
because a hand-written copy is a second source of truth for the same bytes and
would drift the first time the contract gained a field.

One thing this nearly broke. The coding provider scrubs the real work_dir,
$HOME, username and hostname out of its serialized output; a declared schema
sends a second copy of every result to the agent, so returning the raw dict
would have pushed all of it straight through the boundary _virtualize exists to
hold -- the text clean, the structured twin carrying everything it removed. The
structured value is parsed back from the scrubbed text, and a test asserts that
rather than trusting it.

Verified through real MCP handshakes, not handler calls. The unit suites passed
throughout -- 108 and 47 -- while the library's validation runs only over the
wire, which is also how a missing import surfaced as a live discovery failure
instead of a green test. Both branches were exercised: success shapes returned
and validated, and the error unions asserted deterministically because provoking
each tool's own failure at runtime is unreliable.

Five tools still publish nothing: describe_environment, edit_code, job_cancel,
job_logs, job_result, job_status, probe_platform_capabilities. None is
classified into a capability, so none gates conformance; they are left rather
than done badly in bulk.
The repo venv holds x86_64 wheels, so on an aarch64 compute node numpy does
not import and nothing measured there can run. The arch-specific venv is
built on that node and is hundreds of MB of binaries -- a build artifact,
not a checkout, like the two venv rules above it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
execution_modes.md said the epoch-boundary ari_rqgm -> simple_bfts downgrade
"is audited and validated by the ConstitutionalKernel". No downgrade event
exists anywhere in ari-core, rqgm_state.json is write-once, and
validate_epoch_invariance raises only CK-EPO-001/002 -- neither looks at the
mode. The guide was describing a reserved design as shipped behaviour.

Says "not implemented" now, and the three downstream sentences that repeated
"immutable except at epoch boundaries, downgrade-only" say the mode is fixed
for the whole run, so the page no longer contradicts itself. en/ja/zh.

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

Seven findings survived refutation. Two were latent in the schemas I had just
declared and would have broken real work; two were regressions my own fail-open
fix introduced; one was a claim my own test asserted and one path did not meet.

slurm_submit declared JobHandleV1 and never returns one. SlurmClient.submit
builds its own flatter bridge dict, and JobHandleV1 forbids extra properties
while requiring five the bridge never carries, so every arm failed. The library
would have refused each successful submission AFTER sbatch had queued the job --
the agent gets an error, cannot poll or cancel a handle it never received, and
resubmits. I copied the sibling tools' declaration without checking that this
one returns something else. It now declares the shape it actually produces, and
a real submission through the full path returns job 267890, COMPLETED.

oneOf was the wrong combinator in both packages. It demands exactly one arm
match, and these shapes are not exclusive: a timed-out execution is a complete
result that also carries an error string, so it satisfied both arms and was
rejected. The timeout payload -- stdout, stderr, the execution receipt -- was
destroyed and replaced by a schema complaint, after the timeout had already been
paid. anyOf is what was meant.

Then the two I caused fixing the composite fail-open. The subject gate applied
to every decision, so list_tools -- which decides with no arguments -- filtered
every composite out of the agent's visible tools, and lifecycle refs, which
carry a job handle and never a leaf, could not satisfy a gate that demanded one:
every legal poll refused, of exactly the asynchronous binding the lifecycle
surface exists to permit. The gate now applies only to a real dispatch call, and
dispatch always supplies a dict so an absent one unambiguously means visibility.

edit_code's early return skipped the scrub. No host identity actually reaches
the agent there today -- _edit_code is handed the agent's virtual work_dir, not
the resolved one -- but it is in _WORKDIR_TOOLS and my own test asserted the
structured half always comes from scrubbed text. Rather than weaken the claim I
made the path meet it, and the test now checks every return instead of the one I
happened to write.

None of this was visible to 155 passing unit tests. The library's validation
runs only over a real handshake, so the timeout and submission failures were
reproduced by driving the actual protocol, and the submission fix by queueing a
real job.
Both packages changed when the output schemas landed and again when the review
fixes did, so their commit and package digests stopped describing them. The
previous attempt at this staged nothing and committed nothing while reporting
success; the gate caught the drift again, which is the only reason it did not
ship that way.
…ct gate

The gate exists so one reviewed leaf's authorization cannot carry every other
leaf in the federated catalog. A direct binding on the same tool_ref defeats it:
when the named subject is not bound the authorization view falls back to the
direct binding, and a call reaching a leaf nobody reviewed is admitted under its
authority. Not live today -- the broker entry classifies none of its own tools --
and an adversarial reviewer's finding on this was refuted on exactly that
reachability ground, which is why it needed closing rather than filing: nothing
prevented someone classifying `invoke` tomorrow and deleting the gate without
touching it.

Refused where the combination is written rather than defended against in the
view, because a defence there would have to choose between honouring a real
direct binding and honouring the gate, and a broker's dispatch surface supplying
an ontology capability in its own right is not a thing that should be
expressible at all.

The lifecycle-tool half of the test took three tries to make honest. The first
fixture had no lifecycle tool in the Provider Lock, so an older check refused
first; the second classified it into a workspace-write contract, so the
side-effect check refused first. Both passed for the wrong reason. It now
classifies a lifecycle tool into a read-only contract it genuinely satisfies on
every other axis, so the new backstop is the only thing that can refuse it.
…ts own making

The CUDA promotion recorded capability_contract_digest as a local hash of
{capability_ref, semantic}. That value was never the ontology's contract digest,
so the pin could not go stale when the contract moved -- it had never been bound
to the contract in the first place. The pin checker reads it as a mismatch
because it is one, and always was.

Read the ontology and record contract.contract_digest, refusing outright if the
ontology does not declare the ref. The sibling ToolUniverse promotion has done
exactly this all along; this script simply did not.

This does not repair the bundle already on disk. That one keeps its synthetic
digest until the provider is promoted again on an exclusive V100-class node.

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

Both locks recorded capability_ref and stopped there. A capability claim with no
contract digest cannot go stale: there is nothing for the contract to move away
from, so a promotion stays green forever no matter what the ontology later says
the capability means. That is the same defect the CUDA promotion had, arrived at
by omission rather than by a synthetic digest.

Resolve the digest from ARI's ontology at the one point that is both the writer
and the verifier's expectation, so the pin is read on every verification rather
than merely written once. The resolver is its own module because each promotion
hashes its own source into promotion_verifier_digest, and putting it in the
shared promotion module would move every provider's digest at once.

The bundles already on disk record no pin, so they now fail verification. That
is the intended direction -- they were never bound and could not be shown to be
-- and it clears when each provider is promoted again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The launch paths fill blanks in the subprocess environment from a list of .env
files, then fall back to the Settings key only when the slot is still empty. A
value read out of ~/.env was therefore indistinguishable from one the operator
exported, so a global dotfile nobody remembers writing silently beat the key
configured moments earlier -- and beat it invisibly, because from the launcher's
side both cases look like "a key is already present".

Have load_dotenv_files report which names it introduced, and let Settings
override exactly those. A key the operator exported still wins, which was the
documented intent; a key that only ever sat in a file no longer does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The temporary plans under docs/plans/ were the sole written home for a
number of normative decisions, which is one of the reasons none of them
can be deleted. This migrates what could be migrated honestly: 101 items
across nine plans, each verified against the tree before it was written.

Two things were deliberately not done. Designed-but-unbuilt contracts are
written as gaps, never as behaviour -- a plan describing something the
code does not do does not become true by being copied into a guide. And
stale plan text was dropped rather than migrated, e.g. plan 01's claim
that wrap_node_executor is identity in v1, which the shipped method has
long since outgrown.

The pass also corrected roughly ninety places where a permanent doc
already claimed more than the code delivers. The largest: profiles were
documented as a deep merge when _apply_profile reads four keys and
silently discards the rest, and several pages carried "downgrade-only"
resume language for a downgrade that is not implemented.

en/ja/zh throughout; all six docs gates pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The package docstring called it "a FastAPI-style HTTP server" with a
"uvicorn lifecycle". Neither library appears anywhere under ari/viz --
routes.py serves on the stdlib ThreadingHTTPServer, and ari/viz/v1's own
docstring already says "no FastAPI/uvicorn, no new runtime". The two
sibling modules contradicted each other, and the wrong one is the one a
reader meets first.

The only record that this was wrong lived in a temporary plan under
docs/plans/, which is one of the things keeping that plan undeletable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A research contract requiring correctness emitted one hardcoded property,
"artifact-correctness". No shipped manifest declares it -- the three
registered harnesses declare numerical-equivalence, interface-conformance and
reproducibility -- so every correctness obligation resolved to no harness at
all, with no error and no warning.

Measured against the real catalog, before and after:

    artifact-correctness                      -> 0 manifests, 1 unsatisfied
    numerical-equivalence + interface-conformance
                                              -> 1 manifest, 2 covered,
                                                 0 unsatisfied (coverage enforced)

Which concrete properties establish correctness is a statement about the
science, so it is declared in the property vocabulary rather than written into
this function. reproducibility is deliberately absent: the shipped manifests
offer it at validate and certify only, and a correctness requirement is raised
at screen, so demanding it would make every screen run unsatisfiable.

Unset falls back to ["artifact-correctness"], so a configuration without the
key behaves exactly as before.

THE OTHER HALF CANNOT LAND ALONE, and this is why it is not here. Making the
manifests declare artifact-correctness changes their manifest_digest, and
load_harness_catalog requires report.manifest_digest to equal it. So it needs
all three registration reports, evidence bundles and approvals re-minted --
and reports are minted from evidence now, which needs the correctness driver's
parity probe run on a clean tree and a maintainer's signature. This change
resolves correctness without waiting for that, and composes with it: when a
manifest does declare artifact-correctness, adding it to correctness_properties
is the whole edit.

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

Every web-skill tool already declares a capability_ref in skill.yaml, and the
alias table already resolves most of them to real contracts. None of it reached
the binder: a provision is built from declared_capability_refs_by_tool alone,
and that table was empty, so the whole retrieval surface was unbindable while
looking fully annotated from every other angle.

Four tools are classified -- the ones whose locked policy actually satisfies the
contract they claim, verified against a real Provider lock rather than against
the manifest's hint. Three are left out and the file says why: rerank is
read-only with a model permission where the contract is workspace-write over
network-read, so the alias asserts a mapping the loader would refuse; fetch_url
and web_search have no contract, and inventing one to complete the table would
admit an unprovenanced live fetch as a scientific capability.

The two gates that make any of this mean something -- side-effect equality and
permission coverage -- had no test. They do now, including the negative control
that shows the refusals come from the policy and not from the fixture.

The ja/zh references were several facts behind: they still described
literature.search as unsuppliable and quantum.sample as unregistered, both since
resolved, and carried nothing about the exclusive-node retirement or the
compatibility-rule vocabulary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every forgery demonstrated against this harness used the same two primitives:
fork(), and reading /proc/self/cmdline to find the file the credited time is
written to. The harness caught both after the fact -- a wall bound, a
cross-role overhead calibration, a process-group reap -- but each of those is
a check, and a check only refuses what someone thought to check for.

The timed child now runs under Landlock, applied between fork and exec so it
binds the child and everything the child starts; Landlock is inherited and
cannot be dropped, so a forked writer is under it too. It is allowed the
system libraries and ONE writable directory: the run's own scratch.

What it denies is the point. /proc is not in the ruleset, so a kernel can no
longer read its own cmdline to find the timing path -- both forgeries return
early and score as what they are, honest naive loops, with no check having to
fire. The problem directory is not in it either, so a candidate cannot read
the frozen reference that is its own denominator.

The helper is extracted from drivers/native_candidate_host, which has used it
for the correctness harness's candidate process. The perf path had no sandbox
at all.

TESTED AS TWO LAYERS, deliberately. The detection tests now run with the
sandbox disabled, because a defence that is only ever exercised behind another
one is a defence nobody has tested: on a kernel without Landlock the checks
are the only thing standing and they still have to work. A third test asserts
the sandbox prevents the attempt.

It records what it enforced AND what it does not claim: filesystem access
control does not restrict fork, CPU or memory, and a record that overstates is
worse than none.

Best effort by default. On a kernel without Landlock the run proceeds and says
so; a registered harness whose manifest demands isolation is a different
question, refused by the driver rather than here.

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

LLMClient.complete() used to route this backend around litellm. The branch was
lost in a merge and only the streaming guard came back, so a run configured for
claude_code called litellm as `anthropic` and demanded an ANTHROPIC_API_KEY the
CLI provider never needed. The configured backend was silently replaced by a
different one -- the failure mode that looks like a missing credential and is
actually a missing dispatch.

That disguise is why this sat: the four tests that caught it failed with an
authentication error, which reads as "no key on this machine" rather than "the
backend is wrong". So the new test fails on the dispatch itself -- it makes
litellm.completion raise, which is the assertion an existing comment in this
module already made in prose and nothing checked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An adversarial pass re-checked the migrated text against the code, one
auditor per document, told to default to "refuted" when it could not
confirm. It found fifteen real errors across nine of the twelve documents
audited; a second agent re-verified each before touching anything and
rejected none of them.

The migration's failure mode was the expected one: plans describe intent,
and copying a plan's sentence into a reference manual turns a proposal
into a promise. Two examples. rqgm_schemas.md documented
self_audit.ban_recommendations and escalations as live governance
outputs, but every check that could populate them is severity warn, so
escalations is structurally empty and no ban recommendation can ever be
produced -- the reader and the T19 wiring both ship, the producer does
not. manuscript_complete_contracts.md described a governed reviewer as a
gap the code has, when paper_dispatch.py takes the co-evolution branch
and a governed reviewer is active from round zero.

Corrections are in en/ja/zh where a translation exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The pin resolver imported ari.capability_binding.ontology directly, which is
exactly the boundary a skill is not allowed to cross -- and it did not need to:
ari.public.capability_binding already re-exports load_capability_ontology. Same
loader, same digest, through the surface skills are supposed to use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The catalog loader verifies manifest_sha256 only, so a package edit that leaves
the manifest alone passes silently and the commit and package digests quietly
stop describing the code. These pins are what make that visible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
file_formats.md sourced the "both keys must agree" rule from
docs/plans/ari_rqgm Task 02 -- a permanent reference depending on a
document whose whole purpose is to be deleted. The rule now lives in the
execution-modes guide, in the interlock table, so the reference points
there instead.

That was the last docs/plans/ citation anywhere in the permanent docs.

en/ja/zh; check_doc_links reports 0 broken links.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The commit that added the sandbox said it "records what it enforced". It did
not. sandbox_record() had no caller anywhere, so nothing carried it, and a
comment inside preexec_fn told the reader that "the record above" would show a
failure -- there was no record above. A child whose restrict_to raised ran
UNPROTECTED with nothing anywhere saying so.

That is the exact defect this module spends its length guarding against,
written into the guard, in the commit claiming to have closed it. Found by
being asked a third time whether everything was solved.

The decision is taken once now, in the parent, from one probe:

  - if this kernel CAN enforce isolation, the child MUST -- preexec_fn no
    longer swallows, so a failure kills the launch;
  - if it cannot, no attempt is made and the record says the run was
    unprotected, rather than leaving absence to be inferred;
  - either way the status travels with the measurement that used it, so the
    record and the launch cannot disagree. NativePerfReportV1 carries it.

A launch that could not be isolated is typed as a substrate failure. CPython
replaces a preexec_fn exception with an opaque "Exception occurred in
preexec_fn.", so an untyped one would reach the evaluator and be scored as a
bad kernel.

The detection tests simulate a kernel without Landlock by changing what the
PROBE reports, since breaking restrict_to now exercises fail-closed instead --
which is the other behaviour and has its own test.

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

The bundle on disk claimed ari.quantum.sample.local-ideal/v1 and recorded no
contract digest, so nothing could notice the contract moving underneath it. The
promotion now resolves that digest from the ontology, and this is the first
bundle re-promoted through it: the lock carries
sha256:0abe3edb… and the pin checker reports it bound rather than unbound.

Nothing else about the identity moved. Re-running against the same pinned
environment reproduced the same Bell counts and the same golden and replay
fixtures byte for byte; what changed is the capability pin, today's capture
date, and the digests that cascade from both. The lock digest moved with it, so
the test and the six reference pages that quote it move too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The bundle lives inside the package the catalog pins, so promoting it again
moves both the commit and the package digest. Leaving them stale would mean the
pins stop describing the code, which is the exact silence this check exists to
break.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing in the repository declared kind: benchmark, so the
performance-regression slot that six knowledge-skill import profiles require
was reserved and empty, and the driver built to fill it was unreachable:
resolution is keyed on driver revision and no manifest named one. The driver
shipped; the harness did not.

benchmark + target_kinds ['benchmark-submission'] + accepts_external_target
false. models.py forbids a benchmark claiming external target coverage and the
resolver confines a non-external benchmark to {model, agent,
benchmark-submission}; both are coherent -- a benchmark scores a SUBMISSION --
and a candidate kernel handed to one is exactly that. No rule was relaxed to
make this fit; what changed was the label, in the commit that gave each
requirement the target kind its property implies.

The oracle slot pins the PROBLEM, whose digest covers the contract header, the
frozen driver, the reference, the seed and both negative controls -- so a
change to any of them is a re-registration. The dataset slot pins which sizes.

Verified: the manifest's self-pin matches, and NativePerfDriver.prepare accepts
it -- the problem pin, the case-set pin and the driver digest all agree. It is
NOT in the catalog: a row belongs there only once a registration report earns
it, and that report is minted from evidence a compute node has to produce.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
I wrote it to require the pinned commit to equal the one registration runs at.
That is circular: writing the pin changes the manifest, which changes the
commit that contains it, so no manifest could ever pass. The first real
registration run failed on it -- 12/15, and one of the three was my own gate.

The byte-level guarantee was never this field's job. ``prepare`` already
refuses when the driver digest does not match the code that will run, exactly
and by content. What the commit pin is for is PROVENANCE: where to check out to
obtain those bytes. So the gate asks whether it names a commit this repository
has and one the registration descends from. A pin naming nothing, or naming a
commit off this history, still fails -- which is what the shipped reports would
do, since theirs is regex-checked and compared to nothing.

The performance manifest also inherited the correctness manifest's stale pin
when I derived it. It now names the commit that last changed the driver source
its digest covers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The catalog has a fourth entry and its first non-verifier. Everything it
carries was measured rather than asserted.

The registration ran from a clean worktree at f1db2dd on an EXCLUSIVE aarch64
compute node: the parity probe three times, 15/15 gates, decision
eligible-for-verified. Clean control 1.00593 at spread 0.0411, run-to-run
spread 0.0141 over three runs, both negative controls failing for different
reasons -- slow on the ratio, wrong on the residual bound.

WHERE IT RAN IS PART OF THE RESULT, and the evidence bundle says so. The same
commit and the same clean worktree scored 13/15 on an exclusive x86 64-core
node, where the clean control did not resolve (spread 0.1648 against the 0.1
the instrument is read at). A first attempt on the shared login node reached
0.589. The bundle records the node class for that reason.

The approval is signed 'kotama', authorized in this session, and its
authorization_basis says what the maintainer actually saw: the assistant's
report of the evidence, not the raw artifacts -- which are pinned in the bundle
so a later reader can check them independently. A basis claiming an artifact
review that did not happen would be the same defect this whole registration
path was rebuilt to remove.

Two test expectations moved because the shipped state moved: the catalog is no
longer exactly three correctness harnesses. And the resolver fixture now pins a
REAL commit, because the source-pin gate checks ancestry against the repository
and a synthetic sha names nothing -- the fixture was passing on a gate that
could not have been satisfied by anything real.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
90 shipped Python files cited docs/plans/** by path in comments and
docstrings. Those plans are temporary and meant to be deleted, so every
citation was a dangling reference waiting to happen -- and collectively
they were one of the reasons the plans could not be deleted at all.

233 citations now point at the permanent docs that carry the same rule,
each one checked by opening the target and confirming it actually says
what the citation stood for. Plan section numbers ("plan 04 section 5.4")
are replaced by the permanent document's own section names, since a
section number survives only as long as the plan does.

35 citations were deliberately LEFT ALONE. Those rules have no permanent
home yet -- the legacy Settings default values, the api-key length and
"test"-substring heuristics, the bucketed runtime path layout. An honest
dangling citation beats a confident wrong one, and writing those rules
down is separate work.

Behaviour is unchanged: comments, docstrings, and three pydantic Field
descriptions that had the citation inside them. 1863 passed, 3 skipped;
public-API, skill-manifest, metadata and MCP snapshot contracts all green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
WORKFLOW.md documented "Reproducibility Check (ReAct)" as a four-step
Reason/Act/Observe/Reason loop inside a tool called reproduce_from_paper.
That tool does not exist: the name survives only inside tests, one of
which asserts the error string "Tool 'reproduce_from_paper' not found".
What ships is a chain of workflow.yaml stages over four real functions in
ari-skill-paper-re/src/server.py -- fetch_code_bundle, build_reproduce_sh,
run_reproduce, grade_with_simplejudge -- with the LLM appearing in exactly
two of them.

Scope note: this is separated from 110ec4f on purpose. That commit
repointed docs/plans citations; this file had none, so the rewrite fell
outside what was asked for. It is correct, but it is its own decision --
revert this commit alone if you would rather keep the old text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three correctness harnesses have been REFUSED since their driver digest
changed: prepare compares it and they still pinned the old one. Repinned.

The gates could not have passed them either. They were written against the
performance probe report shape, so a correctness driver -- whose probe reports
per-family verdicts -- could not satisfy a gate it genuinely passes. Both
drivers emit one common vocabulary now: a clean control and a list of
negatives. A gate should not have to know which driver answered it, which is
the same correction as giving each requirement the target kind its property
implies.

And the negative-control gate stopped demanding two. Two failing the SAME way
cannot tell a wrong answer from a slow one -- but that is a question about a
stopwatch, and a correctness verifier has one kind of negative to offer. All
must fail; distinctness is demanded where a driver supplies more than one. The
performance driver refuses to probe when one of its two is missing, so the
strength stays where it belongs.

Stability likewise. A deterministic verifier has no spread, and demanding one
would make it permanently unregistrable; what repeating it establishes is that
it repeats, so the clean-control answers must be IDENTICAL and a difference is
the finding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
They had been REFUSED by prepare since the family migration changed their
driver digest. The pins were not quietly rewritten at the time: a signature
covers what was signed, and re-pinning alone would have made three
attestations describe code nobody approved.

So they were re-registered. Three parity-probe runs each, at commit 67e348c on
a clean worktree: 15/15 gates, eligible-for-verified, all three. Fresh reports,
fresh evidence bundles pinning the artifacts by path and digest, fresh
approvals signed kotama with a basis that says what the maintainer actually
saw -- the assistant report of that evidence, not the raw artifacts.

The catalog now loads four registered harnesses, every one of them holding an
approval over a report whose gates were computed.

Two test expectations moved with the state. The resolver fixture emits the
common control vocabulary. And the test asserting these harnesses REFUSE was
inverted: it documented a transitional breakage, and the breakage is repaired,
so it now asserts the pins match the driver that exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kotama7 and others added 24 commits August 21, 2026 20:45
One set answered two questions. `covered_paths` is what the hook consults to
decide whether a commit touches anything that could invalidate a pin, and the
manifests belong in that answer -- editing one is exactly when the pins want
re-checking. The dirty checks then reused the same set to ask something else:
which files a pin is computed FROM. A manifest is never one. It is what a pin
is written INTO, and its own bytes are bound by `manifest_digest`.

That inverted the signal rather than adding noise to it. Re-pinning REQUIRES
editing the manifest, so `repin` refused the one act it exists to perform and
had to be run from a clean checkout to write a pin at all; and `check` printed
"these answers are about the working tree and not about any commit" on every
commit that touched a manifest, which is most of the commits this surface
sees. Loudest where it carried no information, and where a real instrument
file WAS dirty the same line said nothing new -- it was already printing.

`pin_input_paths` is now the narrower set and the dirty checks use it.
Measured after the split: a dirty manifest alone leaves `check` silent, a
dirty `sandbox.py` makes it say so, and the two sets differ by exactly the
five shipped manifests. The test asserts that difference rather than a count,
so a sixth manifest does not need it updated and a file dropped from the
digest functions still fails.
Plan 20 section 8 says "The following list is normative. The parenthesized
name is the required test or parameterized case ID", and names one for each of
its sixty-eight criteria. Ten of those names exist. Everything else that is
covered is covered under a different name, so plan 18's criterion 9 -- "Task 20
criteria 30-58 and 60-62, 64-68 pass" -- fails on a strict reading for naming
alone. I had dismissed that as stale design naming; the plan calls it
normative, so the dismissal was not mine to make.

Renamed only where ONE test binds ONE criterion, because a normative id on a
test asserting something narrower is worse than the wrong name: the name then
vouches for coverage that is not there.

  8  test_skill_body_digest_mismatch (was ..._blocks_composition) -- the
     admitted body_sha256 is re-checked at compose_knowledge_instructions,
     which is where a Knowledge body is used, so a body edited after admission
     never reaches a node's prompt.
  11 test_instruction_identity_skill_hashes (was ..._includes_skill_and_all_
     lock_digests) -- asserts identity.ordered_knowledge_skill_hashes off the
     epoch lock. The old name claimed the three lock digests as well; those are
     schema-pinned fields of InstructionCompositionV1 and this body never
     asserted them, so the new name is the narrower and truer of the two. It
     does NOT bind criterion 64, which wants them in node provenance.
  17 test_binding_lock_determinism (was ..._byte_identical_for_permuted_input_
     order) -- one requirement, snapshot and environment mint one Binding Lock,
     stated as invariance under the order the provisions arrive in, with the
     request digest asserted equal first.
  46 test_benchmark_kind_separation (was test_benchmark_cannot_cover_arbitrary_
     artifact_requirement) -- drives the _coverage branch that refuses a
     benchmark manifest with no external target for any atom outside
     {model, agent, benchmark-submission}.
  67 test_no_production_eval_import (was test_no_production_module_imports_the_
     evaluation_harness) -- its own docstring already said "this is the
     assertion the criterion names": the ast scan over the whole of ari/ with
     the harness's own tree excepted.

TWO OF THESE COLLIDE WITH A NAME ANOTHER PLAN LISTS. Plan 17 section 14 lists
test_binding_lock_is_byte_identical_for_permuted_input_order and plan 18
section 15 lists test_benchmark_cannot_cover_arbitrary_artifact_requirement
among their planned tests. Neither list is declared normative and neither
plan's completion criteria cite it -- both defer to Task 20 section 8 instead
(17 section 15 item 8, 18 section 16 item 9) -- and the tree already deviates
from those lists in adjacent entries (..._weaker_harness against the real
..._weaker_method, test_baseline_lock_is_immutable_and_revision_is_monotonic
against the real test_baseline_lock_and_revision_are_monotonic). The normative
list wins, and no plan file is edited.

NOTHING WAS CREATED TO CARRY A NAME. Where nothing binds a criterion it stays
uncovered and unnamed: a test that exists and passes vacuously under a
normative id would make plan 18's criterion 9 read as met when it is not.

The capability-binding file is staged as this one hunk only; a concurrent
session is writing criterion 27's test in the same file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
The previous commit said re-pinning requires editing the manifest, so the
refusal fired on the act the surface exists to perform and a pin could only
be written from a clean checkout. A peer sharpened that further: the loop
the tool PRINTS -- "Commit them, then re-run" -- was said to be guaranteed
to hit it.

Both are wrong, and neither of us ran it. Reproduced at the commit that
introduced the refusal, on a worktree with a deliberately staled pin:

  repin -> commit -> re-run              "pins: already current", 15/15 gates
  repin -> re-run without committing     REFUSED

The printed loop, followed as printed, does not hit the refusal. What does
is a manifest already dirty when `repin` is invoked, and the path that
reaches it is editing a field this surface does not manage:
`source_full_commit_sha` is not a derived pin, so moving it by hand is the
ordinary way to arrive holding a dirty manifest and be refused for it. That
is what happened here, twice, and it is narrower than "the tool blocks its
own procedure".

`check`'s note keeps its account unchanged, because that half reproduces: it
appears whenever a manifest is dirty, which is exactly during a re-pin, and
adds nothing when an instrument file is dirty because the stale-pin lines
are already printing.

The split itself is unaffected -- a manifest is still never an input to a
digest. Only the story about how often the wrong set was reached, which was
told twice without being run.
Plan 20 criterion 27 names CK-CAP-016 and nothing produces it. The only
caller that sets `provider_description_effective` is the offline KCA probe,
which asserts the boolean itself, so a sizing pass read the gap as a missing
producer and put the repair at a schema migration: a description digest on
LockedToolV1, compared live against locked, plus regenerating every lock.

That would have measured drift. The criterion is about effect. Tracing the
path shows no description reaches a place where authority is decided, in any
of the three places a description lives, so the honest close is a pin rather
than a producer -- and a drift digest would have made an untrusted free-text
field lock-bearing, which is the opposite of why it is excluded today.

The pin drives the production chain twice -- discovery, SKILLS.lock, Provider
catalog, Binder, authorization view, dispatch -- and compares whole serialized
artifacts, so a description that reached any authority input surfaces without
the test naming where to look. The injected text does reach the model's tool
list verbatim, which is what makes the refusal downstream worth stating.

All three channels are driven, because each is excluded by a different
mechanism and one guard would have proved nothing about the other two: live
tools/list text, manifest text, and a federated leaf's descriptor, whose four
free-text fields are written outside this repository. The two identity-bearing
channels move their supplier's identity and nothing else, and an edit nobody
re-registered is refused by the manifest pin and by the re-derived broker
catalog digest. Every compared artifact carries a control that moves it for a
real reason, so none of the equalities can hold by being unable to differ.

Verified red on removal at each of the four doors: folding the text into the
runtime tool identity, into the locked policy, into the resolved manifest
policy, or into a composite provision each fails the test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
…g so

Two commits ago this file said the note "adds nothing new when a real
instrument file is dirty, because the stale-pin lines are already printing".
That is a reason to delete it, and it is wrong in the one state it exists
for. A peer caught it; reproduced here rather than taken on description.

With `sandbox.py` edited and the performance manifest pinned over those
dirty bytes, `check` prints:

  hpc_gemm_problem_correctness.yaml: driver.sha256
  note: 1 file(s) a derived pin is computed from are uncommitted, ...
  1 manifest(s) pin a digest the code no longer has

The performance manifest is ABSENT. It reads current, because `check`
recomputes from the same working tree the pin was computed from, so the pin
matches the dirt. Every other manifest is covered by a stale line; that one
is covered by nothing but the note. And it is in the worse state of the two
-- a pin no checkout can satisfy, rather than a stale pin, which is detected
everywhere.

So the note is loud where it means nothing and is the ONLY signal where it
matters, which is the opposite of redundant. The disposal stays as built:
silent on a dirty manifest alone, firing on a dirty instrument file, and
counting the instrument files rather than the manifests.

Third correction to the same paragraph. The first two were written without
running it; this one is what running it says.
This file was written after a history rewrite orphaned the source pins two
bundles cite. It named three fields and three globs, so it found what it was
told to find. Applying a teammate's rule -- derive the coverage, do not restate
it -- it now walks every shipped artifact and takes any key whose NAME says it
holds a commit.

It immediately found five citations the enumeration never looked at, in
evidence/production_e2e/, and all five are unresolvable. So the gate written for
this defect did not cover the place the defect was living.

AND THE DEFECT IS MINE. 361a85d7 was a commit of this branch. My rewrite moved
every hash and the reflog expiry and `gc --prune=now` that followed removed the
original, so a retained end-to-end record now cites a commit this repository
does not have. I reported that purge as verified five ways -- orphaned blobs
gone, 61 refs unchanged, HEAD unchanged, every worktree valid, fsck silent. All
five were true. Not one asked whether a shipped artifact CITED a pruned commit,
which is the question this file exists to ask and could not, because it was
looking at three fields.

IT IS NOT REPAIRABLE, and that is recorded rather than worked around. The
rewritten equivalent 97107af is reachable and identical in tree, but both
records are digest-bound over their own contents, so editing the field would
forge them, and four test modules read them. Re-running a production e2e to
repair a provenance field is not a repair, it is a different run.

SO THE SPLIT IS DERIVED, NOT EXEMPTED. Live artifacts are whatever the catalog's
entries name, plus the evidence bundle's siblings; a retained record is one the
catalog does not name, which is the distinction the repository already draws --
test_harness_catalog_revision calls production_e2e "the frozen record of a past
run". Live pins must be reachable. Retained pins are pinned: the unresolvable
set must be exactly the loss recorded here, so a second rewrite that takes a
different commit turns this red instead of inheriting an exemption.

Both halves mutation-checked: emptying the recorded set fails the retained test,
and pointing a live bundle at the lost commit fails the live one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…eed the open set

Two guards from this round's own work, both found by its read-only audit, both
the shape this repository keeps producing: a test that restates a subset where
the property is what matters.

CRITERION 11 WAS VOUCHING FOR COVERAGE THAT WAS NOT THERE, which is the exact
artifact the round was warned against. Plan 20 §8.1 item 11 reads "Node
instruction identity contains ORDERED Skill hashes", and the test renamed to its
normative ID asserted `ordered_knowledge_skill_hashes == (one_hash,)` against a
fixture admitting ONE Skill. A 1-tuple compares equal under every ordering, so
the assertion held for a composer that sorted, reversed, or ignored the lock --
and the normative name then said the criterion was met.

Two Skills now, in different composition slots, PROPOSED in the opposite order
to the one the lock admits, so an identity echoing the request comes out wrong.
The second body is chosen so its digest sorts BEFORE the first: with the
original text the lock's order and lexical order coincided and the
sort-discrimination assertion could not fail, which is its own version of the
same defect. Mutation-checked: wrapping the composer's `order` in `sorted()`
turns the test red.

THE BROKER'S UNTRUSTED-TEXT SET WAS FOUR NAMES. `load_brokered_catalog`
validates no descriptor keys and `_composite_provision` reads named keys off a
plain dict, so the set of fields a third party can write into a leaf descriptor
is OPEN -- and a fifth would have been uncovered. The injection is now derived:
every string-valued descriptor key the mechanism does not read, plus the three
container shapes a broker really uses, plus a key no descriptor has yet. Twelve
fields instead of four, and a floor that fails if the inventory stops arriving
rather than passing on an empty set.

The boundary is stated rather than left implicit. List-valued fields are
excluded because they are REFERENCE sets -- `source_ids` names sources the
catalog cross-checks -- so appending to one adds a dangling reference, not free
text, and the refusal that produces would have made the test pass for a
structural reason with nothing to do with a description. Measured: the first
version of this change did exactly that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The three native correctness Harnesses were the last whose registration evidence
declared network_isolation from the REQUEST with nothing anywhere to derive it
from, and the gate that reads the observation skipped all three.

WHAT WAS ACTUALLY MISSING, after two wrong readings of my own. Not a schema
field, and not the isolation: this family runs a candidate in an isolated CHILD,
and `_restrict_candidate_filesystem` fails closed -- Landlock unavailable raises
and the case fails -- so a native candidate never runs unrestricted. That is a
stronger guarantee than the perf path, which records filesystem_isolation: false
and continues. What was missing is that nothing was RECORDED, and the network
was never observed at all.

THE OBSERVER IS WHOEVER LAUNCHES. A family verifies a Callable and never
launches, so it cannot know what the candidate ran under; the worker spawns the
isolated host, so it can. The child observes and returns it in its envelope, the
worker collects it across cases and RE-CREATES the report so report_digest
covers it, and the driver carries it into the attestation beside the oracle. The
filesystem entry is a precondition rather than a finding and is recorded anyway,
so a reader does not have to know the host fails closed; the Landlock ABI is the
one negotiated rather than the assertion that some Landlock was.

THE RESTRICTION PROVED ITSELF TWICE WHILE I WROTE THIS. Importing the probe
lazily beside its first use gave "Permission denied: .../assurance/sandbox.py",
and hoisting only that import gave the same for .../python3.13/socket.py. The
observation is taken BEFORE the restriction: Landlock does not touch a network
namespace, so before and after are the same answer and only one is reachable.

TWO TESTS PINNED THE EXACT SHAPE OF A RESPONSE and failed on a key they are not
about -- the child's envelope compared whole against {ok, result}, and the
worker's output compared whole against a two-key stub. Both now assert their
claim: that the candidate ran restricted and returned the right answer, and that
a scientifically wrong candidate leaves a completed process with a fail verdict.
The worker's stub becomes a real report, because the worker now re-creates one.

The report schema and both driver digests move, so the three manifests need a
re-pin and their bundles need re-earning; that is the next commit.

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

The native driver's bytes moved when the isolated child started reporting what
it ran under and the worker started attaching that record to the report, so each
manifest named bytes the code no longer has and prepare() would have refused
every run.

Their evidence and signatures are void until re-earned by RUNNING the controls.
That is what makes the observation appear in a shipped bundle rather than only
in the code that takes it, and it is what stops the network gate skipping these
three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Criteria 50, 58 and 62 of Task 20 section 8 had behaviour in the tree and no
test carrying their normative id. Two of the three had a test that stopped one
step short of the criterion.

50 -- resume does not resolve against the latest Harness catalog. The dedicated
test proved the published record cannot be exchanged for a newer one. That is
not the criterion: a resume that re-entered the resolver and happened to keep
the old bytes satisfies every assertion it made. Renamed to
`test_resume_pins_harness_snapshot` and extended into the seam a resumed process
enters -- `admit_from_checkpoint` with the newer catalog on disk and the
admission builder wired to fail if it is called at all -- then reads back the
Harness view the resumed run would verify against. The identities compared are
derived by diffing the two admissions over `KCARunAdmissionV1.model_fields`,
floored against arriving empty, rather than listed here.

58 -- the claim-evidence hard gate and the program verifier are two independent
hard gates. Nothing asserted independence; the existing decision test varied the
claim gate only while the assurance gate was `not_required`, which is the one
configuration in which the question cannot be asked. Independence can be lost at
three places and is now bound at all three: the decision (neither gate's pass
rescues the other's fail, and an unknown certification is a failed one), the
evidence (neither gate's report digest or reason code appears under the other),
and the code, by scanning every module of both packages for an import of the
other with the scanner's own ability to see a dependency asserted.

62 -- Harness execution does not depend on agent free tool choice. Half of it
sat in a CLI test and half in an MCP test, and neither reached the verifier. The
authority half is read off the whole capability matrix rather than a named role;
the surface half takes the agent Provider's tools from its own `list_tools` and
drives each one from its own input schema with `FixedVerifier.run` replaced by a
tripwire, then fires the tripwire deliberately so "nothing executed" cannot mean
"nothing could"; the execution half hands the verifier the manifest a caller
chose, a lock the Harness is absent from, and a driver that rewrites the locked
command.

Every claim was proven by deleting its mechanism in a shadow copy and watching
the test go red -- four mutations for 50, four for 58, six for 62.

Criterion 50's test is currently red on this branch for an unrelated reason: the
shipped Harness catalog does not load, because a manifest was re-pinned without
its registration report being re-earned. It is green, and its mutations red,
against the catalog as it stood one commit earlier.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three of Task 20 section 8's criteria had behaviour in the tree and no test
carrying the id the plan declares normative. Two of them had a test that
stopped one step short of the criterion, and one was covered on one of the two
channels the system actually has.

42 -- an Evaluator/Reviewer claiming a fail as a success creates governance
evidence. CK-HAR-019 was tested at `_kca_reports_for_node`, one level below the
run loop, and nothing asserted that the finding is WRITTEN anywhere: the audit
append could have been deleted with the suite green. `test_fail_misrepresentation_evidence`
enters at `run_per_node_kernel_check` against a clean admitted baseline Lock --
so an honest node raises nothing at all and every code that appears belongs to
the misrepresentation -- and reads the persisted `kernel_report` entry back off
the immutable audit log. Both shapes of the override predicate are exercised,
because an override lands on either the aggregate status or the frontier class
and each hides from the other, and the `audit` posture case separates evidence
from enforcement: the finding is still recorded where it is not acted on.
The system has a SECOND channel for the same act, and a guard over one of them
would have been a guard over half the criterion, so the test also drives
`_assurance_contradiction_findings` and enumerates the adversary types that turn
the accusation into evidence refs out of `ADVERSARY_SPECS` rather than naming
them.

43 -- an uncertified result cannot reach final publication. The lane
classification was tested; the publication DECISION was not, and the pure
`build_publication_decision` test hands it `assurance_passed` as a literal. The
publishable-build fixture now takes an assurance posture and one Attestation, so
the whole path runs -- Attestation on disk, exploration snapshot, manuscript
builder, readiness evaluator, `finalize_runtime_publication`. The certified
control is asserted to publish with the assurance gate PASSING and naming that
Attestation's own bytes, because "blocked" is otherwise consistent with a
pipeline that blocks everything and "publishable" with a gate never required.
A screen-tier Attestation and no Attestation at all each block, with every
unrelated gate still passing.

57 -- the published claim's target is the certify Attestation's target. Same
two runs, differing only in the artifact the Attestation is about: same node,
same verdict, same tier, same property results. A certification of a different
artifact stays visible as `stale` evidence rather than vanishing, and the final
decision blocks.

Every claim was mutation-proven in a scratch copy: removing the override
predicate, removing the audit append, and removing the contradiction findings
each turn 42 red; dropping the certify-tier conjunct or pinning
`assurance_required` false turns 43 red; dropping the target comparison turns
57 red.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
…it fires

Task 20 section 8 criteria 31, 36, 37, 38 and 65 existed only as one case each
of the catalogue-wide severity parametrization, where a single name would have
vouched for sixty rules. Each now has a dedicated test under its normative id,
and each states its rule once and then reads the places production applies it
out of the source, because the repeated defect here is a guard written for a
real rule that covers part of where the rule lives.

That is not hypothetical for criterion 37. The first version derived the
references an Attestation target must agree with from the comparisons
validate_attestation performs; deleting the current-candidate half of that
comparison shrank the derived inventory to match and left the test green. The
inventory now comes from the function's SIGNATURE, which cannot shrink when a
check is deleted, and every production call site is checked for handing it an
independently obtained current target rather than one it already compares.

- 31: seven routes refuse a Generator changing the required Harness, its
  tolerance, its oracle pin or the Lock -- the whole K/C/A resource table
  (exactly one non-read grant, and the Kernel blocks every other mutating pair
  in every tier), the producer literal, every _digest field the Lock entry and
  the Verification requirement pin, a SELF-CONSISTENT Lock a resumed run reads
  back as plain JSON, a revision dropping a required Harness, and write-once.
- 36: a tolerance that moves is refused at all four layers that compare one,
  the Kernel's two producers raising one code under two rule ids.
- 37: refused by the procedural validator, the Kernel, the Evidence Clerk
  (both comparisons, the envelope one isolated so the digest arm cannot answer
  for it) and the manuscript snapshot, where nothing raises.
- 38: refused for every Lock pin the model carries, by the Fixed Verifier
  before one can be minted, and for a genuine re-presentation of a document
  that is still internally valid.
- 65: driven through the production per-node fan-out, each artifact superseded
  ALONE, each lighting exactly one report context -- with the context set the
  fan-out emits as the axis inventory rather than a list of three.

No CK code is named in an assertion: each finding is the difference between a
coherent state and the same state with one field moved, so deleting the rule
empties the difference. Every claim was mutation-proven in a scratch copy: 26
single mutations red, and the three that stayed green are redundant pairs or a
documented fallback, each red once the redundancy is removed too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
Task 20 section 8 criteria 45, 49, 51 and 64 get a test under their
normative id in a new suite, and 49 turns out to name a real gap rather
than a missing assertion.

Every scalar digest field in the Knowledge/Capability/Assurance contracts
already carried Field(pattern=SHA256_DIGEST_PATTERN). The anchors living
inside a tuple or a dict did not, so

    HarnessPropertyResultV1(covered_atom_digests=("a" * 12,))

was accepted, and a truncated compatibility identifier could stand in for
an anchor the Kernel compares. Two of them -- attestation_digests and
skill_snapshot_digests -- sat directly beside a sibling field whose own
validator did check the format, which is the shape of a guard that covers
part of where its defect lives. The fix is a Sha256Digest alias carrying
the same pattern, applied at every such field, so the constraint is
declared where it is read.

The tests derive their inventories instead of restating them: the anchor
set is walked from the three ari.public K/C/A surfaces; the Kernel's
scientific blind spot is read off the result model's own free-form payload
annotations and its executor blind spot off builtin_driver_map(); the
compatibility path's artifact names come from the module that publishes
them and its node fields from the two bridges' ASTs; and the node
provenance compared is whichever identities the run admission and the Node
dataclass share. Each carries a floor that fails if the derivation ever
returns an empty inventory, and each was proven to bind by removing the
mechanism and watching it go red.

Criterion 68 is deliberately left uncovered; see the notes with this work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
…that had moved

The harness catalog did not load: `load_harness_catalog` raised "Harness
registration report differs: hpc/gemm-correctness". Three of five rows --
gemm, spmm and stencil correctness -- shipped a registration report AND a
registration evidence bundle whose `manifest_digest` back-reference no longer
matched the manifest beside them. Bisected to 575502d, which re-pinned the
three manifests without re-earning what describes them.

Measured rather than assumed: all 21 failures in the full suite (10 failed, 11
errors of 6679 collected) traced to that one defect, across five test files,
with identical counts at 575502d and at HEAD. Nothing else was wrong.

Re-earned through the signed re-registration path, four controls per family:
  hpc/gemm-correctness     clean=pass negative=fail runs=4 gates=15/15
  hpc/spmm-correctness     clean=pass negative=fail runs=4 gates=15/15
  hpc/stencil-correctness  clean=pass negative=fail runs=4 gates=15/15
All five rows now have matching back-references, cite four or five real
attestations, and none cites its own registration report.

THE ROOTFS WAS VERIFIED BEFORE IT WAS USED, and this is worth recording because
the surface does not do it. `--container-rootfs` is read to build the licence
inventory that goes into every manifest, and `promote()` checks only that the
path is a real directory -- not that it is the pinned image's filesystem. An
inventory built from the wrong bytes would have been signed into three
manifests. Checked instead: the inventory digest computed from the rootfs is
sha256:29703c4d..., byte-identical to the one the shipped manifests already pin,
so it is that image's. A caller who does not think to check gets no warning.

THIS IS THE THIRD TIME TODAY, by three different actors, and none of them was
the same one. The tooling refuses correctly every time and always AFTERWARDS:
`repin check` runs in the pre-commit hook and asks whether a manifest pins the
code it is measured by, which is a different question from whether the
artifacts BESIDE the manifest still describe it. The second question is asked by
nothing until `load_harness_catalog` fails for whoever pulls next. Recorded, not
fixed -- new prevention is a maintainer's call, not a repair I was asked for.

`catalog_source_revision` names the commit the registration was taken at and the
pairing is recorded beside it. Tracked logs re-checked for host identity: 44
files, no hits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…strain one more anchor

Both were found by the read-only audit of the previous round, both confirmed by
mutation, and both are the same defect: an inventory DERIVED FROM THE MECHANISM
UNDER TEST shrinks exactly when that mechanism does, so deleting the thing the
test exists to protect makes the test drive less and pass.

CRITERION 35 was vouching for coverage that was not there. `test_harness_revision_monotonic`
read the pinned-digest inventory out of `validate_harness_revision`'s OWN AST.
Measured: delete the dataset, oracle, driver and container terms, leave only
tolerance, and the derived set collapses from five fields to one, the test drives
only what is left, and it PASSES. A sweep of 503 tests under that mutation gave a
failure set identical to the baseline, and `grep 'cannot swap'` finds exactly one
exerciser in the repository -- this test. A revision swapping the dataset, the
oracle, the driver or the container out from under an already-locked Harness was
refused by nothing any test noticed.

The universe now comes from `LockedHarnessV1.model_fields`, which does not shrink
when a comparison is deleted, and every pinned digest must be either compared or
listed as deliberately uncompared with its reason. Deleting a comparison leaves
that field unclassified and fails; adding one to the model fails until someone
decides which side it is on. Re-mutated: the four-term deletion is now RED.

A KNOWN GAP RECORDED RATHER THAN CLOSED. `result_schema_digest` is pinned on
`LockedHarnessV1` and is NOT compared by the strengthening branch, so a revision
can swap the schema its results are validated against. The schema can move
without the driver moving -- that happened on this tree today. Closing it is a
production change to `validate_harness_revision` and a maintainer's call,
because it would also refuse a revision that legitimately follows a regenerated
schema. It is now in the exclusion list with that reason attached instead of
being invisible.

CRITERION 49, both halves. The test selected trust anchors by NAME SUFFIX, so an
anchor named for its ROLE was invisible: deleting `HarnessAttestationV1.execution_identity`'s
`Field(pattern=...)` left the test green while the field accepted "" and "a"*12.
And a second live instance of the same production defect the criterion is about:
`KnowledgeSkillEntryV1.body_store_key` -- the key a Skill BODY is fetched by --
was a bare `str` accepting "", short values and uppercase, while all three
producers supply a full content address and its sibling on the next line has
carried the constraint all along. Constrained now.

I WROTE THE EIGHTH INSTANCE WHILE FIXING THE SEVENTH, and caught it by mutation
rather than by review. The first repair selected anchors by whether the field
CARRIES the constraint -- which reads better and is worse, because deleting the
constraint then removes the field from the inventory and the test goes green on
exactly the defect it was rewritten to catch. Measured: that version passed with
`execution_identity` unconstrained. The role-named anchors are pinned by name in
an explicit classification instead, with a sibling test that fails if one is
renamed or removed so the list cannot go stale silently. Both mutations are now
RED.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three times today a manifest moved without its evidence being re-earned,
by three different actors, and every one of those commits was clean. The
pin gate asks whether a manifest pins the code it is measured by; nothing
asked whether the artifacts BESIDE it still describe it. Moving a manifest
passes the first and fails the second, and the failure surfaces later, in
`load_harness_catalog`, in the working tree of whoever pulls next -- and it
takes the whole catalog down rather than one row.

IT ASKS THE LOADER. The obvious gate compares each manifest's
`manifest_digest` against the back-references in the report and evidence
beside it. That is three of the comparisons `load_harness_catalog` already
makes -- it also checks each artifact's own digest, its harness id, the
approval, and the digest the catalog row advertises -- so writing those
three would be a subset standing in for a definition that exists, and would
go quiet the day a fourth cross-check was added. The property wanted is
"the catalog loads", so that is the question asked. 177 ms.

IT IS A SEPARATE FILE. `repin_and_promote_harness` is held to naming no
path inside the published harness tree, on the principle that it cannot
write a bundle it cannot name, and a test scans its constants to enforce
that. This check must READ those bytes. Moving it in would turn that guard
red rather than quietly relaxing it.

IT HAS ITS OWN ESCAPE, and that is the point rather than tidiness. Moving
an instrument is exactly what the pin gate refuses, so every actor that
moves one sets ARI_SKIP_PIN_CHECK -- correctly, it is the documented flow.
One commit in this history says so in as many words while being the commit
that left two Harnesses pinning drivers that no longer existed. A single
variable carrying both assertions would hand that habitual escape the
catalog too, which is the case this gate exists for.

Measured on real commits in a scratch worktree: a manifest moved without
its evidence is refused; ARI_SKIP_PIN_CHECK=1 does not get past it;
ARI_SKIP_CATALOG_CHECK=1 does; a commit touching nothing under the harness
tree is not checked at all. Both halves mutation-checked -- nesting the
block inside the pin guard, and letting the gate compare fields itself,
each turn a test red.
…solation

The last three Harnesses whose registration evidence declared network_isolation
from the REQUEST now derive it from a record. Each bundle ships six observations
taken inside the pinned container by the isolated host itself:

  network_isolation: True   network_probe: ENETUNREACH
  filesystem_isolation: True   mechanism: landlock   landlock_abi: 6

Four container executions per family, 15/15 gates each, clean=pass and
negative=fail, signed over a source pin taken from a clean worktree at the
commit whose bytes were measured.

WHAT THIS FINISHES. test_no_shipped_bundle_claims_more_than_it_observed skipped
all five bundles when it was written, then three, and now none: every row asserts
against a run that recorded its own namespace. The claim and the record are the
same object for the first time.

ALL THREE IN ONE RUN, and that is not a convenience. The families share their
executions and one license inventory, so re-registering one lets its bytes land
under the other two signatures -- the surface refuses exactly that, and the
inventory is only replaced when re_registering equals the whole registered set.
Four attempts of mine were refused before I read that: an unextracted rootfs, a
partial one missing the five non-dpkg licence paths, a dirty tree, and one
harness at a time.

THE ROOTFS IS DELIBERATELY PARTIAL, and worth saying so. Extracting the whole
image stalled at 1.4 GB of 1.47 GB and consumed a two-hour allocation, which is
the unsquashfs behaviour this repository already documents and which -p 1 did
not avoid here. The licence inventory reads var/lib/dpkg/status, usr/share/doc,
and five named files; extracting exactly those is 20 MB and seconds, and yields
the same inventory because nothing else is read. Container identity is not
established from it -- that is the SIF's byte digest against its site binding.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Six items were declared MOVED from one plan to another and were in none
of the receiving plans, so each existed nowhere while both plans read as
though it were owned.  Verified absent by grep before acting; each is now
recorded where the declaring plan said it went, and the declaring plan
repointed at that landing.

Three are open questions with no answer anywhere, recorded honestly as
open so deleting a plan cannot lose them:

  03 -> 05  sub-run governance / ARI_DISABLED_TOOLS_FOR_CHILD (also 00's
            Q-15, which routed through 03).  The stub's inert behaviour
            was never the open part -- that is already permanent doc in
            three languages; the open part is whether sub-run spawning
            is governed at all.
  05 -> 01  the _run_loop refactor, absent from 01's R1-R8, now R9.  The
            plans sized that function at ~925 lines; it is ~1,630 today.
  15 -> 05/07  the exploration reviewer binding.  reviewer is a founded
            evolvable prompt role with no component identity, so
            exploration-side leniency is unaccusable; the decision splits
            across 05 (who may be implicated) and 07 (founding a
            component to receive the binding).

One was already permanent and is repointed rather than duplicated:

  15 -> 10  R5 UtilityRecord.  rqgm_schemas.md already states the finding
            more completely than R5 did, so 10 records the arrival and
            points at it.

Two needed a permanent home rather than another plan:

  12  per-call epoch attribution was deferred to nobody and its
      "(documented)" parenthetical was false.  It is true now:
      troubleshooting.md gains the limitation in en/ja/zh.  Reading the
      code to write it showed the risk's mechanism was also wrong -- skill
      calls are not best-effort attributed, they carry no epoch at all,
      which contradicts the plan's own 5.4.  Noted, not amended; that one
      is the maintainer's.
  00  Q-51's final call was owed at Task 13 wrap-up and 13 said nothing.
      Half is answered by publication (env vars freeze by rule); half is
      not (RQGM checkpoint files are documented but absent from
      release_policy.md's enumeration).  Recorded as the split it is.

No completion, deletion or delete-after criteria touched; no status table
touched; no plan deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
Three plan files were the only copy of a design decision the permanent docs
claimed to carry, so a reader had to open a task plan -- or the source -- to
learn what a rule id meant.

- The shipped T1-T21 transition table now sits in the schema reference, one
  row per rule id with its triggering input, its edge-specific guards, the
  three universal guards, the T16 (from, to) encoding decision and the
  forbidden-complement rule. The reference used to name the table and point
  at transition_rules.py; T7, T12 and T17 appeared in no table row anywhere.
- The M1-M10 meta-tier authority matrix now sits in the architecture page,
  each MAY-NOT row against the enforcement point that ships rather than
  against a convention, plus the MAY list it is the complement of. Invariant
  8 paraphrased four rows and no page carried the other six.
- The governed-rewrite vs ungoverned-smuggling distinction now sits under the
  "weights are rewritten at every boundary" claim that creates the apparent
  contradiction. It survived only in MetricSpecWeightCap's docstring, which
  its own plan says is not enough.

All three in en/ja/zh with matching heading structure. The plans keep their
text and gain a dated note pointing at the permanent home; no completion
criterion, deletion criterion, checklist or status table is touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
…ontent

The ari_rqgm_paper plans were the normative reader of fourteen ari_rqgm
plans: their bodies deferred BY SECTION for design content ("snapshot layers
per ../ari_rqgm/07 §5.2", "validate_capability (../ari_rqgm/04 §5.4 item 3)",
"the score-comparability invariant from ../ari_rqgm/10 §5.6"). Each of those
sections has since been migrated to a permanent page, so the citation now
names that page instead — 72 citation sites, 69 links plus 6 bare section
references, each checked by reading what the cited section actually says and
confirming the permanent page says it too:

  ../ari_rqgm/04 §5.4 items 3/5/11  -> reference/rqgm_schemas.md
                                       (CK-ACC-*, CK-REG-00x, CK-CTX-001)
  ../ari_rqgm/04 fixed-layer rule   -> guides/execution_modes.md
                                       (the claim gate is never an evolution
                                        target), key invariant 9
  ../ari_rqgm/07 §5.2/§5.3/§5.5     -> rqgm_prompt_spec.schema.json, the
                                       runtime walkthrough's six-stage ladder,
                                       the four facades
  ../ari_rqgm/10 §5.1/§5.3-5.6/§6   -> key invariant 6, frontier-repair
                                       schemas, and the state page's
                                       stale/invalidated/removed vocabulary
  ../ari_rqgm/13 §3/§5.2/§5.3/§5.4  -> guides/rqgm_evaluation.md
  ../ari_rqgm/15 §5.3/§5.6          -> "The accountability binding on
                                       validated_attack"
  ../ari_rqgm/12 §5.3/§5.7          -> configuration's one-schema-home caps,
                                       internal_boundaries' context views
  ../ari_rqgm/01 §7, /02 §5.6, /05
  §5.3, /06 §5.4/§5.8, /08, /09     -> the RQGM mode boundary, the epoch
                                       freeze, the motion-pipeline records,
                                       key invariants 4 and 7, the four facades

/08 now has no reference of any kind from this set, and /01, /02, /05 and /09
none either; /04's only remaining mentions are the two Depends-on status
lines.

Left alone deliberately: every ORDERING dependency (the INDEX Depends-on
column, each plan's Status line, "owned by parent 14", "parent 15 has
landed"), and every completion criterion, deletion criterion and delete-after
checklist — which is why three content citations survive inside criteria
(04 §11 -> /10 §5.4, 05 §11 -> /07, 07 §13 -> /15). Three more have no
permanent home and are reported to the maintainer rather than repointed to
the nearest plausible page: the Q-49 open-question register (../ari_rqgm/00
§5.9, cited three times), the replay-pool analogy quoted verbatim from
../ari_rqgm/06 §5.8, and the parent-set Task-12 test list (../ari_rqgm/12 §9).

check_doc_links.py: 0 broken links, 0 broken anchors; all 36 distinct
anchored targets also resolve against committed HEAD.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzZuTFFv37eeHja2NY4kqr
…overstating its source

The citation-repointing pass produced exactly the failure it was warned against
and its own audit caught it: a citation repointed at a page that does not carry
the content reads as migrated and is not, so the next reader stops looking. Four
sites, each verified by reading the target rather than by matching the name.

"NEVER KERNEL-WRAPPED" ATTRIBUTED TO A PAGE THAT DOES NOT SAY IT, twice.
docs/guides/execution_modes.md has zero occurrences of the phrase; the claim is
made at docs/reference/rqgm_schemas.md and docs/concepts/rqgm_architecture.md.
Both sites now cite the register that states it about the claim gate. The
Layer-0 half of each sentence still cites execution_modes, which does carry it.

A COUNT THE TARGET DOES NOT SUPPORT. paper/05 said "the four prompt-snapshot
layers" and cited `architecture.md#test-ci-and-docs-surfaces`. That section is
the right home -- it describes the prompt-snapshot layers -- but as a
hand-maintained hash pin plus raw and rendered goldens, and its own "four" is a
different four (the contract-snapshot surfaces, three as first written plus the
viz REST inventory). The count came from the plan and is not carried anywhere,
so it is dropped rather than pointed at a page that appears to support it.

A CITATION NAMING THE RIGHT TARGET AND LINKING TO ANOTHER. paper/05 said "the
fixed transition table's topology-agnostic inherited coverage" and linked to the
four facades plus a config section -- while the transition table had just been
given a permanent home in the same batch. Repointed at it.

M3 OVERSTATED ITS SOURCE, in all three languages. The row said
`DEFAULT_FORBIDDEN_TARGETS` "names it explicitly" of four things; measured, it
names `fixed_verifier` and `constitutional_kernel` and not the metric recomputer
or the claim-evidence gate, which are excluded by the allowed-targets rule
instead. The row now says which mechanism does which, because a reader checking
the cell against the constant would otherwise find it wrong and have no way to
tell whether the rule or the record was.

The anchor I first wrote for the register was itself wrong -- the heading's
ellipsis collapses to a single hyphen, not two -- and the doc-link checker
caught it. Gates now: 0 broken links, 0 broken anchors, doc-sources 0 errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`readme_sync --check` was red at HEAD -- a hard gate with no continue-on-error,
so no PR from this branch could pass -- because `check_catalog_loads.py` and
`test_catalog_load_gate.py` landed without their Contents entries.

Written out rather than left as the placeholder, because the thing worth
recording is WHY they are separate from the pin gate: "does this manifest pin
the code it is measured by" is repaired by re-pinning, and one surface both asks
and repairs it; "do the artifacts BESIDE this manifest still describe it" is
repaired only by re-earning them, and no amount of re-pinning helps. Also why
the check ASKS `load_harness_catalog` instead of comparing three back-references
itself, why it is a separate FILE from the re-pin surface, and why its escape is
`ARI_SKIP_CATALOG_CHECK` and deliberately not the pin gate's -- every actor that
moves an instrument already reaches for that one, so a single variable would
have handed the habitual escape the case this gate exists for.

Seven `— TODO` entries remain in ari-core/tests/README.md, none of them mine and
none of them gating: the gate compares Contents path structure and never reads a
description.

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

Both amended by the maintainer, on questions raised by the migration that
carried their content into the permanent docs. Recording who decided and why,
because a criterion that changes without a reason is indistinguishable from one
bent to make itself met.

PLAN 09 asked for the transition table "reproduced verbatim". Its §5.2 is 19
rows of planning prose with guard names the code no longer uses; the shipped
`TRANSITION_TABLE` is 21 rows. Verbatim would have put a stale table in the
permanent reference under a criterion reading as satisfied -- the exact failure
this procedure exists to prevent, arriving through the procedure. The criterion
now asks for the shipped table with each row's decision and its reason, which is
what was written.

PLAN 14 named "the permanent RQGM developer guide" as the destination. No
document carries that name; docs/guides holds rqgm_migration, rqgm_evaluation
and rqgm_gui, none of which is where the contradiction arises. The criterion now
names the architecture page, which is where it arises -- the §5.9 paragraph sits
directly under the claim that weights are rewritten at every boundary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
WHAT THIS CHANGES: nothing. `git diff --cached HEAD` is empty. The merge exists
so PR #81 can build a merge ref, because nine of the ten workflows fire on
`pull_request` to main and GitHub runs none of them while a PR is CONFLICTING --
which is why "CI green", a criterion under all 55 files in docs/plans, had never
been evaluated on this branch at all.

WHY IT IS EMPTY, measured rather than assumed. Of main's 279 non-dependabot
commits since the 2026-05-29 merge base, 278 already exist in this branch's
history under different hashes -- this branch is main's work rebased, plus 762
commits on top. The one exception, "fix 071 ledger hash", edits
docs/refactoring/reports/orchestration_status.md, a file main itself deleted at
087f299 and which exists on neither side. Dependencies are ahead here, not
behind: react ^19.2.8 against main's ^18.3.1, plus @tanstack/react-query and
pdfjs-dist that main does not carry.

So all 396 conflicts were the same content arriving twice, and every one
resolved to ours.

THE EIGHT FILES MAIN WOULD HAVE RESTORED, and the reason they are not here. Git
offered eight paths present on main and absent here, and each was DELETED BY
THIS BRANCH in a named commit: the evaluator prompt trio at 9d1817a "make
scientific evaluation fail closed", ari-skill-hpc's server.py and slurm.py at
19ddaf2 "expose public HPC runtime package", two paper prompts at 50f255c
"make paper builds evidence locked", and a replicate snapshot at 4dc05f0.
Taking them would have resurrected deliberate deletions -- the same trap that
makes merging origin/RQGM wrong, where 17 of its 25 unique files are the
gui_refresh plans this campaign migrated and deleted. Each was checked against
its deleting commit rather than by inspection.

ANNOUNCED FIRST. Three live peer sessions were asked before this started; all
three cleared it, and two measured their own exposure independently. One warned
that 30 files inside the driver digests live under ari-core/ari and that moving
any of them stales all five Harness manifests at once -- correct in principle,
and it does not apply: main changed ZERO of the 27 digest-covered files since
the merge base, so the merge cannot move a digest. Confirmed after resolving:
"every manifest pins the code it is measured by" and "the shipped catalog
loads".

Gates before committing: READMEs in sync, 0 broken links and 0 broken anchors,
doc-sources 0 errors, public-API 0 breaks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kotama7 pushed a commit that referenced this pull request Aug 23, 2026
WHAT THIS CHANGES: nothing. `git diff --cached HEAD` is empty. The merge exists
so PR #81 can build a merge ref, because nine of the ten workflows fire on
`pull_request` to main and GitHub runs none of them while a PR is CONFLICTING --
which is why "CI green", a criterion under all 55 files in docs/plans, had never
been evaluated on this branch at all.

WHY IT IS EMPTY, measured rather than assumed. Of main's 279 non-dependabot
commits since the 2026-05-29 merge base, 278 already exist in this branch's
history under different hashes -- this branch is main's work rebased, plus 762
commits on top. The one exception, "fix 071 ledger hash", edits
docs/refactoring/reports/orchestration_status.md, a file main itself deleted at
087f299 and which exists on neither side. Dependencies are ahead here, not
behind: react ^19.2.8 against main's ^18.3.1, plus @tanstack/react-query and
pdfjs-dist that main does not carry.

So all 396 conflicts were the same content arriving twice, and every one
resolved to ours.

THE EIGHT FILES MAIN WOULD HAVE RESTORED, and the reason they are not here. Git
offered eight paths present on main and absent here, and each was DELETED BY
THIS BRANCH in a named commit: the evaluator prompt trio at 9d1817a "make
scientific evaluation fail closed", ari-skill-hpc's server.py and slurm.py at
19ddaf2 "expose public HPC runtime package", two paper prompts at 50f255c
"make paper builds evidence locked", and a replicate snapshot at 4dc05f0.
Taking them would have resurrected deliberate deletions -- the same trap that
makes merging origin/RQGM wrong, where 17 of its 25 unique files are the
gui_refresh plans this campaign migrated and deleted. Each was checked against
its deleting commit rather than by inspection.

ANNOUNCED FIRST. Three live peer sessions were asked before this started; all
three cleared it, and two measured their own exposure independently. One warned
that 30 files inside the driver digests live under ari-core/ari and that moving
any of them stales all five Harness manifests at once -- correct in principle,
and it does not apply: main changed ZERO of the 27 digest-covered files since
the merge base, so the merge cannot move a digest. Confirmed after resolving:
"every manifest pins the code it is measured by" and "the shipped catalog
loads".

Gates before committing: READMEs in sync, 0 broken links and 0 broken anchors,
doc-sources 0 errors, public-API 0 breaks.

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

github-actions Bot commented Aug 23, 2026

Copy link
Copy Markdown

Quality Report

  • Generated (UTC): 2026-09-14T16:50:35Z
  • Repo: /home/runner/work/ARI/ARI
  • Checkers: 1 run, 7 unavailable
  • Findings: 23 total; 0 net-new vs baseline

Checkers

checker status findings allowlisted Δ new note
check_complexity unavailable 0 0 0 no JSON for check_complexity in quality-artifacts
check_import_boundaries unavailable 0 0 0 no JSON for check_import_boundaries in quality-artifacts
check_public_api_contracts error 0 0 0 unparseable JSON (check_public_api_contracts.json): Expectin
check_viz_api_schema ok 23 23 0
check_prompts unavailable 0 0 0 no JSON for check_prompts in quality-artifacts
check_dead_code unavailable 0 0 0 no JSON for check_dead_code in quality-artifacts
check_directory_policy unavailable 0 0 0 no JSON for check_directory_policy in quality-artifacts
check_bundle_budget unavailable 0 0 0 no JSON for check_bundle_budget in quality-artifacts

Areas

area LOC findings
ari-core/ari/agent 6490 0
ari-core/ari/assurance 11418 0
ari-core/ari/calibration 17 0
ari-core/ari/capability_binding 2417 0
ari-core/ari/cli 6929 0
ari-core/ari/clone 675 0
ari-core/ari/config 4980 0
ari-core/ari/configs 69 0
ari-core/ari/evaluator 3166 0
ari-core/ari/knowledge 3088 0
ari-core/ari/llm 4665 0
ari-core/ari/manuscript 6222 0
ari-core/ari/mcp 2721 0
ari-core/ari/memory 353 0
ari-core/ari/migrations 435 0
ari-core/ari/orchestrator 4312 0
ari-core/ari/pipeline 5605 0
ari-core/ari/prompts 432 0
ari-core/ari/protocols 924 0
ari-core/ari/providers 1296 0
ari-core/ari/public 1238 0
ari-core/ari/publish 908 0
ari-core/ari/registry 511 0
ari-core/ari/rqgm 42379 0
ari-core/ari/schemas 20 0
ari-core/ari/viz 18602 23
ari-skill-benchmark/src 932 0
ari-skill-coding/src 1311 0
ari-skill-evaluator/src 825 0
ari-skill-harness/src 197 0
ari-skill-idea/src 3221 0
ari-skill-knowledge/src 195 0
ari-skill-memory/src 3837 0
ari-skill-orchestrator/src 3642 0
ari-skill-paper/src 6124 0
ari-skill-paper-re/src 8521 0
ari-skill-plot/src 1019 0
ari-skill-replicate/src 2664 0
ari-skill-tool-registry/src 16633 0
ari-skill-transform/src 3877 0
ari-skill-vlm/src 1003 0
ari-skill-web/src 1544 0

Dead code

  • Source: check_dead_code (status: unavailable)
  • Note: no JSON for check_dead_code in quality-artifacts
  • Baseline: scripts/quality/baselines/dead_code_baseline.json
  • Safe-to-delete surviving human review: 0
classification count
SAFE_DELETE_CANDIDATE 0
QUARANTINE_CANDIDATE 0
TEST_ONLY 0
DOCS_ONLY 0
DYNAMIC_REFERENCE_RISK 0
PUBLIC_CONTRACT 0
REVIEW_REQUIRED 0

kotama7 and others added 3 commits August 23, 2026 16:23
CI's `mcp-tool-contracts` job caught this on the first run this branch has ever
had: `sync_skill_metadata.py` reported eight shipped JSON Schemas out of date
against the models they are generated from. The generated artifacts are derived,
the models are the source, and nothing local had compared them.

At least one is mine. Constraining `KnowledgeSkillEntryV1.body_store_key` and
`HarnessAttestationV1.execution_identity` to `SHA256_DIGEST_PATTERN` earlier
today added `"pattern": "^sha256:[0-9a-f]{64}$"` to the published schemas, and
the published copies still described the unconstrained fields. This is the same
shape a peer session hit this morning from the other direction -- a model field
removed while its generated schema kept declaring it required -- so the drift
runs both ways and the generator is the only thing that closes it.

COMMITTED WITH ARI_SKIP_PIN_CHECK=1, and disclosed because that escape is
routinely reached for and should never be silent. Regenerating a result schema
moves `expected_result_schema_digest`, and three manifests --
hpc_gemm_correctness, hpc_spmm_correctness, hpc_stencil_correctness -- now pin a
digest the code no longer has. The pin gate refused correctly.

The re-pin and re-earn follow in the next change, from a CLEAN tree, and that
order is the point: a derived pin taken over uncommitted bytes names a commit
whose contents no checkout can reproduce, which is a defect shipped in this
repository this morning and caught only by a promote from a clean checkout.

The catalog still loads; this staleness stops `prepare` at run time rather than
breaking the load.

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

Regenerating the eight drifted schemas moved `expected_result_schema_digest`,
which staled hpc/gemm-correctness, hpc/spmm-correctness and hpc/stencil-
correctness. Re-earned through the signed re-registration, four controls each:
all three clean=pass negative=fail runs=4 gates=15/15.

Taken from a CLEAN tree at the schema commit, which is the point of splitting
these two changes: a derived pin computed over uncommitted bytes names a commit
whose contents no checkout can reproduce.

`catalog_source_revision` names the commit the registration was taken at, and
the pairing is recorded beside it. That pin was also red in CI for a second
reason, and it was the guard working: the catalog had moved to `aff7dee4` under
a revision still reading `e8a99f7e`, so a run pinned to that label could not say
which of two catalogs judged it. The pairing now names one catalog.

Both harness checks after: "every manifest pins the code it is measured by" and
"the shipped catalog loads".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI's `mcp-tool-contracts` job, second sub-check: nine source pins across four
Provider packages named commits and package digests the tree no longer has --
`ari-skill-coding`, `ari-skill-hpc`, `ari-skill-tool-registry` and
`ari-skill-web`, each pinning a `full_commit_sha` from an earlier state of this
branch. Not caused by anything in this change; the pins were simply never
retaken as the branch moved, and nothing local compares them.

That gap is the reason the gate exists and its own README says so: the loader
checks only `manifest_sha256`, so a package edit that leaves the manifest alone
drifts in silence, while a manifest edit fails the whole load. The commit and
package digests mean something only because this hard gate compares them.

Re-pinned with `--update`; verify mode now reports "provider catalog pins match
the tree". Blast radius checked rather than assumed: all six Provider capability
pins still match the ontology, both harness checks still pass ("every manifest
pins the code it is measured by", "the shipped catalog loads"), and the rest of
the job passes locally -- skill metadata up to date, skill manifest conformance
passed.

One pre-existing note surfaced by the capability check and left alone: an
`ari-cuda-environment-validator` bundle pins an earlier contract and is
superseded by a sibling bundle of the same Provider, so there is nothing to do
unless that substrate returns.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

1 participant