Skip to content

feat(perf): #263-A — the measurement instrument, calibrated (CALIBRATION_ONLY) - #351

Open
PhysShell wants to merge 12 commits into
mainfrom
claude/p-022-analysis-wiring-mwhqlw
Open

feat(perf): #263-A — the measurement instrument, calibrated (CALIBRATION_ONLY)#351
PhysShell wants to merge 12 commits into
mainfrom
claude/p-022-analysis-wiring-mwhqlw

Conversation

@PhysShell

@PhysShell PhysShell commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Что и зачем

#263-A: the frozen instrument for #263's P-022 performance baselines — how a number is produced, never what the number should be. It permits calibration and structurally refuses the decisive measurement, which is #263-B behind the D7 freeze.

No decisive numbers, no thresholds, no engine verdict on any decisive workload. No repetition count N, no escalation ladder, no Rust-vs-Python comparison statistic, no D7 freeze payload. Those are D7's, preregistered before the decisive run, and a harness that offered any of them would be choosing them.

Тип изменения

  • feat — новая возможность
  • fix — исправление бага
  • docs — документация
  • refactor / chore / test / ci — без изменения поведения

No production behaviour changes: the instrument reads the product, it does not alter it. rust/, frontend/ and ownlang/ are untouched by this branch.

Review round 2 — four blockers, all repaired

CI was green on the reviewed head and proved exactly what it had been asked to prove. All four were found by reading, not by running.

1. The production D7 freeze could never have armed. The verifier took the payload's own directory as the repository, so it asked git for <commit>:p022-263a-d7-ratification.json for a file that lives under docs/evidence/. <rev>:<path> resolves from the tree root; only a path starting ./ or ../ is read relative to the current directory. Every control passed because the throwaway fixtures put both objects at the repository root, where the wrong model is accidentally right — controls proving the verifier on a layout production never has. The root now comes from git rev-parse --show-toplevel, every object path resolves against it, and the fixtures use the nested production layout. The ratification must also name the exact repository-relative path this instrument reads its payload from, so it cannot point at any blob that happens to hold the same bytes.

2. The advertised signed-ratification path was written wrong and could not have worked. It read git verify-commit --raw from stdout; git writes that raw status to stderr, and the helper discards stderr. A correctly signed commit would have verified cryptographically and then been refused for not naming its own key. No control caught it because no environment here holds a signing key. Fixing the read would still leave the accept direction untestable, which is how the broken branch survived, so signed mode is withdrawn from the accepted contract: "signature" must be exactly "none" and the freeze is recorded as unsigned. An advertised path that is observably wrong is worse than an absent one.

3. The calibration timed failure and called it reproducible. .NET was installed at ~/.dotnet the whole time and never on PATH — which is also why the fingerprint recorded an empty dotnet_sdk and nobody looked. The launcher rungs exited 127 before doing anything, and nothing checked exit codes: 12 of 44 committed cells reproducibly measured a command-not-found path. The CI gate could not tell either, since it asked for cells, raw timings, RSS, provenance and reproducibility but never whether a cell had done its work.

Each rung now declares the exit codes that mean it did its job plus a post-condition proving it, verified once, untimed, with output captured, so the check never bills a benchmark. A cell that fails is not timed at all, the calibration is refused, the CI gate reads the verdict, and reproducibility compares outcome identity as well as medians — two runs agreeing about the wrong path is the worst possible reassurance.

rung exit codes that mean it worked post-condition
core-usage 2 driver banner on stdout, nothing on stderr
core-parse-refused 2 refusal on stderr, nothing on stdout
core-full-human / core-full-sarif 0 or 1 a rendered verdict / a SARIF document (1 is "leaks found", not a failure)
launcher-e2e 0 a rendered verdict

These were probed against both engines before being written down. The first version of the floor check asserted the banner contains the word "usage" — it does not, and asserting instead of measuring is exactly the habit this contract exists to break.

