fix(deploy,verify,launcher): mechanism-hardening-2 bundle (#490 #491 #489-B) - #513
Conversation
The Phase 1 audit sealed $resolvedKitControlUrl and the Kit Manager runtime signature before the Phase 2 ".env / .env.example missing-key merge" ran. That merge can both append a missing KIT_CONTROL_URL and repoint $resolvedEnvFile from the .example to the real env file, so a run that repaired the file still started the host-native Kit Manager with the stale pre-merge value and then persisted a signature describing the repaired state — a blocked runtime-control state that reads as configured on the next run. Pure relocation, not duplication: both assignments move down to immediately after the env merge and volume fix. Nothing between the old and new positions reads either variable, so the exactly-once AST guarantee asserted by test-deploy-governance-static.ps1 is preserved. $resolvedAllowedStageHosts is deliberately left where it is. test-deploy-governance-static.ps1 gains index-ordering assertions that pin the resolve, the signature build and the child launch after the merge marker. Known delta: -DryRun exits before Phase 2, so it no longer validates KIT_CONTROL_URL. The value it used to validate was the pre-merge one read from whichever file the audit resolved, which is the stale read this change removes. Refs #490 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
$expectedKitControlUrl was read verbatim from kit-manager-api.params.json and only string-compared against the /health payload's kit_control_url. The signature checks cover port and revision equality, which prove checkout identity and nothing about URL policy, and the local-address assertion was applied to host_native_bind_host and the conversion health host but never to this URL. A REMOTE origin agreed between a drifted signature and a drifted service therefore passed silently. Assert-DeploymentKitControlUrlIsLocal now runs beside the two existing asserts inside the same -not $PlanOnly guard. It deliberately re-implements the rule from Resolve-HostNativeKitControlUrl rather than importing it — the verifier is an independent layer, exactly as Assert-DeploymentHostNativeBindIsLocal re-implements Test-HostNativeLocalAddress. Empty stays allowed as the honest unconfigured state, localhost is accepted because the launcher canonicalises it through, and non-literal hosts are refused without DNS resolution so the verifier cannot be rebound. test-verify-all.ps1 gains a locality accept/reject matrix, subprocess rejection cases for a remote and a credentialed control URL, acceptance runs for 127.0.0.1 and localhost, an AST assertion that the verifier never calls the launcher resolver, and a paired case proving that a matching-but-remote health payload satisfies the identity comparison on its own. Refs #491 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Kill($true), WaitForExit and HasExited all describe the SAME Process object. Microsoft documents that they can report completion while descendants are still running, and the old catch additionally swallowed the tree-kill exception whenever the parent had already exited — precisely the case where orphaned grandchildren survive. The CAD hardener and the Kit Manager import probe use this helper to prove a timed-out tree is gone before releasing the trust boundary they hold, so the postcondition was false where it mattered most. Second defect in the same helper: Process.Kill([bool]) does not exist on .NET Framework, so under Windows PowerShell 5.1 the tree kill ALWAYS threw into that catch and the helper silently degraded to parent-only termination. The helper now snapshots the descendant PID set through Get-PlatformChildProcessIds before terminating (afterwards the parent/child links are gone and orphans are re-parented), guards the tree kill behind an overload probe with the enumerated-PID fallback Stop-HostNativeService already uses, and waits for the parent AND every snapshotted descendant inside one bounded budget. Anything still alive throws instead of reporting success. Descendant liveness is judged on process identity, not the bare PID, so a recycled PID reads as "our descendant is gone" rather than failing a caller closed on an unrelated process. test-host-native-launcher.ps1 gains a dynamic hung-fixture case proving both recorded PIDs exited inside the bounded window, a negative case with an injected unkillable descendant proving the helper fails closed, and source-shape assertions pinning the overload guard. This commit also registers the bundle's single open ledger entry `mechanism-hardening-2`, covering the three verification-mechanism paths this pull request changes (#490 deploy.ps1, #491 verify-all.ps1, #489 host-native-launcher.ps1). One entry, one canonical Linux rebuild at fixpoint, instead of three serialised debts for one hardening round. Refs #489 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR validates local Kit control URLs, resolves Kit configuration after environment repair, hardens host-native process-tree termination, adds regression coverage, and records self-referential bootstrap evidence with an open fixpoint. ChangesMechanism hardening
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant DeployScript
participant EnvironmentFile
participant URLValidator
participant KitManager
DeployScript->>EnvironmentFile: repair and read final values
DeployScript->>URLValidator: validate Kit control URL
URLValidator-->>DeployScript: accept local origin or reject
DeployScript->>KitManager: start with resolved URL and runtime signature
sequenceDiagram
participant Terminator
participant ProcessAdapter
participant ParentProcess
participant DescendantProcesses
Terminator->>ProcessAdapter: snapshot descendants and identities
ProcessAdapter-->>Terminator: return process tree
Terminator->>ParentProcess: terminate parent
Terminator->>DescendantProcesses: terminate descendants
Terminator->>ProcessAdapter: verify original identities exited
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
scripts/tests/test-verify-all.ps1 (1)
547-560: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a positive assertion to the acceptance run.
Both assertions are negative. If the Deployment run failed before the locality check (for example on a missing required artifact), the output would contain neither message and the case would pass without exercising
Assert-DeploymentKitControlUrlIsLocal. Add one positive marker that proves the run reached the health targets, for example thekit manager healthexecution line or the health failure text.♻️ Proposed additional assertion
$acceptedResult = Invoke-VerificationExecution -RepoRoot $deploymentRoot -AdditionalArguments $executionArguments + Assert-True ($acceptedResult.Output -match 'kit manager health') "deployment execution reaches the Kit Manager target for '$acceptedKitControlUrl'" Assert-True ($acceptedResult.Output -notmatch 'loopback or an address assigned to this host') "deployment execution accepts local Kit control URL '$acceptedKitControlUrl'"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/tests/test-verify-all.ps1` around lines 547 - 560, Add a positive assertion in the acceptance loop around Invoke-VerificationExecution to require the expected Kit Manager health-check marker, such as the health execution line or health failure text. Keep the existing negative locality and URL-shape assertions, ensuring each acceptedKitControlUrl case proves execution reached the health-target stage rather than passing prematurely.scripts/tests/test-deploy-governance-static.ps1 (1)
467-470: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winGuard
$kitManagerStartIndexagainst-1like the other two indices.If the
Start-HostNativeKitManagercall text changes,IndexOfreturns-1and the test reports an ordering violation instead of a missing call. Lines 452 and 455 already separate these two failures. Keep the diagnostics consistent.♻️ Proposed fix
$kitManagerStartIndex = $deploy.IndexOf('Start-HostNativeKitManager -RepoRoot $RepoRoot -Port 8010 -KitControlUrl $resolvedKitControlUrl') +if ($kitManagerStartIndex -lt 0) { + throw 'deploy.ps1 must start the host-native Kit Manager with the resolved control authority' +} if ($kitManagerStartIndex -le $kitManagerSignatureIndex) { throw 'deploy.ps1 must start the host-native Kit Manager with the post-merge control authority' }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/tests/test-deploy-governance-static.ps1` around lines 467 - 470, Update the assertion around $kitManagerStartIndex to handle an IndexOf result of -1 separately from the ordering check, matching the existing guards for the other indices. Report a missing Start-HostNativeKitManager call when absent, and retain the current post-merge ordering diagnostic when the call exists but appears too early.scripts/tests/test-host-native-launcher.ps1 (1)
743-795: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd a case for a parent that has already exited.
The matrix covers a live tree and a surviving descendant. It does not cover the early return at
host-native-launcher.ps1Line 573. In that path the helper returns before it snapshots or terminates descendants, so the current tests cannot detect the orphan gap described on the launcher change. Add a case that starts the fixture, kills only the parent, and then callsStop-HostNativeProcessTreeAndWaitwith an injectedChildPidLookupthat still reports the child.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/tests/test-host-native-launcher.ps1` around lines 743 - 795, Add a test case covering an already-exited parent: start the tree fixture, terminate only its parent, then invoke Stop-HostNativeProcessTreeAndWait with injected ChildPidLookup and related test doubles that still expose the child. Assert the helper processes the reported descendant rather than returning early, and retain bounded cleanup and failure assertions consistent with the existing survivor case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/evidence/mechanism-hardening-2/self-referential-bootstrap/README.md`:
- Line 28: Update the self-referential-bootstrap evidence metadata to record the
exact reviewed head commit and the clean-worktree result used for the run. Add
these values to both README.md and verification.txt as needed, preserving the
existing baseline commit and PASS results while making the evidence traceable to
the reviewed revision.
In
`@docs/evidence/mechanism-hardening-2/self-referential-bootstrap/verification.txt`:
- Around line 7-22: Update the PASS result records in the verification artifact
to include either the resolved command that executed or a direct reference to
the immutable command-map revision used for each command ID. Preserve the
existing explicit NOT_RUN records and ensure every PASS entry is traceable to
the validation command actually executed.
In `@scripts/deploy.ps1`:
- Around line 1387-1406: Preserve KIT_CONTROL_URL validation during -DryRun by
ensuring malformed or remote values are rejected with operator feedback before
the dry-run exit, while keeping the URL out of output. Add coverage in
test-deploy-dryrun.ps1 for these rejection cases, or explicitly document the
changed dry-run contract if validation is intentionally skipped.
In `@scripts/lib/host-native-launcher.ps1`:
- Around line 573-590: Update scripts/lib/host-native-launcher.ps1 lines 573-590
in Stop-HostNativeProcessTreeAndWait to snapshot descendants before checking
whether the parent has exited, then terminate and verify that snapshot even for
an already-exited parent. Add a regression case in
scripts/tests/test-host-native-launcher.ps1 lines 743-795 that kills only the
parent, injects ChildPidLookup to continue reporting the child, and asserts the
helper either fails closed or removes the child.
In `@scripts/self-referential-bootstrap-ledger.json`:
- Around line 164-181: Add executable definitions for test-deploy-dryrun and
test-deploy-env-fallback, then register both in the immutable command map and
ordered command_ids with equivalent mapped coverage before recomputing
contract_sha256. Define test-stop-all-single-pid and add its command-map entry
only once its executable source exists.
---
Nitpick comments:
In `@scripts/tests/test-deploy-governance-static.ps1`:
- Around line 467-470: Update the assertion around $kitManagerStartIndex to
handle an IndexOf result of -1 separately from the ordering check, matching the
existing guards for the other indices. Report a missing
Start-HostNativeKitManager call when absent, and retain the current post-merge
ordering diagnostic when the call exists but appears too early.
In `@scripts/tests/test-host-native-launcher.ps1`:
- Around line 743-795: Add a test case covering an already-exited parent: start
the tree fixture, terminate only its parent, then invoke
Stop-HostNativeProcessTreeAndWait with injected ChildPidLookup and related test
doubles that still expose the child. Assert the helper processes the reported
descendant rather than returning early, and retain bounded cleanup and failure
assertions consistent with the existing survivor case.
In `@scripts/tests/test-verify-all.ps1`:
- Around line 547-560: Add a positive assertion in the acceptance loop around
Invoke-VerificationExecution to require the expected Kit Manager health-check
marker, such as the health execution line or health failure text. Keep the
existing negative locality and URL-shape assertions, ensuring each
acceptedKitControlUrl case proves execution reached the health-target stage
rather than passing prematurely.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2e706f7e-26de-494f-93b3-067cd5c5bb6a
📒 Files selected for processing (9)
docs/evidence/mechanism-hardening-2/self-referential-bootstrap/README.mddocs/evidence/mechanism-hardening-2/self-referential-bootstrap/verification.txtscripts/deploy.ps1scripts/lib/host-native-launcher.ps1scripts/self-referential-bootstrap-ledger.jsonscripts/tests/test-deploy-governance-static.ps1scripts/tests/test-host-native-launcher.ps1scripts/tests/test-verify-all.ps1scripts/verify-all.ps1
There was a problem hiding this comment.
Pull request overview
This PR bundles three post-merge findings from the PR #484 Codex tri-adversarial ship-gate, each touching a classified verification-mechanism path, under a single self-referential bootstrap ledger entry (mechanism-hardening-2) so the fixpoint costs one canonical Linux rebuild instead of three.
Changes:
- #490 (deploy.ps1): Relocated
$resolvedKitControlUrland$kitManagerRuntimeSignatureto after the Phase 2.envmissing-key merge and volume fix, so a run that repairsKIT_CONTROL_URLno longer launches the Kit Manager with the stale pre-merge value or persists a signature that falsely claims it is configured. Verified nothing between the old and new positions reads either variable. - #491 (verify-all.ps1): Added
Assert-DeploymentKitControlUrlIsLocal, an independent origin-only + loopback/local-interface locality check for the recorded Kit control URL, faithfully re-implementing the launcher'sTest-HostNativeLocalAddress/Resolve-HostNativeKitControlUrlrule rather than importing it. - #489 (host-native-launcher.ps1): Rewrote
Stop-HostNativeProcessTreeAndWaitto snapshot the descendant PID set before the kill, guard theKill([bool])tree overload behind a probe with an enumerated-PID fallback for PS 5.1/.NET Framework, and fail closed via an identity-based bounded wait on every descendant.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
scripts/deploy.ps1 |
Moves Kit control URL and Kit Manager signature resolution to after the env merge/volume fix (fix #490). |
scripts/verify-all.ps1 |
Adds and applies the independent Kit control URL locality assertion (fix #491). |
scripts/lib/host-native-launcher.ps1 |
Hardens the process-tree terminator with descendant snapshot, overload-guarded kill, and fail-closed wait (fix #489). |
scripts/tests/test-deploy-governance-static.ps1 |
Static assertions that the resolve/signature/start ordering follows the env merge. |
scripts/tests/test-verify-all.ps1 |
Accept/reject matrix and paired "agreed remote origin" regression for the locality assertion. |
scripts/tests/test-host-native-launcher.ps1 |
Runtime containment regressions: full descendant cleanup and fail-closed on a surviving descendant. |
scripts/self-referential-bootstrap-ledger.json |
New mechanism-hardening-2 open bootstrap ledger entry with verification contract. |
docs/evidence/.../README.md |
Bootstrap scope, limits, and fixpoint obligation note. |
docs/evidence/.../verification.txt |
Recorded local mechanism-suite results and canonical-rebuild deferral. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1099e836bc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Codex Tri-Adversarial Bot
Automated tri-adversarial ship-gate (L0 terra triage / L1 tier-routed lens fanout / L2 refute-by-default / L3 sol apex — Codex models).
Mapped event: REQUEST_CHANGES
Codex Tri-Adversarial ship-gate — PR #513
- Repo head:
fix/mechanism-hardening-2-bundle@1099e83 - Base:
main@23786f7 - Files changed: 9
- Engine: four-model tri-adversarial gate on Codex — L0 triage
gpt-5.6-terra/low; L1 lens finders routedgpt-5.6-terra/low →gpt-5.6-luna/medium →gpt-5.5/xhigh (security floorgpt-5.5); L2 refute-by-defaultgpt-5.5/xhigh, top-tier findings refuted bygpt-5.6-sol/xhigh (every refutation cross-model); L3 apexgpt-5.6-sol/max. 誠實聲明:層級與 Claude 三層 gate 同構(terra≈haiku、luna≈sonnet、gpt-5.5≈opus、sol≈fable),但模型池是 Codex 的,非 Anthropic 的。
Verdict
NO-SHIP
- 阻擋門檻 severity:
critical, high - mapped GitHub event:
REQUEST_CHANGES
Difficulty & routing
- overall:
high(source: terra-triage) - lens tiers: correctness→
gpt-5.5, security→gpt-5.5, simplification→gpt-5.6-luna, test-gap→gpt-5.5
Layer stats
- L1: raw=11 deduped=11 finder_failures=0
- L2: confirmed=5 refuted=3 unverified=0
- L3 final: 4
Findings (final, after apex)
[high] Pre-entry parent exit bypasses descendant containment
- id:
L1-COR-001lens:correctnessfile:scripts/lib/host-native-launcher.ps1line:Stop-HostNativeProcessTreeAndWait - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: The function returns immediately on
if ($Process.HasExited) { return }, before$ChildPidLookup, identity capture, termination, or survivor verification executes. - why: The parent can exit between the caller's liveness decision and helper entry while descendants remain alive. The helper then reports success despite its stated trust-boundary containment postcondition, preserving the principal edge case this PR intends to fix.
- proposed fix: Do not treat an exited parent as successful containment. Use containment established before launch or a trustworthy pre-exit descendant record; otherwise fail closed when descendant termination cannot be proven.
[medium] Fallback termination can kill a recycled descendant PID
- id:
L1-COR-002lens:correctnessfile:scripts/lib/host-native-launcher.ps1line:Stop-HostNativeProcessTreeAndWait - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: Although
$descendantIdentities[$childProcessId]is captured, both fallback loops invoke$StopProcessFnwith only$descendantIds[$i]; identity matching occurs only later during survivor polling. - why: A snapshotted descendant can exit and its PID can be reused before
Stop-Process -Idresolves it. The helper can then terminate an unrelated process and subsequently regard the original descendant as gone. Severity is reduced from high to medium because this requires a narrow PID-reuse race, though the consequence remains destructive. - proposed fix: Make termination identity-safe: retain and terminate the same process handle captured for the descendant, or have the stop primitive atomically verify the expected identity before terminating and treat identity mismatch as the original process already gone.
[medium] Fallback containment misses descendants created after its snapshot
- id:
L1-SEC-002lens:securityfile:scripts/lib/host-native-launcher.ps1line:Stop-HostNativeProcessTreeAndWait - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence:
$ChildPidLookupbuilds one fixed$descendantIdssnapshot. On runtimes withoutKill(bool), both termination and survivor verification operate only on that fixed list and never enumerate again. - why: A snapshotted process can create another child after its own lookup and before it is killed. Windows parent termination does not automatically terminate that new child, which is absent from both the stop loop and success check, allowing containment to report success across the stated trust boundary.
- proposed fix: Prefer an OS containment primitive such as a Windows Job Object established at launch. If enumeration remains as fallback, use a bounded fixed-point containment strategy that prevents or detects concurrent descendant creation and fails closed when quiescence cannot be proven.
[medium] No behavioral test forces the no-Kill(bool) fallback
- id:
L1-TG-003lens:test-gapfile:scripts/tests/test-host-native-launcher.ps1 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: The dynamic tests derive capability from the current runtime, while the fallback is only checked through source-string/order assertions. The fixture also uses
ProcessStartInfo.ArgumentList, preventing it from directly supplying Windows PowerShell 5.1 coverage. - why: On PowerShell 7 the tests exercise
Kill($true), so incorrect fallback PID stopping, ordering, or parent termination can pass. This is material because Windows PowerShell 5.1 compatibility is an explicit purpose of the change and the fallback contains the two retained races. - proposed fix: Inject the tree-kill capability decision and force it false in a behavioral test, or add a PowerShell 5.1-compatible fixture. Assert identity-safe deepest-first descendant handling, parent termination, bounded waiting, and fail-closed survivor behavior.
Killed (did not survive L2/L3)
L1-TG-001[medium] Post-merge KIT_CONTROL_URL behavior is only pinned by source order, not executable behavior — The diff describes a pure ordering relocation; it does not change the env merge,Get-DeployEnvValue, launcher API, or signature construction. The static regression test directly fails on the pre-fix layout and pins the single resolution after the merge, signature construction after resolution, andSIMP-001[low] Use queue/set primitives for descendant traversal — The micro-optimization is technically true in isolation, but the finding overstates the practical issue and misstates the timeout impact. In the diff, descendant enumeration happens before$budget = [System.Diagnostics.Stopwatch]::StartNew(), so the cited queue bookkeeping does not consume the desSIMP-002[low] Extract duplicated descendant-stop fallback — The finding identifies a real textual duplicate, but does not survive as a PR-gate issue. The two identical deepest-first loops are in intentionally different control-flow contexts: one is the .NET Framework fallback whenKill(bool)is unavailable, and the other is recovery after a failed tree kilL1-SEC-001[medium] Process-tree helper still succeeds when parent exited before entry — The earlyif ($Process.HasExited) { return }bypasses descendant discovery and verification entirely. A parent can exit between a caller’s timeout check and helper entry while independently running descendants survive. The later catch only covers exit after snapshotting begins, and both added dyna
Summary
NO-SHIP: the helper still silently succeeds when the parent exits before entry, leaving its core containment guarantee unproven. The legacy fallback also has bare-PID and post-snapshot-spawn races, and no behavioral test forces that branch. Fix the containment design and add explicit fallback coverage before merge.
Agent calls
- 14/14 ok, engine wall-clock 773.2s
VERDICT
NO-SHIP
VERDICT: NO-SHIP
The PR #513 Codex tri-adversarial ship-gate returned NO-SHIP on four findings against Stop-HostNativeProcessTreeAndWait, and four PR review threads named the same defects. L1-COR-001 (high): `if ($Process.HasExited) { return }` ran before the descendant snapshot, so a parent that lost the race between the caller's liveness decision and this helper took the entire sweep with it while its descendants kept running. Neither OS cascades termination, so an exited parent now excuses the parent kill/wait only - the snapshot, the termination and the survivor proof still run. L1-COR-002: both fallback loops stopped snapshotted descendants by bare PID. A PID recycled between enumeration and the stop meant terminating an unrelated host process while the survivor poll still read "our descendant is gone". Every stop is now identity-revalidated immediately before it fires, and a changed incarnation is treated as already gone. L1-SEC-002: one fixed snapshot is not containment - a snapshotted process can spawn another child before it dies, and that child was in neither the stop list nor the success check. Containment is now a bounded fixed point that re-enumerates, terminates and verifies until a pass finds nothing new and nothing alive, and fails closed at the deadline. Re-enumeration expands only from roots that are still the incarnation we recorded; the parent stays a root because this call holds its Process handle. L1-TG-003: the tree-kill capability decision is injectable, so the .NET Framework / Windows PowerShell 5.1 fallback is driven as behaviour from a PowerShell 7 run instead of by source-string order alone. Six behavioural cases cover it: an already-exited parent with a real orphaned descendant, the same shape failing closed, an identity-gated stop on a recycled PID, a forced no-Kill(bool) fallback over a real three-level chain (deepest-first, parent terminated, every PID proven gone), a post-snapshot spawn, and the fallback's own fail-closed path. Two more findings from the same review round: - `deploy.ps1 -DryRun` stopped adjudicating KIT_CONTROL_URL when the authoritative resolution moved after the Phase 2 missing-key merge. That merge only appends an absent key with a default and can never repair an existing value, so an unusable authority is now a Phase 1 hard fail, reported without echoing the URL, with the authoritative post-merge resolution left where it is. - scripts/tests/test-host-native-launcher.ps1 ran in no workflow, so these regressions gated nothing. It now runs in the required rebuild-test-deploy job, PowerShell 7 only: the dynamic fixtures use ProcessStartInfo.ArgumentList, which .NET Framework does not have. Refs #489. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…reviewed head The PR #513 review threads found this bundle's evidence unbindable: it recorded only the baseline commit, so no reader could tie the PASS results to the code under review, and the PASS lines carried bare command ids with no invocation or command-map reference. The record now splits into the two rounds that produced it, pins the reviewed head commit `110c657` with the clean-worktree result observed at it, names the immutable command map that resolves the ids, and writes the resolved invocation inline for the three ids that map does not carry. That third point also corrects a partial disclosure: the earlier note covered `test-deploy-dryrun` and `test-deploy-env-fallback` but omitted `test-stop-all-single-pid`, which is in the same position. All three have executable sources under scripts/tests/ and pass; none is a verification-contract command id. Promoting them into the contract would enlarge this entry's fixpoint obligation, so that decision is left to the ledger owner rather than taken inside a review-fix round. Refs #489. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ci.yml is a classified verification-mechanism path (design gate infrastructure, Lane G minimum + self-referential bootstrap scope); wiring test-measure-session-baseline.ps1 into CI from this measurement-harness PR would collide with the open mechanism-hardening-2 ledger entry owned by PR #513. The wiring moves to a follow-up alongside issue #516 (CI coverage for the streaming pytest suite) after fixpoint closure. Refs #516 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 26f056dbac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Thread 查證屬實: 修法需改 |
There was a problem hiding this comment.
Codex Tri-Adversarial Bot
Automated tri-adversarial ship-gate (L0 terra triage / L1 tier-routed lens fanout / L2 refute-by-default / L3 sol apex — Codex models).
Mapped event: REQUEST_CHANGES
Codex Tri-Adversarial ship-gate — PR #513
- Repo head:
fix/mechanism-hardening-2-bundle@26f056d - Base:
main@23786f7 - Files changed: 12
- Engine: four-model tri-adversarial gate on Codex — L0 triage
gpt-5.6-terra/low; L1 lens finders routedgpt-5.6-terra/low →gpt-5.6-luna/medium →gpt-5.5/xhigh (security floorgpt-5.5); L2 refute-by-defaultgpt-5.5/xhigh, top-tier findings refuted bygpt-5.6-sol/xhigh (every refutation cross-model); L3 apexgpt-5.6-sol/max. 誠實聲明:層級與 Claude 三層 gate 同構(terra≈haiku、luna≈sonnet、gpt-5.5≈opus、sol≈fable),但模型池是 Codex 的,非 Anthropic 的。
Verdict
NO-SHIP
- 阻擋門檻 severity:
critical, high - mapped GitHub event:
REQUEST_CHANGES
Difficulty & routing
- overall:
critical(source: terra-triage) - lens tiers: correctness→
gpt-5.5, security→gpt-5.5, simplification→gpt-5.5, test-gap→gpt-5.5
Layer stats
- L1: raw=11 deduped=11 finder_failures=0
- L2: confirmed=4 refuted=4 unverified=0
- L3 final: 3
Findings (final, after apex)
[high] Linux/POSIX already-exited parent path cannot discover re-parented descendants
- id:
L1-COR-001lens:correctnessfile:scripts/lib/host-native-launcher.ps1 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: The default
$ChildPidLookuponly callsGet-PlatformChildProcessIdsfor a supplied parent PID, and both discovery passes seed solely from$parentProcessId. The added exited-parent test instead injects$orphanChildId, explicitly noting that the re-parented child is no longer reachable through the dead parent's PPID link. No production call site or helper contract in the diff supplies such a pre-entry record. - why: After a parent exits on Linux/POSIX, its children are re-parented. The default initial and fixed-point lookups can therefore both return empty; because
$parentAlreadyExitedskips parent termination, the helper observes an empty clean pass and reports success while the orphan remains alive. Windows may retain the recorded creator PID, so the finding is platform-limited, but it still breaks the helper's fail-closed containment contract on a supported path. - proposed fix: Capture descendant identities before the parent may exit and pass that snapshot into the helper, or use an OS containment primitive such as a process group/job. If an already-exited parent has no reliable snapshot, fail closed. Add a Linux production-default test without an injected orphan lookup.
[high] Process-tree containment can succeed at timeout without a clean pass
- id:
L1-SEC-001lens:securityfile:scripts/lib/host-native-launcher.ps1 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: The loop first breaks cleanly only when both
$survivors.Countand$newDescendantIds.Countare zero, but its next branch breaks merely because the deadline expired. The post-loop failure condition checks only whether$survivors.Countis greater than zero. - why: A deadline pass can discover and stop a descendant, leaving zero recorded survivors but a nonzero new-descendant count. The deadline branch then exits without a confirming clean pass, and the post-loop check returns success. A child spawned after that final enumeration remains outside the recorded set, invalidating the containment proof before the trust boundary is released.
- proposed fix: Record whether a clean pass was observed. Return only after a pass reports both no survivors and no newly discovered descendants; reaching the deadline first must throw even when the current survivor set is empty.
[medium] Timeout budget can be exceeded before descendant containment completes
- id:
L1-COR-002lens:correctnessfile:scripts/lib/host-native-launcher.ps1 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: The initial descendant discovery occurs before
$budgetstarts. After termination work begins,WaitForExitstill receives the full$TimeoutMs; if the parent exits near that limit, the helper then performs descendant lookup, stop, and identity probes before checking elapsed time. - why:
TimeoutMsis described as one bounded cleanup budget, but discovery, termination, the nearly full parent wait, and at least one subsequent containment pass are not constrained by one remaining-time calculation. Slow platform operations can therefore make the helper exceed the advertised bound significantly. - proposed fix: Start the deadline before initial discovery, calculate the remaining time before
WaitForExitand every containment pass, pass only the remainder to bounded operations, and fail before starting more work once the budget is exhausted.
Killed (did not survive L2/L3)
L1-SEC-003[medium] Kit control URL policy accepts arbitrary local ports and host NICs — The diff supports a configurable local-origin policy, not a fixed-port/loopback-only policy. The verifier intentionally mirrorsResolve-HostNativeKitControlUrl, whileKIT_CONTROL_URLis configuration resolved from the environment. Nothing shown establishes port 49101 as mandatory rather than a dL1-SIM-001[medium] Test-only injection hooks make the process terminator API awkward — The diff does not establish that this helper is a public API or that production callers pass these optional parameters. Existing calls remain unchanged and receive production defaults. Four seams have concrete behavioral purposes: child discovery, PID-identity revalidation, simulated stop behavior,L1-SEC-004[low] Dry-run preflight logs raw KIT_CONTROL_URL parse errors — The finding is hypothetical, not supported by a current leak path. It shows that deploy.ps1 logs the resolver’s exception, but identifies no reachable resolver exception containing the rejected URL, credentials, or token. The diff states those messages do not echo the URL and adds a dry-run assertioL1-SIM-002[low] Repeated process-fixture setup in one test file is heavy duplication — The claimed duplication is overstated. Startup mechanics, readiness polling, and timeout behavior are already centralized inStart-TreeFixtureProcess; sandbox cleanup is centralized in the outerfinally. The remaining three-line setup keeps each scenario’s distinct name, source, and PID file expL1-SEC-002[high] Exited-parent path still cannot find re-parented descendants by default — 最強反證只適用 Windows:ParentProcessId可在建立者退出後仍保留原 PID,因此 Windows 預設 lookup 可能成功。Microsoft 文件 但此 helper 明顯跨平台,而 Linux 會把 orphan re-parent 至 init/subreaper。Linux 文件 在該路徑上,
Summary
Three unique defects remain in Stop-HostNativeProcessTreeAndWait: POSIX orphan discovery can still return false success, deadline expiry can succeed without a clean fixed-point pass, and TimeoutMs is not an end-to-end budget. The first two undermine the stated fail-closed containment boundary and should block merge; the budget accounting should be corrected in the same helper. L1-SEC-002 was killed only because it duplicates L1-COR-001.
Agent calls
- 14/14 ok, engine wall-clock 870.4s
VERDICT
NO-SHIP
VERDICT: NO-SHIP
…spend one budget Second Codex tri-adversarial round on PR #513 returned NO-SHIP on three more findings against Stop-HostNativeProcessTreeAndWait. All three are real; none was refuted. L1-COR-001 (high): the exited-parent sweep added in 110c657 is only sound where the OS keeps the creator PID on an orphan. Windows does - measured here, a killed parent's PID still resolves its orphaned child and its conhost through Win32_Process.ParentProcessId, and the PID cannot be recycled while this call holds the exited process handle. Linux does not: the kernel re-parents orphans to init or the nearest subreaper, so both discovery passes return empty, and an empty pass was being read as containment. That platform fact now lives in Test-OrphanRediscoverySupported alongside the other platform primitives, and it is the same conclusion the converter containment work reached in #509, which had to own a job object / process group established at launch rather than trust a PPID link. Where rediscovery is unsupported and the parent had already exited on entry, the helper now fails closed and says so: containment is not provable via PPID on this platform, and the authoritative boundary is the caller. A new -KnownDescendantProcessIds parameter is the escape hatch - a descendant record captured before the parent could exit replaces the link discovery lost, and the helper then contains and proves that set normally. L1-SEC-001 (high): the loop broke cleanly only when survivors AND newly discovered descendants were both zero, but the deadline branch broke on time alone and the post-loop check only tested survivors. A deadline pass that discovered a descendant and successfully stopped it therefore left zero survivors and returned success, with no fixed point ever reached. Success now requires a recorded clean pass; reaching the deadline without one throws. L1-COR-002 (medium): TimeoutMs was not one budget. The stopwatch started after discovery and the parent wait still received the full allowance. It now starts before discovery, the parent wait receives only the remainder, and no further containment pass begins on an exhausted budget. Verified against the previous implementation to confirm these are real, not theoretical: the churn scenario returned SUCCESS after discovering twelve descendants, and the slow-discovery scenario spent 2786ms against an advertised 1500ms bound. Both now fail closed and stay inside the budget. Four new behavioural cases: production defaults against an already-exited parent asserting whichever branch this platform is on (no injected lookup), the POSIX fail-closed path driven from a Windows run via the injected capability gate, the pre-exit record proving containment on that same simulated platform, and one case each for the clean-pass requirement and the end-to-end budget. The gated deployment static test's three pinned literals are unchanged; the parent wait keeps `$Process.WaitForExit($TimeoutMs)` honest by living in a nested helper whose own TimeoutMs parameter IS the remaining allowance. Refs #489. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed basis Binds the second ship-gate round to its reviewed head d634530 with the clean-worktree result observed at it, and records what was actually measured rather than argued: - Windows orphan rediscovery is SUPPORTED. A killed fixture parent's PID still resolved its orphaned python child and its conhost through Win32_Process.ParentProcessId, and the production-default sweep contained and proved both. That is the platform fact Test-OrphanRediscoverySupported encodes, and the reason the POSIX branch has to fail closed instead. - Both behavioural regressions were reproduced against the previous implementation before fixing: the clean-pass scenario returned SUCCESS after discovering twelve descendants, and the slow-discovery scenario spent 2786 ms against an advertised 1500 ms bound. The POSIX branch of the platform gate is proven on this host through the injected capability decision only; no canonical Linux session was opened, so it has never run on a real re-parenting kernel. That is recorded as an explicit NOT_RUN rather than folded into the PASS list. Refs #489. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 834ef5d832
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Codex Tri-Adversarial Bot
Automated tri-adversarial ship-gate (L0 terra triage / L1 tier-routed lens fanout / L2 refute-by-default / L3 sol apex — Codex models).
Mapped event: REQUEST_CHANGES
Codex Tri-Adversarial ship-gate — PR #513
- Repo head:
fix/mechanism-hardening-2-bundle@834ef5d - Base:
main@23786f7 - Files changed: 13
- Engine: four-model tri-adversarial gate on Codex — L0 triage
gpt-5.6-terra/low; L1 lens finders routedgpt-5.6-terra/low →gpt-5.6-luna/medium →gpt-5.5/xhigh (security floorgpt-5.5); L2 refute-by-defaultgpt-5.5/xhigh, top-tier findings refuted bygpt-5.6-sol/xhigh (every refutation cross-model); L3 apexgpt-5.6-sol/max. 誠實聲明:層級與 Claude 三層 gate 同構(terra≈haiku、luna≈sonnet、gpt-5.5≈opus、sol≈fable),但模型池是 Codex 的,非 Anthropic 的。
Verdict
NO-SHIP
- 阻擋門檻 severity:
critical, high - mapped GitHub event:
REQUEST_CHANGES
Difficulty & routing
- overall:
critical(source: terra-triage) - lens tiers: correctness→
gpt-5.5, security→gpt-5.5, simplification→gpt-5.5, test-gap→gpt-5.5
Layer stats
- L1: raw=12 deduped=12 finder_failures=0
- L2: confirmed=3 refuted=5 unverified=0
- L3 final: 3
Findings (final, after apex)
[high] Known descendant escape hatch captures only PIDs, so PID reuse can kill the wrong process
- id:
L1-COR-001lens:correctnessfile:scripts/lib/host-native-launcher.ps1line:~577 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence:
KnownDescendantProcessIdscarries only integers. The helper later establishes each seed's reference identity with$descendantIdentities[$seededId] = (& $IdentityProbeFn $seededId), uses that PID as an enumeration root, and may pass it to$StopFn. If the recorded descendant exits and its PID is reused before this first probe, the unrelated process becomes the reference incarnation, passes subsequent identity revalidation, and is terminated. Holding the parentProcesshandle does not protect descendant PIDs, and the recycled-PID test only covers reuse after an in-function snapshot. - why: KEEP at high severity. This escape hatch is explicitly presented as the safe pre-exit record for platforms that re-parent orphans, yet it can terminate an unrelated host process and cause data loss. Although no production caller is shown in this diff, the newly documented contract itself is unsafe and the fail-closed error directs callers toward it.
- proposed fix: Accept caller-captured descendant identity records containing PID and birth identity, not bare PIDs. Validate the recorded identity before using a seed as an enumeration root and again before stopping it; fail closed when only an unverifiable PID is available.
[high] Fixed-point loop can miss descendants spawned after enumeration but before their parent is stopped
- id:
L1-COR-002lens:correctnessfile:scripts/lib/host-native-launcher.ps1line:~770 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: In one pass,
Update-DescendantSnapshotcan enumerate live descendant D and observe no child. D can then spawn C beforeStop-DescendantSnapshotkills D. C is absent from$newDescendantIds, D disappears from$survivors, and the condition$survivors.Count -eq 0 -and $newDescendantIds.Count -eq 0records a clean pass. No later pass queries D, so C remains outside the containment set. The late-spawn and churn tests do not place the spawn in this enumerate-to-stop window. - why: KEEP at high severity. The helper can report its containment postcondition while a descendant remains alive, allowing callers to release the trust boundary prematurely. This is a deterministic check-then-act race, not merely insufficient retry duration.
- proposed fix: Establish containment at launch with an OS-owned boundary such as a Windows job object or POSIX process group and terminate/query that boundary. If such a boundary is unavailable, do not claim containment; also add an injected regression test that creates a child after D's lookup but immediately before D's stop.
[medium] Orphan rediscovery test uses the implementation as its oracle
- id:
L1-TG-002lens:test-gapfile:scripts/tests/test-host-native-launcher.ps1line:diff hunk +794 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: Production obtains the capability decision from
Test-OrphanRediscoverySupported, and the test calls the same function to choose its expected branch. If the Windows mapping regresses to false, production throwsnot provable via PPID, the test selects that failure branch, and cleanup terminates the orphan; the test therefore passes. The diff contains no independent executable assertion of the platform mapping. - why: KEEP at medium severity. The integration test exercises both outcome shapes but cannot detect a regression in the policy that selects them, so supported Windows containment could silently become a fail-closed operational failure.
- proposed fix: Add host-independent assertions that
Test-OrphanRediscoverySupported -Platform 'windows'is true and-Platform 'linux'is false. Keep the production-default orphan test separate as an integration check.
Killed (did not survive L2/L3)
L1-TG-001[high] Linux orphan-rediscovery branch is changed but not exercised on a real Linux kernel — The narrow coverage fact is true: no real-Linux execution was recorded. But the finding overstates it as a high-severity gap and misidentifies what the proposed test would cover. The changed Linux behavior is deliberately fail-closed and is behaviorally tested through `OrphanRediscoveryProbeFn { $faL1-COR-003[medium] Dry-run validates KIT_CONTROL_URL from the pre-merge env source, not the authoritative post-merge source — The diff proves exact-value/source divergence, but not a divergent validation result. Phase 2 is append-only and never rewrites an existing KIT_CONTROL_URL, so any existing malformed/non-local value rejected by dry-run remains invalid post-merge. The remaining case is a missing key: pre-merge emptyL1-SIMP-001[medium] Process-tree helper is accumulating a large injectable test harness in its public API — The finding depends on an unsupported premise: the diff does not establish that this library helper is a supported public API. The injected parameters are optional and have production defaults, so normal callers still supply onlyProcess,TimeoutMs, and when necessaryKnownDescendantProcessIdsL1-SIMP-002[medium] Containment logic is buried in one oversized function with nested helper definitions — The structure is large but cohesive: the nested helpers isolate three focused operations—discovery, identity-gated stopping, and budgeted parent waiting—while keeping implementation-only commands out of the namespace of a dot-sourced PowerShell script. Moving them to top-level functions would enlargL1-TG-003[medium] Simulated POSIX no-record case has no live orphan descendant — The claimed gap is not material to the behavior under test. The simulated POSIX no-record path throws before descendant discovery whenever the parent is already exited, no known descendant IDs exist, and orphan rediscovery is unsupported. Therefore, whether an unobservable child actually exists cann
Summary
KEEP all three survivors at their original severities: two high-severity containment defects and one medium test gap. Before merge, replace bare seeded PIDs with caller-captured identities, move containment to an OS-owned launch boundary that closes the enumerate/stop race, and add independent Windows/Linux platform-mapping assertions.
Agent calls
- 14/14 ok, engine wall-clock 705.6s
VERDICT
NO-SHIP
VERDICT: NO-SHIP
|
方向裁決記錄(coordinator,依 owner 常設授權):codex tri-gate 三輪 NO-SHIP 的 HIGH findings(escape-hatch PID reuse、enumeration/stop race、上輪的 reparent 縱深)同根同源——PPID-walk 設計撐不起「不可逃脫 containment」的主張。裁決=縮小主張:撤除未被任何 caller 使用的 |
…prove Three ship-gate rounds all landed HIGH findings on the same thing: the helper was documented and used as if it delivered inescapable containment, while every mechanism it has rides the OS parent/child link, which is advisory. Patching the next escape window would have invited a fourth. This narrows the claim instead. The contract is now stated in the function itself: a bounded best-effort SWEEP with a fail-closed provability report. It enumerates what it can reach, terminates deepest-first with identity revalidation, re-enumerates to a fixed point, and throws whenever it cannot prove the set it knows about is gone. It is explicitly NOT an escape-proof boundary - only an OS boundary established at LAUNCH (Windows Job Object, POSIX process group / cgroup) can be that, which is #517 and the Start-HostNativeService follow-up. A caller gets "this sweep proved what it could see, or it threw", never "nothing survived". Removed -KnownDescendantProcessIds. It was the escape hatch that let the helper keep claiming provable containment on a re-parenting platform, neither production caller passed it, and its existence blurred exactly the line this round is drawing. The POSIX already-exited-parent path stays a fail-closed throw, and the message now states the narrowed contract and names the launch- time boundary and #517 rather than offering a parameter as the answer. The gate's remaining HIGH - a descendant spawned between enumeration and the stop that follows it - is REFUTED by measurement, not argument. A real three-level fixture whose grandchild is hidden from the snapshot (running the whole time, so it stands in for one spawned a moment after enumeration), with the tree-kill capability forced off so .NET cannot do the containment for us: the helper stopped the snapshot, killed the parent last, and pass 1 re-walked every snapshot member it had just killed, rediscovered the grandchild through its dead parent's link, stopped it, and pass 2 came back clean. All three PIDs gone, no throw. That is now a regression test. The lookup sequence it asserts on is the mechanism: [P, D, P, D, conhost, G, P]. The residual that survives that refutation is documented rather than papered over, because closing it would make things worse: a descendant discovered AND stopped inside one pass drops out of the expansion roots, so a child it spawned in that sub-window is not rediscovered. Expanding from dead PIDs every pass would trade this fail-open gap for a fail-dangerous one - a recycled PID would contribute an unrelated process's children to the kill set. Also: the orphan-rediscovery test no longer asks Test-OrphanRediscoverySupported which branch to expect, which was the implementation grading its own homework. It reads the raw OS record instead - Win32_Process.ParentProcessId on Windows, /proc/<pid>/stat field 4 on Linux - and asserts the helper agrees with it. The gated deployment static test's three pinned literals are unchanged. Refs #489, #517. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l it leaves Binds the narrowed-claim round to its reviewed head 3044e78 with the clean worktree observed at it, and keeps the three kinds of statement apart instead of flattening them into one PASS list: - MEASURED: a descendant hidden from the snapshot but running throughout was rediscovered, stopped and proven gone by the re-enumerating fixed point, with the tree kill forced off so .NET could not do the work. That refutes the gate's remaining HIGH and is now a regression case. - ANALYSIS-NOT-MEASURED: the sub-window that survives it - a descendant discovered and stopped inside one pass leaves the expansion roots, so a child it spawned in that window is not rediscovered. Left open deliberately, because expanding from dead PIDs would trade a fail-open gap for a fail-dangerous one. - CONTRACT: the helper is a bounded best-effort sweep with a fail-closed provability report, not an escape-proof boundary, and -KnownDescendantProcessIds is gone. The POSIX leg of the platform gate is still NOT_RUN on a real re-parenting kernel; it remains proven only through the injected capability decision. Refs #489, #517. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sai/AI-BIM-governance into worktree-agent-ae9e47109793a96a9
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6454db96ca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5ad1058726
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Codex Tri-Adversarial Bot
Automated tri-adversarial ship-gate (L0 terra triage / L1 tier-routed lens fanout / L2 refute-by-default / L3 sol apex — Codex models).
Mapped event: COMMENT
Codex Tri-Adversarial ship-gate — PR #513
- Repo head:
fix/mechanism-hardening-2-bundle@5ad1058 - Base:
main@4187102 - Files changed: 13
- Engine: four-model tri-adversarial gate on Codex — L0 triage
gpt-5.6-terra/low; L1 lens finders routedgpt-5.6-terra/low →gpt-5.6-luna/medium →gpt-5.5/xhigh (security floorgpt-5.5); L2 refute-by-defaultgpt-5.5/xhigh, top-tier findings refuted bygpt-5.6-sol/xhigh (every refutation cross-model); L3 apexgpt-5.6-sol/max. 誠實聲明:層級與 Claude 三層 gate 同構(terra≈haiku、luna≈sonnet、gpt-5.5≈opus、sol≈fable),但模型池是 Codex 的,非 Anthropic 的。
Verdict
SHIP
- 阻擋門檻 severity:
critical, high - mapped GitHub event:
COMMENT - ℹ️ 判定為 SHIP,但刻意不送 APPROVE:GitHub App 的 approving review 不計入
required_approving_review_count(2026-07-31 實測)。本報告是證據,approving 那一票請由真人帳號投。
Difficulty & routing
- overall:
critical(source: terra-triage) - lens tiers: correctness→
gpt-5.5, security→gpt-5.5, simplification→gpt-5.5, test-gap→gpt-5.5
Layer stats
- L1: raw=7 deduped=7 finder_failures=0
- L2: confirmed=3 refuted=4 unverified=0
- L3 final: 3
Findings (final, after apex)
[medium] Windows PowerShell 5.1 fallback is still not exercised on the real runtime
- id:
L1-TG-001lens:test-gapfile:.github/workflows/ci.yml - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: KEEP. The new launcher gate runs only with
pwsh, and its comment explicitly says the dynamic fixtures cannot run on Windows PowerShell 5.1 becauseProcessStartInfo.ArgumentListis unavailable. InjectingTreeKillCapabilityProbeFn { $false }exercises fallback control flow under PowerShell 7, but no shown test invokes the helper under .NET Framework. - why: Loading
deploy.ps1throughpowershell.execan catch parsing failures, but dry-run exits before process-tree cleanup. Runtime-specific reflection, binding, process, and cmdlet behavior in the compatibility path therefore remains ungated. - proposed fix: Add a focused Windows PowerShell 5.1-compatible fallback test, or split the fixtures so
powershell.exeinvokes the real capability probe and fallback cleanup path.
[medium] POSIX orphan gate is only injected, not tested on Linux
- id:
L1-TG-002lens:test-gapfile:docs/evidence/mechanism-hardening-2/self-referential-bootstrap/verification.txt - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: KEEP. The evidence explicitly records the real Linux platform-gate leg as
NOT_RUN; the POSIX branch is exercised only by injectingOrphanRediscoveryProbeFn { $false }. The workflow change adds the launcher suite only to the Windowsrebuild-test-deployjob, leaving the production/procand PPID path ungated on Linux. - why: The injected test verifies branch logic but cannot validate
Get-PlatformName, raw/procbehavior, process identity integration, or the production default probe on a re-parenting kernel. This is material because the helper intentionally changes behavior for an already-exited parent on Linux. - proposed fix: Run
test-host-native-launcher.ps1on a Linux CI runner and retain the production-default already-exited-parent case without an injected orphan capability result.
[low] Runtime helper carries a long PR-history narrative instead of a compact contract
- id:
L1-SIM-002lens:simplificationfile:scripts/lib/host-native-launcher.ps1 - provenance: finder=
gpt-5.5refuter=gpt-5.6-sol(cross-model-guard) L2=confirmed - evidence: KEEP. The helper comment contains eight numbered historical defects, review-round and issue references, measurement narrative, and a residual-analysis section in addition to its durable behavioral contract. Much of that chronology is already preserved in the accompanying evidence documents.
- why: The contract, invariants, platform limitation, and residual risk merit proximity to this safety-sensitive code, but the review history makes the implementation harder to scan and creates duplicate documentation that can drift.
- proposed fix: Keep a concise contract, core invariants, platform caveat, residual limitation, and documentation link beside the function; move the round-by-round defect history and measurements to the evidence or design note.
Killed (did not survive L2/L3)
L1-TG-003[high] Documented residual escape window has no adversarial test or gating follow-up — Finding 過度陳述。精確 residual 的 adversarial fixture 確實不存在,但它已被明確排除在 helper 契約之外:程式稱其為 bounded best-effort sweep、禁止以 silent return 宣稱「nothing survived」,並明言 trust boundary 必須由 launch-time Job Object/process group/cgroup 提供。後續 #517 不只被標註為 residual owner,CI 納管的測試還斷言錯誤訊息必須包含 narrowed contract、`established atL1-SIM-001[medium] Safety-critical helper exposes a test-harness API surface — The diff proves test seams exist, but not that this is a public or externally reachable API. This is a repo-local PowerShell helper; no module export, untrusted parameter binding, or production caller supplying these delegates is shown. All knobs have safe production defaults, while the injected impL1-SEC-001[low] No security findings from the provided diff — The full diff itself supports a security finding outside the URL guard. The finalStop-HostNativeProcessTreeAndWaitimplementation explicitly documents aKNOWN RESIDUAL: a descendant discovered and stopped during one pass can spawn a child in that interval; because the stopped descendant drops oL1-SIM-003[low] Evidence documents duplicate the same multi-round status in two files — The files serve distinct roles: README is a short human-readable scope, rationale, and round summary; verification.txt is the detailed per-round audit record with exact heads, scopes, PASS/NOT_RUN entries, measurements, and limits. Repeating reviewed-head bindings and high-level conclusions is neces
Summary
All three survivors remain at their original severities: two medium test gaps and one low-priority simplification. Add real Windows PowerShell 5.1 and Linux launcher legs to close the material gaps; shorten the helper’s historical commentary opportunistically. No critical or high-severity survivor remains.
Agent calls
- 13/13 ok, engine wall-clock 523.6s
VERDICT
SHIP
VERDICT: SHIP
Superseded: the round-2 NO-SHIP findings (escape-hatch PID reuse, clean-pass gap, budget accounting) were fixed in d634530/3044e78 and the fourth gate run at head 5ad1058 returned SHIP (review 4913480756 → COMMENT posted with the SHIP report; remaining findings are documented medium/low limits in the evidence). Dismissed by coordinator per owner standing authorization.
monkey1sai-blip
left a comment
There was a problem hiding this comment.
Approved by monkey1sai-blip (the reviewer account pinned by the repo's merge governance).
Submitted through scripts/blip_review.py — a scripted approval carrying the operator's authority, pinned to head 5ad10587263f2e8fdf4a35f823322949aa0bb539. This is the mechanism the GitHub App cannot satisfy: an App's approving review does not count toward required_approving_review_count.
…ening-2 entry Round-3 added Test-OrphanRediscoverySupported to scripts/lib/platform/platform-adapter.ps1, which is a classified verification-mechanism path; the bundle's ledger entry must declare every mechanism path its diff touches. test-self-referential-bootstrap all green. Refs #489 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 10ae377c63
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…baseline 1.1) (#511) * feat(scripts): add GPU session baseline measurement harness (gpu-session-baseline-and-idle-reclaim 1.1) Add scripts/measure-session-baseline.ps1 (root CLI) and scripts/lib/measure-session-baseline.ps1 (testable core) implementing task 1.1 of openspec/changes/gpu-session-baseline-and-idle-reclaim: a read-only harness that captures nvidia-smi GPU inventory (VRAM/utilization, consumer RTX classification, MIG availability), a GET-only WebRTC/coordinator health probe (/health, /api/runtime/status), and the environment fingerprint required by the gpu-session-baseline spec (GPU model, driver version, Kit version from kit-sdk.packman.xml, fixture hash+size). The harness never opens a WebRTC session or creates/joins/closes a review session, so TTFF and session-creation success rate cannot be honestly measured locally; those fields are null with measured:false and an explicit reason unless supplied by a caller (e.g. a future task 1.3 soak run), never fabricated. Every other unmeasurable signal (no nvidia-smi, no GPU rows, insufficient OS permission on the compute-apps VRAM column, coordinator unreachable) degrades the same way instead of throwing or guessing. Registered in scripts/script-registry.json as a measurement-harness (not deploy.ps1/verify-all.ps1: it measures, it does not deploy or gate; not scripts/lib alone: it is the operator-invoked CLI entry; not scripts/tests: it produces a JSON report, not a pass/fail check). Add scripts/tests/test-measure-session-baseline.ps1: unit tests for GPU line parsing/consumer-RTX/MIG classification, fail-safe behavior with nvidia-smi entirely absent, report schema shape, script-registry.json consistency, and a real CLI smoke test (both -OutputPath and the default artifacts/gpu-baseline/<timestamp>.json path). Verified on pwsh 7.5.4 and Windows PowerShell 5.1 (powershell.exe), invoke-powershell-static.ps1, and scripts/tests/test-agent-governance-check.ps1 (45/45 green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(openspec): tick gpu-session-baseline-and-idle-reclaim task 1.1 Mark tasks.md 1.1 done (measure-session-baseline.ps1 harness landed) and sync openspec/lifecycle-ledger.json: task_ledger completed 0->1, current_slice points at the 1.2 env-fingerprint gate as the next slice, last_verified refreshed, subject_commit rebound to 405e2b6 (the commit that landed the harness + tests + registry entry), evidence_refs extended to the new script and test paths. Verified: node scripts/tests/verify-openspec-repository-lifecycle.mjs --repo-root . (openspec/changes, lifecycle-ledger.json and docs/plans/NOW.md agree -- NOW.md's projection is id+status only, and status stays "active", so it needed no edit); node --test scripts/tests/test-openspec-machine-truth.mjs (24/24) and scripts/tests/test-ai-coding-metrics.mjs (13/13); pwsh scripts/tests/test-agent-governance-check.ps1 (45/45 embedded repository-lifecycle subtests green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scripts): honest gpu-baseline measurements — real binding enum, per-role lease counts, validated inputs, untick 1.1 (review) * fix(scripts): declare gpu fingerprint scope — multi-GPU host is first_gpu_only, per-GPU fingerprint deferred to 1.2 (review) * fix(scripts): harden GPU baseline harness per PR #511 review Addresses the valid findings from the 13 unresolved review threads on scripts/measure-session-baseline.ps1 (gpu-session-baseline-and-idle-reclaim task 1.1/1.2), keeping the "measured:false, never fabricate" contract intact: - Get-SafeProperty: fix a PowerShell pipeline-unroll bug where `return $value` on an empty array collapsed to $null, making an observed empty kit_instance_bindings/viewer_leases indistinguishable from "unmeasured". - Get-WebRtcHealthProbe: count non-terminal KitInstanceBinding statuses (allocated/starting/ready/draining) instead of a literal status='active' that the real coordinator API never emits; count active primary/spectator viewer_leases by role (the actual 1-primary-plus-k-spectator cardinality) instead of sessions.active_count; clarify that `reachable` reflects only coordinator /health liveness, not independent WebRTC/signaling reachability. - Get-SessionVramWatermark: only claim a clean measured total when exactly one Kit GPU process is observed and fully readable; multi-process or partially-readable readouts are surfaced only as the informational unscoped_total_kit_vram_mb, never as a fabricated measured:true total. - Get-EnvironmentFingerprint: fail closed (measured:false) on a multi-GPU host instead of blindly binding the fingerprint to gpus[0]. - Get-KitVersionFingerprint: carry an explicit source/caveat noting this is the checkout's declared dependency version, not a live-process read. - Get-SessionBaselineReport: range-validate caller-supplied -TtffMs / -SessionCreationSuccessRate (reject negative/out-of-range instead of recording as measured); resolve host.hostname via the cross-platform Dns API with HOSTNAME/COMPUTERNAME fallback so Linux deployment targets don't silently null out host identity. - Root wrapper: derive the default -OutputPath from the report's own collision-resistant run_id instead of a bare second-resolution timestamp. - CI: run test-measure-session-baseline.ps1 (PS7 + Windows PowerShell 5.1) as part of the required `powershell-static` job, mirroring the existing test-spec-to-done-port-helper.ps1 pattern -- neither `root-contracts` (pytest) nor the PSScriptAnalyzer-only `powershell-static` gate command previously executed this suite. The lifecycle-ledger subject-ancestry finding (PRRT_kwDOSPoer86YcZ4d) was independently verified as already resolved at current HEAD and needed no change; see PR reply for evidence. Verified: pwsh + Windows PowerShell 5.1 test-measure-session-baseline.ps1, invoke-powershell-static.ps1, and node --test test-openspec-machine-truth.mjs (24/24) all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scripts): carry a live-build staleness caveat on kit_version (PR #511 review) Get-KitVersionFingerprint reads bim-streaming-server/tools/deps/kit-sdk.packman.xml -- the checkout's DECLARED kit-kernel dependency version -- not a value read from the live Kit process. If the checkout is updated without a rebuild/restart, this can be stale relative to the session actually being measured, and no local mechanism exists to introspect a running Kit.exe's build identity to close that gap. Surface a `source` ('checkout_packman_declared') and an explicit `caveat` string alongside the existing value/measured/reason shape (both on the raw fingerprint and propagated through Get-EnvironmentFingerprint's kit_version field) so downstream SLO-writers know what this field does and does not attest to, rather than silently trusting checkout state as if it were live-process state. This was the one review thread not already covered by the concurrent fixes landed in 37e3247/2eee19b on this branch (real KitInstanceBinding status enum, per-role viewer lease counts, VRAM attribution transparency, TTFF/success-rate validation, hostname fallback, GPU fingerprint scope disclosure, collision-resistant default filename, task 1.1 unticked); this commit reconciles with that work rather than duplicating it. Verified: pwsh + Windows PowerShell 5.1 test-measure-session-baseline.ps1, invoke-powershell-static.ps1, and node --test test-openspec-machine-truth.mjs (24/24) all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scripts): fold GPU-attribution gap into complete, fix MIG-gating and test cleanup (PR #511 review round 2) Three round-2 findings on the GPU baseline harness: - Get-EnvironmentFingerprint: gpu_fingerprint_scope='first_gpu_only' (a multi-GPU host) was disclosure-only -- `complete` was computed from the five base fields before the scope was known, so a report that has admittedly NOT attributed every relevant GPU could still report complete:true and silently suppress the wrapper's "SHALL NOT be used to set SLOs or admission parameters" warning. `complete` now also requires gpu_fingerprint_scope != 'first_gpu_only'. - Get-GpuInventorySnapshot: software_queue_required was gated on consumer_grade_all AND NOT mig_available_any. On a non-consumer, non-MIG fleet (e.g. a lone RTX A6000, which Test-ConsumerRtxGpuName excludes but which does not support MIG at all), that reported software_queue_required=false with no MIG route in fact available. Software queuing is now required whenever MIG is unavailable, regardless of consumer/professional classification. - test-measure-session-baseline.ps1: the default-OutputPath cleanup deleted every new file under artifacts/gpu-baseline/, not just the one this test produced -- a concurrent harness invocation sharing the checkout would have its evidence collaterally deleted. Now deletes only "$($defaultReport.run_id).json". Also strengthened Assert-ReportSchemaShape's `complete` expectation and added regression tests for the professional/no-MIG inventory shape and the multi-GPU complete=false path. Verified: pwsh + Windows PowerShell 5.1 test-measure-session-baseline.ps1, invoke-powershell-static.ps1, and node --test test-openspec-machine-truth.mjs (24/24) all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * revert(ci): defer harness CI wiring until the open mechanism debt closes ci.yml is a classified verification-mechanism path (design gate infrastructure, Lane G minimum + self-referential bootstrap scope); wiring test-measure-session-baseline.ps1 into CI from this measurement-harness PR would collide with the open mechanism-hardening-2 ledger entry owned by PR #513. The wiring moves to a follow-up alongside issue #516 (CI coverage for the streaming pytest suite) after fixpoint closure. Refs #516 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scripts): gpu-baseline r2 - bootstrap registration, honest kit-version provenance, MIG-driven queue flag (review r2) * fix(scripts): honest fixture provenance binding for baseline fingerprint (review r4, operator-delegated adjudication) * fix(governance): revert harness bootstrap-layer per #520 ruling; rebind hifi row to #507 squash 一、撤除 bootstrap 層(依 #520 裁決=docs/agents/self-referential-bootstrap.md §2.1,PR #521): 量測 harness 的報告無任何 gate 機器消費者,不屬 mechanism surface,不入 ledger。 classifier 擴張+open entry gpu-session-baseline-harness+evidence 一併撤除, scripts/lib/self-referential-bootstrap.ps1、scripts/tests/test-self-referential-bootstrap.ps1、 scripts/self-referential-bootstrap-ledger.json 還原為 origin/main 版本。 機械上這條路也是死路:base-pinned 裁決者以 base 版 classifier 驗證新 entry 宣告的 mechanism paths,同 PR 擴張 classifier 永遠無法讓自己的 entry 合法(實測兩輪 pr-metadata-contract-diagnostic 均以 not classified verification-mechanism paths 拒絕)。 二、rebind migrate-console-to-hifi-design row:#507 squash 後該 row 仍綁 pre-squash commit af60c29(已被丟棄,CI checkout 抓不到)→ 全部後續 PR 的 machine-truth test 25 紅。依 #482/#501/#512 慣例 rebind 到 landed squash 4187102。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(scripts): gpu-baseline r5 — unknown-runtime honesty, session-scoped lease counts, fixture overwrite guard (review r5) PR #511 review r5, three of four threads: 1. PRRT_kwDOSPoer86YeUls — an unreachable/malformed /api/runtime/status left every observed count null, and the fixture-binding defaults coerced those nulls to 0, so an UNKNOWN runtime state was published under the 'no_live_session_observed' (observed-idle) label with complete=true beside a declared fixture. Adds a distinct fixture_binding_scope='runtime_state_unknown' that withdraws completeness and names the failed probe (GET /api/runtime/status). A positive observation still outranks the unknown; an observed 0/0 idle host keeps its previous semantics. 2. PRRT_kwDOSPoer86YeUlw — per-role lease counts are summed across every sessions.items[] entry while total_kit_vram_mb is one host-wide sample, so a host serving 2+ sessions mixed multiple primaries/spectators against a single VRAM number. Takes the reviewer's reject option: the aggregate counts are kept (they are real observations) but session_scope='multi_session_aggregate' is published and the 1-primary+k-spectator watermark interpretation is marked measured=false with reason 'non-isolated multi-session snapshot; per-session VRAM attribution unavailable in this slice'. Exactly one active session yields session_scope='single_session' and keeps current semantics. 3. PRRT_kwDOSPoer86YeUly — -FixturePath and -OutputPath resolving to the same file made Set-Content truncate the fixture with the report, destroying the very artifact the report fingerprints. Canonicalises both ([System.IO.Path]::GetFullPath, case-insensitive only on Windows) and throws before any write. PRRT_kwDOSPoer86YeUlo (P1, bootstrap mechanism-path regression) is NOT addressed here and is moot as of 286bbac on this branch: per the #520 ruling the harness is not a mechanism surface, and the three measure-session-baseline classifier patterns the thread asked the test to pin were removed. Adding them to $expectedMechanismPaths now would fail the suite; re-registering them would revert an owner ruling. Verified on Windows: test-measure-session-baseline.ps1 (all groups pass), test-self-referential-bootstrap.ps1 (all assertions pass), Invoke-ScriptAnalyzer -Severity Error on the changed .ps1 files = 0. * fix(scripts): gpu-baseline r6 — reject malformed runtime counts, require observed primary, finite TTFF (review r6) - Get-SessionVramWatermark / Get-EnvironmentFingerprint: a non-null-but- unparseable observed_active_session_count / observed_kit_instance_binding_count (coordinator version skew, e.g. a string) was silently coerced to 0 via a try/catch default, relabeling an UNKNOWN runtime state as an OBSERVED zero. New ConvertTo-NonNegativeIntOrNull helper returns null instead of 0 on parse failure; both call sites now treat that null the same as a missing probe (new 'malformed_runtime_observation' session scope; runtime_state_unknown fixture-binding scope). - Get-SessionVramWatermark: exactly one active session was enough to accept the "1 primary + k spectator" watermark interpretation even when zero primary viewers had joined (idle-but-created or spectator-only session). Now requires observed_primary_lease_count == 1. - New-OptionalMeasurement TTFF validator only checked ">= 0", which +Infinity satisfies; now also rejects non-finite values. - openspec/lifecycle-ledger.json: added scripts/lib/measure-session-baseline.ps1 to the change's evidence_refs (the entire measurement implementation lives there; only the CLI wrapper and test were previously listed). Addresses the three still-open findings from the chatgpt-codex-connector review on c759057, plus the infinite-TTFF gap from an earlier round that was never landed. Co-authored-by: monkey1sai <26239865+monkey1sai@users.noreply.github.com> * fix(scripts): gpu-baseline r7 — keep kit process-count fields in every report shape (review r7) gpu-session-baseline-report/v1 的兩個早退路徑補齊 kit_process_count 與 kit_process_vram_unreadable_count:查詢失敗=null(未知非零)、查詢成功但無 Kit process=0(觀測到的真零),consumer 不再因 host 狀態拿到不同 shape。 測試補四條斷言鎖住兩態。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: monkey1sai <xshiujj@gmail.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
…nt (#529) * fix(governance): close the mechanism-hardening-2 debt with its fixpoint All 15 verification-contract commands were rerun with the post-merge mechanism on main (daf551e, the #513 squash): - commands 1-13 locally, in exact contract order, single uninterrupted pass, every process exit 0 (evidence table with per-command timestamps) - command 14 canonical-linux-rebuild in pinned form from a fresh origin/main isolated worktree (no -IdentityFile, no -TargetId): deploy exit 0, tag deploy-20260812-639221315101291265-002 -> daf551e pushed - command 15 canonical-linux-deployment-verify in pinned form against that new deployment: six checks passed, none failed, exit 0 Ledger entry status open -> closed with fixpoint reverified_at 2026-08-12T11:40:00Z and mechanism_commit daf551e. Gates: test-self-referential-bootstrap and test-agent-governance-check all green. Refs #489 #490 #491 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(governance): add the fixpoint attestation.json for mechanism-hardening-2 The evidence gate requires docs/evidence/<slug>/fixpoint/attestation.json alongside the summary; added in the same schema as the linux-test-deploy-verifier-hardening precedent (all 15 contract command ids with exit_code 0, contract sha256 preserved, result pass) and linked from the ledger entry's fixpoint evidence_refs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: monkey1sai-blip <monkey1sai@icloud.com> * docs(evidence): resolve the stale reverified_at limit line Gate finding L1-correctness-1: verification.txt still carried the draft-era LIMIT saying the ledger timestamp was unset, contradicting its own completion fields, the summary, and the closed ledger entry. Replaced with an explicitly historical RESOLVED note. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Signed-off-by: monkey1sai-blip <monkey1sai@icloud.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: monkey1sai-blip <monkey1sai@icloud.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
fix(deploy,verify,launcher): mechanism-hardening-2 bundle
Closes #490, closes #491;#489 為 part-of。PR #513 review 後續修正納入同一 branch,避免在 canonical Linux fixpoint 前拆出另一個 deploy mechanism bundle。
變更摘要
補齊
.env.web-plane.host-kit.example的KIT_CONTROL_URL=repo 樣板佔位,並修正 deploy 明示環境檔優先權與 process-tree 子程序列舉失敗的 fail-closed 行為。修改原因
PR #513 的交叉審查證實兩個仍可重現的 P2:Phase 2 會覆寫 operator 明示的
-EnvFile;Windows CIM 或 Linux procfs 列舉失敗會被誤當成「沒有子程序」。兩者都可能讓部署在錯誤權威或未清乾淨的程序樹上繼續。主要變更
.env.web-plane.host-kit.example保留空值 placeholder,不寫入真實機密。scripts/deploy.ps1只在自動使用.examplefallback 時 repoint canonical env;明示-EnvFile保持不變。scripts/lib/platform/platform-adapter.ps1將成功空列舉與 CIM/procfs 讀取或解析失敗分開。scripts/lib/host-native-launcher.ps1對列舉失敗附 parent PID 並 fail closed。變更分類
AI Coding Governance
Stop-HostNativeProcessTreeAndWait與Get-PlatformChildProcessIds均 target-not-found;以直接 source、tests 與獨立 reviewer交叉裁決Deploy Path Verification
scripts/deploy.ps1、scripts/lib/host-native-launcher.ps1、scripts/lib/platform/platform-adapter.ps1pwsh -NoProfile -NonInteractive -File scripts/tests/test-deploy-dryrun.ps1(exact head exit 0)pwsh -NoProfile -NonInteractive -File scripts/tests/test-verify-all.ps1(exact head exit 0)Windows On-Demand Verification
test-deploy-dryrun.ps1、test-deploy-governance-static.ps1、test-platform-adapter.ps1、test-host-native-launcher.ps1、test-verify-all.ps1與 PR review agent required checks,全部 exit 0;Windows PowerShell 5.1 targeted enumeration fail-closed probe 亦通過。Exact-head CI run:https://github.com/monkey1sai/AI-BIM-governance/actions/runs/31587458547(最終狀態以 PR checks 為準)。Self-Referential Bootstrap
origin/main重建並拒絕 unmerged revision;本 branch 變更 deploy resolver、platform adapter 與 process-tree terminator,只能在 merge 後以 mainline fixpoint 產生 canonical evidence驗證方式
platform adapter (linux)已通過。git diff --check通過;latestorigin/mainmerge 結果為Already up to date,沒有衝突。風險與影響
正常的空 child set 仍可通過;只有無法可靠列舉時改為 fail closed。未修改 API、資料結構或真實
.env值;新增/確認的環境變數契約為 repo 樣板KIT_CONTROL_URL=。跨 orphan reparent 的強 containment 仍需 launch-time Job Object/process group,追蹤於 #522(Refs #517)。回滾方式
若只撤回本輪 P2 修正,revert
e4cd51db4f1f6d88c6aa0b1d73b8bb29b7e205a4;若撤回整個 bundle,於 merge 後 revert 對應 merge commit。後續建議