Skip to content

feat(step7): host qualification and the execution binding, kept apart - #356

Open
PhysShell wants to merge 4 commits into
claude/p022-s8-memory-metricfrom
claude/p022-hostqual
Open

PhysShell wants to merge 4 commits into
claude/p022-s8-memory-metricfrom
claude/p022-hostqual

Conversation

@PhysShell

@PhysShell PhysShell commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Phase 2 of the S8 repair chain. Stacked on #355 (the instrument's memory-metric repair), which it needs for the closed memory vocabulary.

Three artifacts, three questions, never merged:

question
host qualification does this environment satisfy the host predicate of a named, frozen T0?
session eligibility is this session, on that qualified host, able to start now?
session admissibility did the attempt that ran remain the one that was authorised?

and, separately, the execution binding: which Linux and Windows environments, which candidates and which instrument form this campaign. hostqual.py neither issues nor owns it — a utility that checks a CPU governor must not become the root of campaign identity. D7 later binds execution_binding_sha256 rather than restating the machines.

Qualification is versioned by T0

A qualification claims "this host satisfies T0-7", so it cannot float free of T0. --qualify requires the T0 path and commit and proves: the commit exists, blob_sha really is commit:path, the bytes hash to the recorded sha256, and the document declares FROZEN. A qualification against a NOT_FROZEN T0 is refused — today's reconnaissance cannot become campaign evidence by reuse — and the binding refuses a host qualified under a different T0 than it binds.

Every consumed artifact is proved before it is read

One boundary, one validator per artifact type: parse → prove kind/schema/shape → only then read semantics. Binding, qualification, environment manifest, preflight record, provisioning and session declaration all go through it.

Artifact validity is not predicate outcome. Malformed input — wrong kind, wrong schema, the string "false" where a boolean belongs, a VM omitting a VM-only field — is refused before any record exists. A declared boolean false is not malformed: it is a perfectly good declaration that this host does not qualify, and it reaches a real artifact. The exit codes say which happened:

0  valid artifact, positive outcome
1  valid artifact, NEGATIVE outcome — the record exists and names the failed check
2  malformed input or operational misuse — no record is produced

The envcapture schema string is duplicated rather than imported (neither tool imports the instrument or the capture tool), with a control proving the copy still equals what the producer emits.

Evidence classes stay apart

Declared — provisioning and the per-session operator declaration. Content-addressed, checked by shape and value, never called machine proof: a guest OS cannot establish that no neighbour arrived on the same hypervisor. dedicated_to_p022 and no_concurrent_user_workload must be true, hosted_ci_runner false, is_vm a real boolean; a VM must promise fixed vCPU, fixed RAM, no live migration and no dynamic memory, with n/a unavailable to it, while a physical host answers those four with an explicit n/a: <reason>.

Machine-observedmanifest.provenance.ci == false (the field always exists, so demanding its absence could never be satisfied); performance governor on every applicable CPU plus an identified turbo mechanism; on Windows an accepted plan with processor state pinned at 100% on AC and DC, read by GUID and by position because every label on a non-English machine is localized; and the stratum's memory mechanism actually existing here.

Identity, candidate, quiesce

  • The qualification records a canonical hash of the whole identity block, and a session must reproduce it — a changed kernel, CPU count, RAM or toolchain can no longer walk past a matching fingerprint.
  • The candidate is hashed against the binding before the clock, and again after the session.
  • Quiesce is 120 s and is actually waited: 60 s quiet, then twelve 5 s intervals sampled from /proc/stat or GetSystemTimes, mean below 5 %, no interval above 20 %. A missing sample, a rewound counter or a zero denominator is NOT_ELIGIBLE rather than a skipped interval. A quiesce failure starts no clock, so it is not INVALID and costs no retry budget.
  • A postflight pass re-checks identity, power and candidate and requires closing-probe evidence. Preflight may not certify what a session did after it started.
  • A preflight and a postflight carrying two different execution_binding_sha256 values cannot meet in one session.

The binding proves its inputs, and --verify is total

The harness digest is recomputed from the instrument sources at the bound commit by the frozen formula, without importing the harness; because the workload manifest is one of those sources, proving the digest at a commit also proves the manifest there. --verify re-proves T0, instrument, manifest, both qualifications and both candidates — and there is no partial mode under that name: incomplete inputs are refused by the function and by the command line, so the string binding verified cannot appear over a skipped component.

Driven live, not only by fixture

The first live Windows run found a defect in this tool's own parser: it read the possible-range minimum as the current setting and reported 0% on a machine pinned at 100%. Fixed — the block ends with the two current indices, AC then DC.

Exploratory witness, not qualification evidence: on the development machine processor_min_dc = 5. Under the AC+DC rule that host does not qualify, which is the point of the rule — a machine can look perfect on its current power source and carry a different profile the moment the source changes.

