Skip to content

scripts, docs: CS-RG qualification harness and protocol (CSRG-U6) - #97

Merged
novelKR merged 4 commits into
mainfrom
claude/project-thread-af6ntg
Oct 13, 2026
Merged

novelKR merged 4 commits into
mainfrom
claude/project-thread-af6ntg

Conversation

@novelKR

@novelKR novelKR commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Step 3 of the CSRG-U6 plan: the qualification harness and its protocol, frozen together before any qualification run. Nothing here runs or claims a qualification.

No pin change. No change to the installed service.

Issue

Scope: CSRG-U6 (parity, fault qualification and completion). Tracked in #76 and novelKR/DevGuard#14. This PR does not close an issue.

Item Value
Base main at 7024f74 (#96's merge)
Head 8ed8b08
Pins Codex gitlink 6b9826e3 with the recorded patch (tree 19999371), DevGuard 6e7e065; both unchanged

Summary

Harness. scripts/qualify-devguard.py, with qualify_host.py and qualify_analysis.py. It needs Python 3.9 and no third-party packages, and runs on the qualification Mac in a minimal environment.

  • candidate records the exact combination in candidate.json: the commit and its code-tree hash, the DevGuard client pin, the Codex patch check, the binaries, the installed release and whether it matches its manifest, the policy (host.toml hash, doctor, the codespace consumer without its credential digest), the DG-1 qualification record, the toolchain and the host.
  • matrix runs the claimed modes against the installed service, in-process and uds, pipe and PTY. It covers registration, exits, I/O, termination, timeout, exec failure, shortage, an unsupported minimum, mixed governed and ungoverned executions, a missing or unusable helper, status mode, an unavailable authority and a runner socket. Each governed launch is checked against DevGuard's journal.
  • faults and authority inject failures against DevGuard's fixture authority, never the installed service. The faults are surviving and escaping descendants, a stalled observer, the authority lost, restarted or stalled, a helper ending before READY, refused and missing credentials, a terminate race, shutdown, and a worker killed while preparing. authority measures process control while the authority is paused, stalled, restarted or stopped.
  • control measures the control path under DG-1's load: status p99, termination acknowledgement p99 and the scope's actual end, I/O round trips, refusal latency, and the footprint against the reservation. It runs three repetitions per runner mode, each 10 minutes idle and at least 30 minutes of load.
  • verify checks a sealed directory's manifest and recomputes every verdict from the raw files.

Protocol. docs/cs-rg-qualification.md, in English and Korean, covers the candidate, the supported-mode claims and every scenario's pass rule. It maps the work order's fault list to the cases and names the known gaps: a replay on the runner wire is not tested, and a journal refusal is not injected. It also gives the control-path targets (DevGuard's SLO protocol, not redefined), the verdict rules, accounting, regressions and evidence. The integration roadmap links it.

Legacy off path (CSRG-C09). The decision record keeps participation off as the compatibility path and default, unchanged. It gives the scope, the reason, what is duplicated, what off does not provide, and when to revisit or remove it. This is the U6 plan's recommendation (its open question 3), so merging this PR records it. Likewise, the control measurement qualifies under the cold load condition; a warm run is an additional condition when it is made (open question 4).

Comment fix. The doc comment on Participation::Required no longer says that CodeSpace cannot launch governed executions.

Found while building. On the qualification host the Codex submodule holds the recorded patch, so a plain git status reports the checkout as dirty. The harness leaves the submodule's files to the Codex check, and a submodule at another commit still counts as a change.

Fixed after the macOS check. The Mac's check of 7a0a748 found three faults in the harness, fixed in 8ed8b08:

  • A governed tty: true process killed by SIGTERM reports exited with exit code 143 (128 + 15), not 1. The pinned Codex PTY spawns a child that inherits descriptors, as a governed one does, on its descriptor-keeping path, which reports a signal death as 128 plus the signal. The matrix now expects 143, and docs/agent-integration.md says so; it said 1, which holds only for an ungoverned PTY.
  • The matrix's settled check read DevGuard's instances once, right after a mode. DevGuard retires an instance once it sees that its owner is gone, which can come after the gateway has exited, so the check now awaits both the charge and the instances for up to 60 seconds. Its stop message no longer says that work stayed charged when only an instance was still active.
  • The authority suite started the restarted phase's termination targets 5 ms after stalled, while DevGuard still kept new work closed: a host sample that goes stale while the authority is held closes admission until its samples have been clear for 30 seconds. A target refused RESOURCE_UNAVAILABLE is now tried again every second for up to 90 seconds, and each phase records the refusals and how long admission took.

A product defect found on the Mac. In uds mode, gateway shutdown kills the worker outright, so the worker cannot end its executions, and on macOS nothing gives them a parent-death signal: a pipe execution outlived the gateway until it ended by itself. The faults suite's uds shutdown case caught it. The fix changes Rust code, so it comes in its own PR after this one, and the candidate is frozen after that.

Commits:

  • 9571cd1 scripts: CS-RG qualification harness (CSRG-U6)
  • ca3670b docs: CS-RG qualification protocol and the legacy off path decision (CSRG-U6)
  • 7a0a748 policy: say that required executions are launched through the authority
  • 8ed8b08 scripts: fix the three harness findings of the macOS check (CSRG-U6)

Contract changes

None. No tool schema, error code, status value or runtime behavior changes. The harness only uses the existing MCP tools. The integration guide now gives the exit code a governed tty: true process already reports for a signal death.

Tests

scripts/tests/test_qualify_devguard.py has 37 tests, which CI runs in the python stage of the policy scan. They cover:

  • the analysis rules: nearest-rank percentiles with missing samples, and the interval, repetition and run verdicts;
  • the MCP client against a fake server, the gateway wrapper and the journal reads;
  • evidence sealing, and verify, including a falsified run;
  • the host.toml parser, which never returns a digest;
  • the candidate rules, and the dirty check with a patched submodule;
  • the retry of termination targets that DevGuard refuses for pressure, and the settled check's wait for DevGuard's instances.

Verified in the cloud (Linux).

  • On 8ed8b08, the 37 tests pass on Python 3.13 and 3.9, python3 scripts/validate-upstream.py policy python passed (102 tests), and scripts/check_docs.py passes (19 paired documents).
  • On 7a0a748, the docs site's prepare passed and clippy was clean for codespace-policy.
  • On 7a0a748, a smoke run against a real gateway built with --features devguard, in-process and uds: the client and wrapper parse every payload the suites read. That covers exec, status, read, write, resize, terminate, refusals with their attempt IDs, and the gateway log's launch lines.

CI on 8ed8b08: CI run 38384742098 passed all 17 jobs. Documentation site run 38384743435 passed. On 7a0a748, CI run 38282198652 and documentation site run 38282200167 passed.

Run on the qualification Mac (macOS 27.0, Apple M1), in a minimal environment with Rust 1.95.0: release builds of codespace-mcp and codespace-codex-runtime with --features devguard, the installed release 0.1.0-6e7e065-94d85365, and DevGuard's fixture authority and launch helper at 6e7e065. Before each suite nothing was charged, no codespace instance was active and memory pressure was normal. Afterwards nothing stayed charged, every codespace instance was retired, and the service kept its process. 8ed8b08 changes no Rust source, so its runs reused the same binaries.

Suite Head Result
candidate 7a0a748 Passed.
matrix 7a0a748 Failed on the first two harness findings above; uds was not reached.
matrix 8ed8b08 Passed: all 45 checks in both runner modes, including the runner socket. A governed PTY's death by SIGTERM reported 143 in both modes.
faults 7a0a748 26 of 27 passed. The uds shutdown case failed on the product defect above: the pipe execution's process group had not ended 15 seconds after the gateway exited.
authority 7a0a748 Incomplete: restarted was inconclusive in both modes on the third harness finding; the other four phases passed.
authority 8ed8b08 Passed: all five phases in both modes. The restarted targets were admitted after 34.6 s (in-process) and 33.7 s (uds), those of every other phase within 0.11 s.
control --rehearsal 7a0a748 Inconclusive, as a rehearsal is: idle and load passed in both runner modes, and the only reason is that rehearsal durations are below the protocol.
verify both Every sealed directory verified: recorded and recomputed verdicts agree.

faults runs again on the fix's PR. The evidence is sealed on the Mac; MANIFEST.json SHA-256 by suite:

  • 7a0a748: candidate 6b1cc6a10df9f3ca2b80472b5c0d9090a7e1a3a3517a99c1124d10b08a4d9400, matrix bf20056a7351b155f2e73388dbb22cd34d3a54721db0ed0b420842b3a2681f4a, faults e1568fcb5ccb298e2eb581e6687c0947ccf40c8dee1960bcedddb57473792c1a, authority 42f8ef5f6b20665120e059add4fd1d7fa3f4ad6f98c8a5b8588fe35f5def1290, control 2a8507da10ac571259e4a7f3437102dd24a63cf7d129d9a30492c80f30940681
  • 8ed8b08: matrix 64198825c697e333aaab14469224f297b59852067192afd4614c8e0c77302aa2, authority ffe9332ace437ff926ea2b787daf94ed58a8a691f00e23374fbefaa7797372e4

Security scenarios

  • Credentials. The harness never reads, prints or passes the consumer's credential; the gateway gets the file's path. The host.toml parser returns the consumer's settings without its digest, which is tested. Evidence holds no credential, digest or child environment.
  • Environment. Every child gets a minimal environment: HOME, PATH, TMPDIR, LANG and the CODESPACE_* settings it names.
  • The installed service. Injected faults never touch it. They run against the fixture authority in private scratch directories, so nothing can stay Suspect or charged on the owner's service, and the matrix runs nothing there that leaves a descendant.
  • Evidence. Sealed evidence is read-only and never rewritten, and verify writes nothing.

Out of scope

  • Any qualification run or claim, and the frozen candidate.
  • The fix for the uds shutdown defect, which follows in its own PR.
  • The DG-1 measurement of the installed release, done separately and recorded by docs: record release 0.1.0-6e7e065-94d85365 as DG-1's qualified release DevGuard#34.
  • Pin changes, DevGuard releases and changes to the installed service.
  • The final report, the supported-mode table in the operator guide, and the ledger update, which come in later PRs.
  • P1-RECOVERY, and governed execution on Linux before DG-LINUX.

scripts/qualify-devguard.py runs the CSRG-U6 qualification on the
macOS host: candidate records the exact combination, matrix runs the
claimed modes against the installed DevGuard service, faults and
authority inject failures against DevGuard's fixture authority, control
measures the control path under DG-1's load, and verify recomputes a
sealed run's verdicts from its raw files.

It drives codespace-mcp over MCP stdio with its own small client, reads
DevGuard's journal read-only, gives every child a minimal environment,
and never reads, prints or passes a credential: the gateway is given
the credential file's path. qualify_analysis.py holds the verdict rules
as pure functions; Python 3.9 with no third-party packages.

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

docs/cs-rg-qualification.md, in English and Korean, is the protocol the
harness implements: the candidate, the supported-mode claims, the
functional matrix and fault cases with their pass rules, how the
order's fault list is covered and where it is not, the control-path
targets, schedules and verdicts, accounting, regressions and evidence.
It also records the decision the work order requires for the existing
backends (CSRG-C09): keep participation off as the compatibility path
and default, with its scope, duplicated parts, what it does not
provide, and when to revisit or remove it.

The integration roadmap links the page from its status and from the
C09 paragraph.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The comment on Participation::Required still said CodeSpace could not
launch them yet, which stopped being true with CSRG-U4.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The qualification Mac ran the suites at 7a0a748 on 2026-10-13. Three
of its failures were the harness's:

- matrix expected a governed PTY's death by SIGTERM as exit code 1.
  A governed PTY child inherits its launch descriptors, so the pinned
  Codex PTY spawns it on its descriptor-keeping path, which reports
  128 plus the signal (143), as docs/devguard-integration.md already
  says. The harness, the protocol and docs/agent-integration.md, which
  named only 1, now say so.
- matrix's settled check read DevGuard's instances once, right after
  the last gateway exited, and failed on an instance DevGuard retired
  moments later with nothing charged. It now awaits the charge and the
  instances for up to 60 seconds, and its stop message names both.
- authority's restarted phase started its termination targets while
  DevGuard still kept new work closed after the stalled phase's stale
  host sample. A target refused RESOURCE_UNAVAILABLE is now tried again
  every second for up to 90 seconds, and the phase records the wait.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@novelKR
novelKR marked this pull request as ready for review October 13, 2026 10:58
@novelKR
novelKR merged commit 2117800 into main Oct 13, 2026
19 checks passed
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