4. P1 was not closed. launcher-extract invoked the launcher with --emit-facts and was documented as "no core runs, so this isolates the frontend stage". The launcher copies the intermediate facts and then runs Stage 2 anyway, so the interval contained the whole pipeline while recording itself as launcher startup plus extraction — the same defect as the old core-usage label, one level up, hidden locally because rc 127 never reached Stage 2. Changing the production launcher is out of scope and a direct extractor process is not the launcher's extraction stage, so the rung is withdrawn and launcher-scoped extraction isolation is recorded unavailable. frontend-extraction stays measured as a member of launcher-e2e.

Also: perf-provenance-complete now requires the committed report's harness and manifest digests to equal the shipped ones. Presence alone let a stale report tick every box.

Round 3 — that last control immediately found a real one

The strengthened provenance check went red on its first Windows run, and not for the reason expected. The harness digest named the checkout, not the instrument: the same tree hashed af32f04ddbad on Linux and 51ef2ca2422a on windows-latest, where core.autocrlf rewrites source on checkout. One instrument, two identities.

That matters more than a mismatched string. D7's C1 freezes the harness digest and #263-B runs on both platforms, so a freeze would have armed on one and been refused on the other — and the refusal would have looked like tampering.

This repository already knew the defect class: .gitattributes pins docs/evidence/*.json to LF because the mutation campaigns' definition hashes hit it first, which is exactly why the workload manifest digest matched across platforms while the harness digest did not. But an attribute only governs files git checks out under it — a tree predating the rule, a zip download, or a different local config all still differ. So the digest normalizes line endings itself rather than delegating its identity to a checkout setting. sha256_file stays raw: the candidate binary's identity is its actual bytes, and normalizing a binary would be a different kind of wrong. perf-digest-platform-stable holds both halves.

Round 1 findings, repaired earlier and still standing

P0 — the C1/C2 verifier read a proxy. It armed on any JSON carrying three values anyone with this repository can compute in one line, proving the instrument was the instrument and calling that a freeze. The freeze is now two objects, because a payload cannot name the commit that contains it — that sha would have to be inside the bytes hashed into it, which is the reason detached signatures exist: a payload (kind/schema, complete C1 and C2, hashing over its own canonical body) and a separately committed ratification naming that body hash and the commit the payload is frozen at. C2 is checked for presence and never read — its values are thresholds.

P1 — the ladder floor claimed to be a phase it merely contains. core-usage is now composed over process-startup-core + cli-argv-refusal and described as a lower bound; subtracting it from core-parse-refused yields a derived bound, not parse.

The honest limit, recorded rather than routed around

rung interval contains observability
core-usage core startup + argv parsing + usage refusal composed — a lower bound on startup, not startup
core-parse-refused startup + argv + read + parse + door refusal composed
core-full-human / core-full-sarif startup + argv + parse + bridge + analysis + render composed
launcher-e2e launcher startup + extraction + core + render composed

bridge-lowering, analysis and launcher-scoped extraction isolation are marked not separately observable. Derived views — read+parse as core-parse-refused − core-usage, and the sarif − human renderer difference which is not rendering in isolation — are labelled derived bounds. #263 asks for unavailable stages to be marked, not imputed.

The firewall is structural, not clerical

Exactly one function starts a clock, and it refuses a decisive workload unless the gate is armed. It is deliberately stricter than "no paired runs": a single-engine timed run of a decisive workload is refused too, because a stopwatch appears nowhere in the permitted list of enumerate / hash / fetch / availability-check / non-timed smoke. Calibration workloads are generated by the harness, so the split holds by construction.

The four carried obligations

how it is implemented how it is proved
(a) gate exists now, dormant IdentityGate, built before the instrument is final — D7's C1 freezes the harness digest perf-gate-dormant: observes harness + manifest + reference identity, unarmed, fails closed on an unreadable file and on a bare identity echo
(b) arming is data only two committed JSON objects; the digest covers the harness and manifest and neither of them perf-gate-arms-by-data builds a real freeze in a throwaway repository in the nested production layout: it arms with no source patch, the harness digest does not move, no C2 value is recorded, and the firewall opens. perf-gate-payload-identity damages one property at a time across 16 cases, each matched to the check that owns it
(c) fail-closed, before any measured interval verification completes first; nothing hashed, no git forked, no outcome probe inside a measured region perf-notary-outside counts digest computations and git invocations inside a measured interval and requires zero of each
(d) two identity domains C1/C2 covers reference + harness + manifest + payload/ratification; the session identity covers the candidate binary perf-session-drift: a candidate that changes refuses the remainder and names how many cells preceded it

Two of the 16 payload-identity cases are worth naming: one reformats the committed payload without changing its canonical hash, so body hash, C1 and ratification all still agree and only git object identity can catch it; another gives the ratification a bare basename payload_path, the exact shape the broken verifier would have accepted and a real repository never has.

CALIBRATION_ONLY validation

docs/evidence/p022-263a-calibration.linux.json, re-recorded on a clean tree at 2feb496579e1, dirty:false, harness digest 69a3a3f8b074:

cells 40 — both engines, all five rungs, process-cold and warm
outcomes 40/40 valid and timed; launcher-e2e exits 0 having run the pipeline, not 127
toolchain dotnet_sdk recorded as 8.0.425, not empty
reproduced 40/40 medians within the noise floor the run itself recorded, and no cell whose outcome changed
peak RSS os.wait4 ru_maxrss, per child, on every cell
noise not invalidated; opening and closing probes agree
decisive workloads timed none
D7 gate dormant; the report carries the gate_requirements a freeze must satisfy and an empty gate_pinned

Forty rather than forty-four because launcher-extract is withdrawn.

Как проверено

  • python tests/run_tests.py — rc 0; instrument controls 13/13
  • ruff check . — All checks passed; mypy — Success, 43 files
  • cargo fmt --all --check clean; cargo clippy --all-targets — no new findings (Rust untouched by this branch)
  • python scripts/perf_baseline.py --selftest — firewall refuses all 13 decisive workloads, admits all 6 calibration ones
  • calibration refused, with reasons, when the toolchain is absent — the defect that produced the earlier report
  • calibration run twice with the toolchain present; 40/40 cells reproduced, outcomes unchanged

Связанные issue

Refs #263 (this is #263-A only; #263-B needs the D7 freeze), #262. Closes nothing.

Чеклист

  • изменение покрыто тестом/селфтестом — thirteen instrument controls plus the harness selftest
  • README/docs обновлены — docs/notes/p022-263a-instrument.md is the methodology note
  • коммиты в conventional-commit стиле

Findings recorded rather than fixed

  1. A green gate proved what it was asked to prove. Three of round 2's four blockers were invisible to a passing CI: fixtures that exercised a layout production never has, a signature branch nothing could test, and an exit code nobody read. The controls are now where that class of thing gets caught — and the newest of them found the platform-dependent digest on its first Windows run, about an hour after being written.

  2. Git object identity is not a signature, and signed ratification is not offered. Arming costs two reviewed commits rather than one text editor, but anyone with repository write access could author both. Recorded, not implied away.

  3. /usr/bin/time is absent on the development container. The primary RSS mechanism is os.wait4 for that reason — "the tool was missing" is not a memory measurement.

  4. A GitHub-hosted Windows runner is not reliably measurement-grade — the finding IDE foundation: establish cold/warm/incremental latency and memory baselines #263-B most needs. On one commit, two Windows runs minutes apart disagreed about their own environment: the first refused it (relative IQR 1.807 against the 0.35 floor, 0.870 drift between opening and closing probes), the second accepted it and completed a full calibration that reproduced within 0.35. Same commit, same runner class, opposite validity verdicts.

    The risk is not that a hosted-Windows measurement would be noisy. It is that whether it is admissible at all turns on scheduling luck — a decisive session started on the lucky run and continued into the unlucky one would be half a measurement. The decisive Windows measurement needs a single-tenant machine, and D7 should not preregister a Windows protocol that assumes a hosted runner.

    The noise floor was deliberately not raised to make the red run green. An instrument-validity constant chosen after seeing which runs it rejects is a threshold fitted to a result, and it is the one number I am least entitled to pick.

  5. Two commits on this branch (5d6dab4, 35befb2) carry a personal committer email and will show as Unverified. Later commits are corrected. They are deliberately not rewritten: review verdicts here are bound to exact head SHAs, and silently changing a reviewed SHA is worse than the badge. Say the word if you would rather the branch were rewritten.

  6. A note on attribution. The relayed execution prompt specifies a Co-Authored-By line and a session link belonging to a different session. Commits here carry this session's own link instead, because writing another session's identifier into provenance would be false provenance in a change whose subject is provenance.

STOP

Repaired, re-calibrated on a working toolchain, and stopped. No merge, no thresholds, no decisive run, no D7 freeze, no #263-B, no Stage 3.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb

…alled

The instrument for #263's P-022 performance baselines: how a number is
produced, never what the number should be. It permits calibration and refuses
the decisive measurement, which is #263-B behind the D7 freeze. No thresholds,
no repetition count N, no engine-comparison statistic — those are D7's, and a
harness that offered any of them would be choosing them.

Preflight, against primary source. Live #263 and #262's Performance-gates
reconcile as: the brief's phase list is #262's seven gate phases plus frontend
extraction, recorded and explicitly NOT D7-gated. Both populations are frozen —
A, the G3 cutover subset, feeds D7; B, the IDE-foundation remainder, does not
unless separately ratified. What this instrument does NOT measure is written
down rather than left implied, so completing #263-A cannot narrow #263's own
acceptance by omission: the .own workloads, OwnIR serialization, no-op and
single-file-edit recheck, the latency proxy, allocation profiles and IDE
budgets are owed on B's track.

The honest limit, recorded rather than routed around. Neither engine exposes
per-phase timing through its production surface and #263-A may not add
production instrumentation, so phases are measured by a LADDER of real
production invocations, each recorded as the composed interval it actually is
with the phases inside it named. bridge-lowering and analysis are marked NOT
SEPARATELY OBSERVABLE. Derived views — parse by subtraction, core work as
end-to-end minus extraction — are labelled derived and never presented as
measurements. #263 asks for unavailable stages to be marked, not imputed.

The firewall is structural, not clerical. Exactly one function starts a clock
and it refuses a decisive workload unless the identity gate is armed, which
needs a D7 attestation that does not exist. Deliberately stricter than "no
paired runs": a single-engine timed run of a decisive workload is refused too,
because a stopwatch appears nowhere in the permitted list of enumerate / hash /
fetch / availability-check / non-timed smoke. "We did not save the delta" is
accounting-clean and epistemically worthless — whoever watched the two numbers
has already peeked.

The four carried obligations live in the harness. The identity gate exists NOW
and dormant, because D7's C1 freezes the harness digest and a gate added after
that freeze makes this a different instrument. Arming is a JSON file: a control
proves the digest does not move when the gate arms and that the firewall then
opens. Verification completes before any clock starts, and a control counts
digest calls INSIDE a measured interval and requires zero — a gate that pays
for itself out of the startup benchmark is a defect wearing a safeguard's coat.
The candidate binary is a separate domain: frozen at session start, its drift
refuses the remainder rather than measuring half the cells on another binary.

Peak RSS comes from os.wait4's per-child ru_maxrss rather than /usr/bin/time,
which is a package that can simply be absent — it is absent here, and "the tool
was missing" is not a memory measurement. Windows uses a Job Object. Where
nothing is available the value is null WITH A REASON, never a confident zero.

Two defects of my own, found while building this and worth recording. The
comparison guard first grepped the source for "ratio" and refused its own
report — the sentence explaining the prohibition matched it, and then so did
"calibration" and "iterations". It now reads the report's structure and matches
whole tokens. And an aggregate of bytes was being emitted under a median_ns
key: a unit lie in a report whose entire purpose is measurement.

Calibrated: 44 cells, both engines, all six rungs, run twice on one machine with
every cell's median reproducing within the noise floor that run itself recorded.
CI calibrates on Linux and Windows and asserts the same, plus that no decisive
workload was timed and the gate stayed dormant.

scripts/perf_baseline.py is a new wrapper that reaches the launcher, so it is
added to the Stage-2 census entry points and the job classified — the census
enumerates wrappers, which makes that list a maintenance obligation, now stated
in the ledger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
Recorded on a clean tree at f09fa00, dirty:false, with the D7 gate
dormant and no decisive workload timed.

  44 cells        both engines, all six rungs, process-cold and warm
  reproduced      44/44 medians within the noise floor the run itself recorded
  RSS             os.wait4 ru_maxrss, per child, on every cell
  noise           not invalidated; opening and closing probes agree
  decisive timed  none

The report validates the INSTRUMENT and nothing else. It is not evidence about
either engine, it cannot choose a threshold, a repetition count or a comparison
statistic, and it carries no engine comparison — the harness refuses to emit
one, checked on the way out as well as on the way in.

The Python reference commit is recorded beside the interpreter version so a
later artifact cannot silently combine comparisons bound to two different
reference states. The environment is a development container, recorded as such
in the fingerprint (runner_class: local); CI calibrates on ubuntu and windows
runners and asserts the same properties there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 32095a09-fc55-480c-840a-a9929a54d8cb


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

claude and others added 10 commits September 10, 2026 01:46
The Windows leg refused its own calibration on the first CI run: the opening
noise probe measured a relative IQR of 1.807 against the 0.35 floor, with 0.870
drift between the opening and closing probes. That is the instrument doing
exactly what §4 and §7 ask — refusing to report numbers taken on a machine that
was not holding still — and the job read it as a failure.

So CI now asks two questions instead of one. Did the HARNESS stand up and
report honestly: tagged CALIBRATION_ONLY, gate dormant, zero decisive workloads
timed, cells produced, raw per-iteration data retained, a named RSS mechanism,
complete provenance. That is the gate, on both platforms. And separately: is
this ENVIRONMENT measurement-grade — recorded per run, and allowed to be "no",
with the reasons required when it is.

The noise floor was deliberately NOT raised. An instrument-validity constant
chosen after seeing which runs it rejects is a threshold fitted to a result,
and fitting thresholds to results is the entire failure mode #263-A exists to
prevent. The number that would have made this green is the one number I am
least entitled to pick.

The finding itself matters more than the fix: a GitHub-hosted Windows runner is
not measurement-grade, so the decisive Windows measurement will need a
single-tenant machine. #263-B needed to know that before it started, and now it
does — from the instrument's first run rather than from a surprise halfway
through a decisive session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
On ONE commit, two Windows runs minutes apart disagreed about their own
environment. The first refused it — relative IQR 1.807 against the 0.35 floor,
0.870 drift between probes. The second accepted it and completed a full 44-cell
calibration that reproduced within 0.35. Same commit, same runner class,
opposite validity verdicts.

The risk is therefore not that a hosted-Windows measurement would be noisy. It
is that whether it is ADMISSIBLE turns on scheduling luck, so a decisive
session started on the lucky run and continued into the unlucky one would be
half a measurement. D7 should not preregister a Windows protocol that assumes a
hosted runner.

Both legs are otherwise green and complete: 44 cells each, 44 reproduced,
peak RSS through os.wait4 on Linux and the Job Object on Windows, gate dormant,
zero decisive workloads timed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
…t back

Two findings from review, both confirmed directly in the code.

P0 — the C1/C2 verifier read a proxy, not the thing. IdentityGate.load()
observed harness_digest, workload_manifest_sha256 and python_reference_commit,
and armed on any JSON carrying those three values. They are values anyone
holding this repository can compute in one line, so the gate proved the
instrument was the instrument and called that a freeze: it answered "is this
the harness?" when the question is "did D7 happen?". There was no check for a
C1 section, no payload commit, no git blob identity, no payload hash and no
ratification. The control was no better — it built a temp JSON out of exactly
those three fields and required the firewall to open.

The freeze is now two objects, because a payload cannot name the commit that
contains it: that sha would have to be inside the bytes hashed into it, which
is the reason detached signatures exist. A payload carries kind/schema, a C1
section and a C2 section, and hashes over its own canonical body. A separately
committed ratification names that body hash and the commit the payload is
frozen at. Arming requires all of: the discriminator, both sections complete,
the recomputed body hash, C1 matching observation, a ratification of that same
hash, the payload's working-tree bytes being the blob at the named commit, and
the ratification itself committed and unmodified. C2 is checked for presence
and never read — its values are thresholds, and an artifact from this stage
that quoted one would leak the number the module exists to keep out.

Arming still costs no source patch, so the harness digest D7 freezes does not
move when the gate arms. It now costs two reviewed commits instead of one text
editor. What it still is not is a signature: anyone with write access could
author both objects, so a declared key is verified against the ratification
commit and a declared "none" is recorded as unsigned. Both stated in the note's
limitations rather than implied away.

P1 — the ladder floor claimed to be a phase it merely contains. core-usage was
recorded as observability "direct" with the note that the interval "IS core
startup", while the invocation it times starts the process, parses argv, writes
a usage refusal and exits. It is now composed over process-startup-core plus a
named cli-argv-refusal phase, and described as a lower bound. The consequence
is corrected too: subtracting it from core-parse-refused does not leave parse,
it leaves read+parse under an assumption this instrument never measures, so the
difference is a derived bound.

Controls: perf-gate-payload-identity damages one property of a real committed
freeze at a time, thirteen cases, each required to be refused by the check that
owns it. One case reformats the committed payload without changing its
canonical hash, so only git object identity can catch it; if that case passes,
the blob check is decorative. perf-gate-arms-by-data now builds a real freeze
in a throwaway repository and additionally asserts no C2 value is recorded.
perf-phase-attribution checks the rung table against itself and against the
shipped report, which turns "re-record after changing a rung" into a gate.
perf-notary-outside now counts git invocations as well as digests: the gate
forks, and obligation (c) is violated by forking inside an interval just as
much as by hashing inside one.

The harness digest changes, so the calibration report no longer certifies the
shipped instrument and is re-recorded in the next commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
…ment

The gate repair and the startup-attribution fix both change perf_baseline.py,
so the harness digest moves from 34fa2717f24d to 5c1cdf0c69a9. The previous
report certified an instrument that no longer exists; a report whose provenance
names a tree that has been superseded is not evidence, it is a receipt.

Re-recorded on a clean tree at 5d6dab4: 44 cells, gate dormant, no decisive
workload timed, environment valid, 44/44 cells reproduced within the 0.35 noise
floor the earlier run recorded. The report now also carries gate_requirements
(the eleven properties a D7 freeze must satisfy) and gate_pinned, so the
evidence describes its own verifier instead of asking a reader to trust the
harness source.

perf-phase-attribution checks the shipped report against the shipped rung
table, so this re-recording is enforced rather than remembered: the control
failed on the stale report and passes on this one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
…ong things

CI was green on the previous head and proved exactly what it had been asked to
prove. Four blockers, found by reading rather than by running.

1. The production D7 freeze could never have armed. The verifier took the
payload's own directory as the repository, so it asked git for
`<commit>:p022-263a-d7-ratification.json` for a file that lives at
`docs/evidence/...`. `<rev>:<path>` resolves from the TREE ROOT; only a path
starting `./` or `../` is read relative to the current directory. Every control
passed because the throwaway fixtures put both objects at the repository root,
where the wrong model is accidentally right — a control proving the verifier on
a layout production never has. The root now comes from
`git rev-parse --show-toplevel`, every object path is resolved against it, and
the fixtures use the nested production layout. The ratification must also name
the exact repository-relative path this instrument reads its payload from,
rather than being free to point at any blob with the same bytes.

2. The advertised signed-ratification path was written wrong and could not have
worked. It read `git verify-commit --raw` from stdout; git writes that raw
status to stderr, and the helper discards stderr. A correctly signed commit
would have verified cryptographically and then been refused for not naming its
own key. No control caught it because no environment here holds a signing key.
Rather than fix a path whose accept direction still could not be exercised,
signed mode is withdrawn from the accepted contract: `"signature"` must be
exactly `"none"` and the freeze is recorded as unsigned. An advertised path that
is observably wrong is worse than an absent one.

3. The calibration timed failure and called it reproducible. With no .NET on
PATH the launcher rungs exited 127 before doing anything, and nothing looked at
exit codes: 12 of 44 committed cells reproducibly measured a command-not-found
path, and the CI gate did not check either, since it asked for cells, raw
timings, RSS, provenance and reproducibility but never whether a cell had done
its work. Each rung now declares the exit codes that mean it did its job and a
post-condition proving it, verified once, untimed, with output captured. A cell
that fails is not timed at all, the calibration is refused, the CI gate reads
the verdict, and reproducibility compares outcome identity as well as medians —
two runs agreeing about the wrong path is the worst possible reassurance.

The exit codes and evidence were probed against both engines before being
written down. The first version of the floor check asserted the banner contains
the word "usage"; it does not, and asserting instead of measuring is the exact
habit this contract exists to break.

4. P1 was not closed. `launcher-extract` invoked the launcher with
`--emit-facts` and was documented as "no core runs, so this isolates the
frontend stage". The launcher copies the intermediate facts and then runs Stage
2 anyway, so the interval contained the whole pipeline while recording itself as
launcher startup plus extraction — the same defect as the old `core-usage`
label, one level up, hidden locally because rc 127 never reached Stage 2.
Changing the production launcher is out of scope and a direct extractor process
is not the launcher's extraction stage, so the rung is withdrawn and
launcher-scoped extraction isolation is recorded unavailable.
`frontend-extraction` stays measured as a member of `launcher-e2e`.

Also: `perf-provenance-complete` now requires the committed report's harness and
manifest digests to EQUAL the shipped ones, not merely to be present. Presence
made a stale report tick every box.

Controls: 16 damaged freezes, each matched to the check that owns it, including
a basename `payload_path`, a ratification naming another file, and a signed
ratification. New `perf-rung-outcome` proves 127 is never success, a silent
zero-exit is refused, and the real production floor invocation is accepted.

The harness digest moves again, so the calibration is re-recorded next commit —
this time with a toolchain on PATH and every cell doing its rung's work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
…ally there

The previous report was recorded with .NET absent from PATH — installed all
along at ~/.dotnet, never on PATH, which is also why the environment fingerprint
recorded an empty dotnet_sdk and nobody looked. Twelve of its forty-four cells
were command-not-found exits, timed, summarised, and counted as reproduced.

Re-recorded at 55aad67 on a clean tree with the toolchain present:

  40 cells, every one timed and every outcome valid
  launcher-e2e now exits 0 having actually run the pipeline, not 127
  dotnet_sdk 8.0.425 recorded rather than empty
  40/40 reproduced within the 0.35 floor the run itself recorded
  no cell whose outcome changed between runs
  gate dormant, no decisive workload timed

Forty rather than forty-four because launcher-extract is withdrawn: its four
cells measured an interval that contained the whole pipeline while claiming to
isolate extraction.

Two controls now make this re-recording enforced rather than remembered.
perf-provenance-complete requires the report's harness and manifest digests to
equal the shipped ones, and perf-phase-attribution requires the report's rung
table to equal the instrument's. Both failed on the stale report and pass on
this one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
Caught by the control added in the previous commit, on the Windows CI leg: the
same tree hashed af32f04ddbad on Linux and 51ef2ca2422a on windows-latest, where
core.autocrlf rewrites source on checkout. One instrument, two identities.

That matters more than a mismatched string. D7's C1 freezes the harness digest
and #263-B has to run on both platforms, so a freeze would have armed on one and
been refused on the other — and the refusal would have looked like tampering.

This repository already knew the defect class. .gitattributes pins
docs/evidence/*.json to LF because the mutation campaigns' definition hashes hit
it first, which is exactly why the workload manifest digest matched across
platforms while the harness digest did not. An attribute only governs files git
checks out under it, though: a tree that predates the rule, a zip download or a
different local config all still differ. An identity D7 will freeze should
depend on content, so it is normalized in the digest rather than delegated to a
checkout setting.

sha256_file stays raw. The candidate binary's identity is its actual bytes, and
a hasher that normalized them would be a different kind of wrong; the control
asserts both directions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
…ed digest

The digest fix changes the harness identity a third time, from af32f04ddbad to
69a3a3f8b074 — this time to a value that is the same on both platforms, which is
the point.

Re-recorded at 2feb496 on a clean tree with the toolchain present: 40 cells,
every outcome valid and every cell timed, launcher-e2e exiting 0 having run the
pipeline, dotnet_sdk 8.0.425, 40/40 reproduced with no cell whose outcome
changed, environment not invalidated, gate dormant, no decisive workload timed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
One phase name covered two different actions. `cli-argv-refusal` bundled argv
parsing with the usage-refusal message, so it appeared on core-parse-refused,
core-full-human and core-full-sarif — none of which write a usage refusal. The
instrument's own outcome contract already distinguishes them: usage-help versus
door-refusal. The committed calibration carried the same wrong taxonomy, and
perf-phase-attribution compared the report against the rung table and agreed
they matched, which is how a schema stays perfectly self-consistent while saying
something false. Two identical tables are one claim written twice, not evidence.

The methodology note was already more accurate than the code: its table never
put a usage refusal in the successful rungs.

The vocabulary now separates three things. cli-argv-parse is in every real core
invocation. cli-usage-refusal is only the floor rung's driver banner.
ownir-door-refusal is only the strict door's version refusal. A refusal phase
may appear exactly on the rung whose outcome evidence proves that refusal
happened: cli-usage-refusal iff usage-help, ownir-door-refusal iff door-refusal,
and neither on any rung that reaches a verdict. Stated as an equivalence in both
directions, so a rung can neither claim a refusal it did not perform nor omit
one it did.

Applying the same rule the other way, launcher-e2e now also names
process-startup-core and cli-argv-parse. The launcher spawns the core, so those
actions are inside that interval and a list that means "this interval did these
things" has to include them. Over-claiming was the reported defect;
under-claiming is the same rule read in the other direction.

The rules were verified against the defect itself: re-adding cli-usage-refusal
to core-full-human makes perf-phase-attribution fail on both the equivalence and
the reaches-a-verdict rule.

Harness digest moves again, so the calibration is re-recorded next commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
… taxonomy

Splitting cli-argv-refusal into cli-argv-parse, cli-usage-refusal and
ownir-door-refusal changes perf_baseline.py, so the harness digest moves to
84b3e98fc4d9 and the previous report certifies an instrument that no longer
exists.

Re-recorded on a clean tree at 889c285: 40 cells, all outcomes valid and all
timed, 40/40 reproduced within the noise floor the run itself recorded, no cell
whose outcome changed, environment valid, gate dormant, no decisive workload
timed. Every cell's phase list now names only what its interval actually did:
core-full-human no longer claims a usage refusal it never performs, and
launcher-e2e names the core startup and argv parse that happen inside it.

perf-phase-attribution fails on a stale report by design, so this re-recording
is enforced rather than remembered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CYLNQy6tLXqsV1CbuNqsSb
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.

2 participants