Windows S606, honest declaration (this box is NOT dedicated):
  ci                     pass  manifest.provenance.ci == false
  power_policy           pass  active plan 8c5e7fda-…, processor state 100%
  required_memory_metric pass  max_process_peak_commit via win32 job object
  single_tenant          fail  the declaration is valid evidence that this host
                               does not qualify: dedicated_to_p022 is declared false
  => a qualification artifact IS written, qualified: false, exit 1
Linux (WSL):
  power_policy           fail  no cpufreq/scaling_governor on any CPU
  => artifact written, qualified: false

Both refusals are correct, and both leave evidence: neither environment is a measurement host, the tool says so for the right reason, and the attempt is recorded rather than discarded.

step 7 host qualification controls: 24 passed, 0 failed. Regression: envcapture 11/11, perf instrument 16/16, calibration freeze 7/7, training preregistration 9/9.

No real campaign qualification or binding has been created. T0 remains NOT_FROZEN; collection_authorized: false.

🤖 Generated with Claude Code

https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh

Two artifacts, because they answer two questions. Qualification: does this one
environment satisfy the T0-7 predicate. Execution binding: which Linux and
Windows environments, which candidates and which instrument form THIS campaign.
A utility that checks a CPU governor must not become the root of campaign
identity, so `hostqual.py` neither issues nor owns the binding.

`hostqual.py` splits evidence into the two classes that are not the same thing.
DECLARED provisioning — dedication, absence of other users, hypervisor
configuration — is content-addressed, shape-checked and never called machine
proof: a guest OS cannot establish what the hypervisor is doing. MACHINE-OBSERVED
is what a checker actually asserts: `manifest.provenance.ci == false` (the field
is always present, so a predicate demanding its absence could never be met),
`performance` governor on every applicable CPU plus an identified turbo
mechanism, the Windows active plan with AC processor state pinned at 100/100,
and the stratum's memory mechanism actually existing on the host.

Quiesce is implemented exactly, not approximately: twelve 5 s intervals over the
final minute, `/proc/stat` on Linux and `GetSystemTimes` on Windows, mean below
5 % and no interval above 20 %. A missing sample, a counter that went backwards
or a zero denominator is NOT_ELIGIBLE rather than a skipped interval — an
unreadable machine is not a quiet machine. A quiesce failure starts no clock, so
it is not INVALID and costs no retry budget.

A qualification is not a certificate of perpetual quiet. Session eligibility is
proved again per session, against a fresh manifest, and the record names the
execution binding, the qualification and that manifest — so a qualified host
that this campaign never bound cannot be substituted into the middle of it.

`execbinding.py` carries references and identity, never copies: duplicating the
governor or the provisioning blob would create two copies of one fact, and two
copies drift. It requires both strata, refuses an unqualified or mislabelled
host, refuses a binding whose strata share one memory metric, and refuses to
overwrite an existing binding — a rebuild before the first clock is legitimate
but never silent.

Both platform branches were driven live, not only by fixture. On Windows the
first live run found a defect in this tool's own powercfg parse: it read the
range's minimum as the current setting and reported 0 % on a machine pinned at
100 %. The block ends with the two current indices, AC then DC, and everything
before them describes the possible range. Fixed here, with the labels never
parsed — on this Russian Windows every one of them is localized. On Linux the
tool correctly refuses WSL, where no cpufreq governor exists.

step 7 host qualification controls: 10 passed, 0 failed.

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

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: a5634186-e63e-4640-a488-b824d382fab6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

The mini-review found a hole in the middle of a chain that was otherwise
content-addressed end to end: a qualification said "this host satisfies T0-7"
without naming WHICH T0. SHAs everywhere and the causation missing anyway.

Q1 — `--qualify` now requires the T0 path and commit, proves the commit exists,
that `blob_sha` really is `commit:path`, that the bytes hash to the recorded
sha256, and that the document declares FROZEN. A qualification against a
NOT_FROZEN T0 is refused, so today's reconnaissance cannot become campaign
evidence by reuse. `execbinding` refuses a host qualified under a different T0
than the campaign binds.

Q2 — `--environment-id` is gone. The id is the manifest's own observed value and
the provisioning declaration must agree with it: one identity, not three strings
that agree while everyone behaves.

Q3 — provisioning is checked by VALUE, not only by shape. `dedicated_to_p022`
and `no_concurrent_user_workload` must be true, `hosted_ci_runner` false,
`is_vm` a real boolean; a VM must promise fixed vCPU, fixed RAM, no live
migration and no dynamic memory, with `n/a` unavailable to it, and a physical
host must answer those four with an explicit `n/a: <reason>` rather than bare
booleans. A VM declaring `fixed_vcpu: false` could previously qualify.

Q4 — per-session operator facts left the host record. "No campaign workload",
"no interactive user", "no prohibited background job" are properties of a
moment, and now live in a session declaration bound by sha256 into eligibility.

Q5 — the 120 s window is waited, not asserted. The quiet minute really elapses
before the twelve 5 s intervals; a control drives an injected clock and requires
60 + 60. A constant nobody waits for is documentation.

Q6 — identity is compared as a whole. The qualification records a canonical hash
of the manifest's identity block and the session must reproduce it, so a changed
kernel, CPU count, RAM or toolchain can no longer walk past a matching
fingerprint.

Q7/Q8 — the candidate is hashed against the binding before the clock, and a new
`--session-postflight` pass re-checks identity, power and candidate afterwards
and requires closing-probe evidence. Preflight may not certify what a session
did after it started.

Q9 — power is a structured snapshot, compared by equality through the session.
Owner ruling applied: Windows requires 100% on AC *and* DC, so a machine cannot
be compliant while plugged in and change policy when the power source does.

Q10/Q11 — the binding proves its inputs instead of trusting strings. The harness
digest is recomputed from the instrument sources at the bound commit by the
frozen formula without importing the harness; the workload manifest comes from
that commit's git object; `--verify` re-proves T0, instrument, manifest, both
qualifications and both candidates.

Q12 — a preflight and a postflight carrying two different
`execution_binding_sha256` values cannot meet in one session.

Q13 — a provisioning template lands in `scripts/step7/examples/`, outside any
evidence directory, marked EXAMPLE / NOT EVIDENCE, with no real identity in it,
and a control keeps it valid under the rules it teaches.

step 7 host qualification controls: 18 passed, 0 failed.

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

Two findings from the second Phase-2 review, both reproduced before repair.

S-A — machine-produced artifacts were consumed as ordinary dicts. A document
whose kind was "NOT A BINDING AT ALL" was accepted as the execution binding, and
an arbitrary object carrying `identity` and `provenance` was accepted as an
environment manifest: the probe returned an ELIGIBLE session from two
handwritten files. The asymmetry was exactly backwards — the two DECLARED
artifacts were validated by kind and schema, and the three machine ones were
not.

There is now one boundary. `load_artifact()` parses, proves the artifact by its
own validator, and only then hands it over; no consumer reads a field before the
type is established, and a refusal raises rather than becoming "not eligible" —
an input that is not what it claims is an operator error, not a session that
failed a predicate, so it exits 2. The envcapture schema string is duplicated
rather than imported, with `hostqual-producer-schema` proving the copy still
equals what the capture tool emits.

One validator per artifact type, not five call sites: `validate_qualification`
now lives in `execbinding` — the module that binds campaigns is the lower one,
so `hostqual` reuses it without an import cycle. That immediately paid: the
required A4 attack found that binding never checked a qualification's SCHEMA,
only its kind. A wrong-schema qualification could be bound. It cannot now.

S-B — `--verify` narrowed silently to whatever it was handed. Called without the
strata it skipped the qualification and candidate checks and still printed
"binding verified". Owner ruling applied: no partial mode under that name. The
function refuses incomplete input maps — not only argparse, because the next
caller may be a script — and the command line refuses before running anything.
The string "binding verified" is now unreachable unless the full set was
attempted and passed.

P-A — the control inventory is checked by a control. The docstring list and the
executed set must be the same set, because this class of defect has now been
found twice and a third discovery wearing a new hat is not a surprise worth
paying for.

step 7 host qualification controls: 22 passed, 0 failed. Regression: envcapture
11/11, perf instrument 16/16, calibration freeze 7/7, training prereg 9/9.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
S-C, found by the review of the previous repair — and introduced by it. Closing
the artifact-boundary hole, I folded value judgements into the shape validator,
so `dedicated_to_p022: false` was refused exactly like a forged document. The
honest case that used to produce a record with `qualified: false` and
`single_tenant: fail` produced nothing at all: one line on stderr and no
artifact.

That erases negative attempts, which is how a laboratory ends up with machines
that pass on the first try because the other tries were never artifacts.

The boundary now separates the two questions it had merged:

    artifact validity   is this the artifact it claims to be — kind, schema,
                        types, applicability shape. Malformed is refused before
                        any record exists.
    predicate outcome   do the declared values satisfy the predicate. Every
                        failure reaches a real record.

So the string "false" is malformed, a missing key is malformed, `is_vm` answered
with "n/a" is malformed, a VM omitting a VM-only field is malformed, and a
physical host answering those with bare booleans is malformed — while the
boolean `false`, anywhere it is allowed, is a valid declaration that this host
does not qualify.

Exit codes now say which class occurred, and are written down rather than
implied:

    0  valid artifact, positive outcome
    1  valid artifact, NEGATIVE outcome — the record exists and says why
    2  malformed input or operational misuse — no record is produced

The same split applies to the session declaration: an operator who truthfully
records that a prohibited job is running gets `eligible: false` with the reason,
not an error message and no evidence.

Three controls added, driven through the command line so the exit codes are
part of the proof: an honest provisioning negative, an honest VM negative and an
honest session negative each leave an artifact naming the failed check, while
`"false"` as a string leaves none and exits 2.

step 7 host qualification controls: 24 passed, 0 failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018xhcg5opoFbSTdYHpkSXCh
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