feat(deploy): migrate the persistent test deploy area to remote Linux (PR-B) - #467
Conversation
Lane G working plan for moving the persistent test-deploy environment from the local Windows box to 192.168.20.181 (Ubuntu 24.04, RTX 5080). Records the 21 decisions from the 2026-07-31 grilling session, the spike evidence (R1/W1/W2 all passed), and the PR-A/PR-B task split. Spike outcome: Kit 106.3 initialises its RTX Hydra renderer on Blackwell GB203 and streams 1920x1080 WebRTC across subnets with a bidirectional DataChannel, so no Kit SDK upgrade is needed. Two new findings are folded in: Linux headless requires --no-window, and a Windows-authored checkout lands *.sh without the exec bit so clone must chmod +x. Also corrects a risk misjudgement: ufw on the target is inactive, not active as previously recorded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
When a PR changes the verification mechanism itself (deploy path, evidence harness, or an adjudicating gate script), it cannot obtain evidence of the post-change behavior through the pre-change mechanism. This lands the general capability decided as D-7 in docs/plans/remote-linux-test-deploy-target.plan.md: - scripts/self-referential-bootstrap-ledger.json: machine-checked debt ledger (schema self-referential-bootstrap-ledger/v1, empty at birth) - scripts/lib/self-referential-bootstrap.ps1: ledger integrity validation (fail closed on malformed input, closed entries require a complete fixpoint record) and the PR-time debt gate - check-pr-body-evidence.ps1: mechanism-touching PRs must declare "Self-referential bootstrap" yes/no; yes requires the PR's own open ledger entry and a concrete reason; any other open debt blocks the PR until fixpoint evidence is committed - docs/agents/self-referential-bootstrap.md: portable rule text (no product nouns), indexed from AGENTS.md and CLAUDE.md - PULL_REQUEST_TEMPLATE.md: new evidence table - agent-governance.yml: runs test-self-referential-bootstrap.ps1 (ledger integrity x9, body gate x10, wire-up through the real checker x2) Validation: test-self-referential-bootstrap, test-pr-body-evidence, test-agent-governance-check, test-pr-review-agent all pass locally; workflow YAML parses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…B1-B3) B1 - scripts/deploy-target-registry.json + loader lib: the deploy target becomes registry data instead of constants scattered across five files. Two targets are defined (remote-linux-181 = canonical_test_deploy, local-windows = on_demand_platform_verification per decision D-3); linux_container stays a reserved kind - schema slot only, using it fails validation until phase 2 actually implements it. Spike findings are encoded as schema invariants so they cannot regress: linux targets MUST carry --no-window (F-1: headless Kit crashes in carb.windowing-glfw without it) and restore-exec-bits (F-2: a Windows-authored checkout lands *.sh as 100644 and repo.sh execs tools/packman/python.sh). B2 - scripts/lib/platform/platform-adapter.ps1: single-codebase pwsh primitives for the ownership gates - child process enumeration, TCP listener owner, process identity via a birth token (windows: Win32_Process.CreationDate; linux: /proc/<pid>/stat field 22 starttime), venv python and Kit launch resolution driven by the registry. B3 - equivalence is proven, not assumed: the SAME test suite passed unmodified on this Windows box and on the Ubuntu 24.04 target (pwsh 7.6.4), covering identity stability, PID-reuse rejection via birth token, child enumeration, and listener ownership. CI runs it on windows-latest (agent-governance workflow) and on ubuntu-latest via a new non-required ci.yml job (platform adapter (linux)) - branch protection is deliberately untouched. Validation: test-deploy-target-registry (11 cases) and test-platform-adapter both pass on windows and linux; both workflow YAMLs parse. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…(B4) The deploy target stops being four copies of the same constants and becomes registry data: - deploy.ps1: DefaultPublicHost / FixedTestDeployRoot / DefaultEdgeSiteId / DefaultEdgeRuntimeDataRoot now resolve through Get-DeployTargetForCurrentPlatform. On Windows the resolved values are byte-identical to the former inline constants (zero behavior drift); on Linux they resolve to the canonical remote-linux-181 profile. - rebuild-test-deploy lib: TestDeployFixedPath / TestDeployEdgeSiteId / TestDeployEdgeRuntimeDataRoot read the 'local-windows' target explicitly - this local rebuild routine only knows the local Windows deployment; canonical dispatch over SSH lands with B6/B8. - run-runtime-command-authority-host-native-evidence.ps1: binds to 'local-windows' explicitly (D-20 tier-3 Windows evidence harness). - find-deploy-blockers.ps1: filters by the current platform target's deploy_root. - test-rebuild-test-deploy harness: overrides DATA instead of code - copies deploy.ps1 unmodified and writes a sandbox registry whose local-windows deploy_root points at the sandbox, replacing the old string-rewrite of the constant line. - registry lib: adds Get-DeployTargetForCurrentPlatform (platform-kind filter -> RepoRoot match -> canonical tiebreak, PS 5.1 compatible). Deliberately NOT converged: test fixtures citing D:\Users\deploy paths (coordinator config.test.ts etc.) exercise parsing with arbitrary example values and are not drift sources. Validation: test-rebuild-test-deploy passes on pwsh 7 AND Windows PowerShell 5.1 (CI-style -File invocation); test-deploy-dryrun, test-deploy-governance-static, test-deploy-nullderef-guard, test-preflight-volume-alignment, test-verify-all, and the registry/ adapter suites all pass; find-deploy-blockers smoke-runs; the evidence harness parses clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
… (B5-B6) B5 - New-RemoteRebuildScript emits the bash rebuild that runs on an ssh target: clone-if-missing over the zero-credential https url, the verbatim contract refspec fetch (+refs/heads/main:refs/remotes/origin/main, stop on failure, never stale), reset --hard to fresh origin/main, git clean preserving .env*, and restore-exec-bits (spike F-2). B6 - env layering per decisions D-14/D-15: the operator pushes the per-target base file (registry env_file) on every rebuild; the remote keeps its override at <runtime_data_root>/env.local - outside the checkout, so git clean cannot eat it; the effective env is merged per-key with override-wins semantics. The merge has exactly ONE implementation: the remote script invokes Merge-DeployTargetEnvLayers from this same lib via pwsh inside the freshly-reset checkout, so no bash mirror can drift. The effective env is snapshotted at deploy time as point-in-time attestation: non-secret values in the clear, secret-looking keys reduced to sha256-8 fingerprint + length (the value never enters the record - the repo is public). Operator entry rebuild-test-deploy.ps1 gains -TargetId (default = registry canonical, i.e. remote-linux-181) and -IdentityFile; ssh targets dispatch through Invoke-RemoteTestDeployRebuild, the local Windows path is unchanged. Honest coverage statement: merge/masking/script-content/ssh-shape are sandbox-tested (test-remote-deploy-transport, wired into agent-governance CI); the generated script passed bash -n on the real Ubuntu target; the LIVE ssh dispatch is intentionally unexercised until B8 provisions the bimdeploy ssh credential, and the end-to-end run is B11's self_referential_bootstrap evidence. Also fixed en route: PS has no '<' stdin redirection - the pipeline feeds ssh stdin instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
Decisions D-16/D-17: the fixture authority is bucket bim-control on the shared MinIO; consumers pin bucket/key + etag + size (+ version_id when available) and fail closed on any drift - evidence is never taken against silently different data. - scripts/ifc-fixture-manifest.json: ifc-fixture-manifest/v1, born with zero entries (mechanism first; enrolment needs MinIO credentials - the bucket rejects anonymous access, verified 403). - scripts/lib/ifc-fixture-pin.mjs: manifest validation (fail closed), comparePin with named mismatch reasons, cache sidecar verdicts, and a lazy live-HEAD that reuses the coordinator's @aws-sdk/client-s3 via createRequire - no second S3 client, no hand-rolled SigV4. - Local caches verify only through the sidecar written at download time: the 89MB fixtures are multipart uploads, so the S3 ETag is not a re-computable content MD5. No sidecar => unverifiable => unusable. - scripts/tests/test-ifc-fixture-pin.mjs: 15 pure-logic cases, wired into agent-governance CI (no SDK/network needed). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…(B8 partial) Live evidence, taken over the EXACT production channel the code specifies (ssh BatchMode, bimdeploy, key auth - not the ad-hoc admin channel used during the spike): Invoke-RemoteTestDeployRebuild -> EXIT=0 clone-check -> contract refspec fetch -> reset --hard origin/main (landed d20e5a8, newer than the dispatch started - never-stale works) -> clean preserving env -> restore-exec-bits (rwxrwxr-x verified on repo.sh and packman/python.sh) -> single-implementation pwsh merge -> effective env (9 keys) -> masked snapshot on the operator side. Two real defects found ONLY by the live run, both fixed: 1. The remote merge referenced the transport lib inside the remote checkout - which resets to origin/main, where a pre-merge branch's lib does not exist (the self-referential bootstrap situation, live). The operator now ships this very file alongside the dispatch (<runtime_data_root>/transport-lib.ps1): still one implementation, delivered instead of assumed. 2. The PS pipeline appends an OS newline (CRLF) when feeding native stdin, landing a stray \r line in bash ($'\r': command not found, exit 127 after an otherwise successful run). The script now travels as a base64 one-liner - byte-precise. Provisioning done live en route (B8): bimdeploy authorized_keys installed over the one-time admin channel; keypair lives outside the repo and outside backup scope. Also: the per-target env family is now ignored by the TRACKED .gitignore (.env.web-plane.host-kit* with tracked *.example carve-out). The old arrangement kept the rule in .git/info/exclude - local-only, and the root cause of the historical "stale base re-adds a retired .env" trap. Validation: test-remote-deploy-transport (updated: shipped-lib + LibPushCommand assertions) passes; live dispatch EXIT=0 with snapshot 20260731T112730Z-effective-env.json. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
The three earlier snapshots were intermediate artifacts of the two defects fixed in the parent commit; only the successful run's snapshot is evidence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…review holes Hardens the bootstrap gate against every attack confirmed in the PR review round (10 threads, verified against head 01a66ce): P1 delete-debt-to-pass: the gate now evaluates the base -> head ledger transition. The ledger is append-only; removing an entry fails closed and open debt is computed over base UNION head, so deleting an entry cannot empty the debt set. P1 impersonate-earlier-entry: the declared 'Bootstrap ledger entry' must be ADDED by this PR (present at head, absent at base). P1 forged fixpoint: mechanism_commit must exist AND be an ancestor of the PR base (i.e. merged), fixpoint evidence_refs must exist in the head tree, and closure without base context is refused outright. P1 live merge-authority context: check-pr-body-evidence gains -PrNumber and resolves the base ledger via git show <base>; the required pr-metadata-contract check now passes PR_NUMBER, so the full transition gate runs against the live PR body and live shas. P1 fixture isolation: gate tests use fabricated ledgers only; the real repo ledger gets a parse-integrity check and is never assumed empty, so real debt entries cannot break unrelated CI. P2 padded reasons: lexical diversity (>=6 words, >=5 distinct) plus a generic-vocabulary dominance check kills 'bootstrap bootstrap ...'. P2 timestamp prefixes: anchored DateTimeOffset.TryParseExact replaces the prefix regex; '2026-99-99T99:99:99garbage' is rejected. P2 unbound pr field: new entries must record the actual PR number. P2 enforcement workflows unclassified: agent-governance.yml, pr-review-agent.yml, ci.yml and scripts/verification-manifest.json now classify as mechanism paths. P2 unscoped mechanism paths: a new entry's verification_mechanism_paths must be a subset of the PR's changed paths, and its evidence refs must exist in the head tree. Entry immutability is canonical-JSON compared; the only legal transition is open -> closed with a verified fixpoint. Validation: rewritten adversarial suite (every attack above has a test, incl. a git fixture repo for ancestry) passes; test-pr-body-evidence, test-agent-governance-check, test-pr-review-agent pass; both workflow YAMLs parse. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…ygiene Round-2 findings from the tri-adversarial bot (1 P1 + 6 P2), all verified real and fixed: P1 evidence refs must be committed files: Test-Path accepted '.', directories, and untracked workflow artifacts. Evidence refs (both a new entry's bootstrap refs and a closure's fixpoint refs) are now verified with git cat-file -t HEAD:<ref> == blob - filesystem-only presence is not reviewable evidence. Tests cover '.', untracked-but- present, and missing refs. P2 adjudicating verifiers unclassified: verify-functional-runtime- result.ps1, verify-security-exceptions.ps1 and verify-openspec- machine-truth.mjs now classify as mechanism paths. P2 local preflight parity: check-pr-local-preflight.ps1 now passes -PrNumber through to the checker, so pr-binding violations surface locally instead of only in CI. P2 punctuated generic reasons: tokens are stripped of punctuation before blocklist/diversity checks; 'bootstrap, needed, required, because, chicken, egg.' is rejected. P2 non-array entries: '"entries": null' and object forms fail closed. P2 fixpoint chronology: reverified_at must be strictly after opened_at - a fixpoint cannot predate its debt. P2 stray env file: .env.web-plane.host-kit.remote-linux-181 was accidentally committed from a live-dispatch test (its consumers live on the PR-B branch, and the ignore rule only existed there). Untracked it and added the per-target env ignore family to this branch's .gitignore as well. Validation: adversarial suite (now 40+ cases incl. committed-blob and chronology attacks) passes; test-pr-body-evidence, test-agent-governance-check, test-pr-review-agent pass; preflight parses clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…s (round 3) Round-3 findings from the tri-adversarial bot (1 P1 + 5 P2), all verified real and fixed: P1 self-adjudication: a PR editing the checker or this library used to be judged by its own edited copy. pr-review-agent.yml now materializes the gate scripts from the PR BASE revision (git archive base) and runs that copy against the PR's data; a base predating the gate falls back to the head copy exactly once (nothing to weaken). The base checker's parameter surface is feature-detected before passing -PrNumber. P2 synthetic-merge evidence: blobs (and the head ledger itself) now resolve against the passed PR head SHA instead of ambient HEAD, so a blob that only exists in the merge tree or newer base cannot serve as head-tree evidence. P2 lenient timestamps: raw JSON date tokens are validated against the anchored ISO forms BEFORE ConvertFrom-Json materializes them as [datetime]; '2026-07-31T08:00:00' (no timezone) now fails closed. P2 coerced pr numbers: 'pr' must be a native integral JSON number; '"500"' and 500.4 are rejected. P2 local enforcement entrypoints: check-pr-local-preflight.ps1 and scripts/hooks/require-gstack-evidence.ps1 classify as mechanism. P2 CJK reasons: substance for CJK prose is judged on ideograph count (>=12) instead of whitespace tokens, and the tokenizer keeps Unicode letters - a concrete Chinese rationale is first-class, padded ASCII still dies. Validation: adversarial suite (now covering all three review rounds) passes; test-pr-body-evidence, test-agent-governance-check, test-pr-review-agent pass; workflow YAML parses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
… (round 4) Round-4 findings from the tri-adversarial bot. Four are fixed as code; the fifth (workflow self-pinning) hits a documented trusting-trust boundary that needs an owner infra decision, not an autonomous change. P1 closure binding: a closure could name any ancestor commit plus any existing blob. Now the mechanism_commit must actually have modified one of the entry's declared verification_mechanism_paths (it is THIS mechanism's merge, not an ancient ancestor), and each fixpoint evidence blob must have been introduced at or after that commit (a pre-existing unrelated blob cannot stand in for post-merge re-verification). The git fixture is rebuilt with a real 4-commit history proving both. P2 timestamp key casing: the raw-string validator is now case-insensitive on the key, so 'Opened_at' (which PowerShell reads case-insensitively) can no longer bypass the anchored-format check. P2 design-assets classifier: scripts/lib/design-assets.ps1 (dot-sourced by deploy.ps1) now classifies as a mechanism path. P2 padded CJK: a CJK reason must also carry >=8 distinct ideographs, so '引導引導...' padding is rejected while real Chinese prose passes. P1 workflow self-pinning (NOT auto-fixed, documented in docs/agents/self-referential-bootstrap.md §4.1): a PR-triggered check runs the PR's own workflow YAML, so a PR that guts the invocation step can green the required check without running the base-pinned gate. Current mitigation: pr-review-agent.yml is itself a mechanism path, so editing it forces a bootstrap declaration + reviewable ledger debt. Full closure needs a base-owned trigger (pull_request_target, which exposes secrets to PR context - a security-posture change) or an org-level required action. That is an owner decision, surfaced rather than churned. Validation: adversarial suite (now covering four review rounds incl. mechanism-touch and post-merge-evidence binding) passes; test-pr-body-evidence, test-agent-governance-check, test-pr-review-agent pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…ledger' into feat/remote-linux-deploy-target
… (B9) Rewrites product-operability-and-script-contract.md for the migration: §6 Script Contract - the deploy target is now registry data (scripts/deploy-target-registry.json) instead of one hardcoded path: exactly one canonical_test_deploy (remote-linux-181) plus on-demand platform verification targets (local-windows, explicitly non-canonical and non-resident). The operator entrypoint sentence and the "freshly fetch origin with +refs/heads/main:... never stale" clause are preserved VERBATIM - only target resolution changed. Adds the ssh transport, per-target env layering with remote-wins overrides outside the checkout, and the deploy-time effective-env snapshot (secret values never recorded). The two spike-derived platform invariants (--no-window, restore-exec-bits) are documented as schema-enforced, not advisory. §5 Real IFC E2E - fixture authority moves from one machine's local storage/ to pinned MinIO objects, because there is no longer a single "main workspace". Local storage/ is demoted to an ETag-sidecar-verified cache; no sidecar means unverifiable, not usable. Also fixes the pre-existing drift: the section named storage\270_0dac5239-... which does not exist on disk. §3 Frontend Dual-Gate - splits the single pipeline into the two gates that now run on different machines, and states why design fidelity stays Windows-bound (baselines are Windows-Chromium, runner label allowlist) and why runtime evidence must come from the Windows browser hitting the target rather than the target's own localhost. §8 - adds the mutual non-inference table for the three stack kinds (isolated_branch_stack / deploy-target / self_referential_bootstrap). §1 - viewer :5173 may not be the INITIAL entrypoint but must be reachable as the /ui/open 302 handoff target; exposure is controlled by source-subnet allowlist rather than bind address (phase 1). Also fixes a defect this rewrite's own gate caught: the platform-adapter-linux job added in B1-B3 used the always()+classifier-failure pattern without the guard step, which would report skipped-success. It is deliberately NOT a required check, so the correct fix is to drop always() entirely (a non-required job should just skip) rather than bump the governance count from 14 to 15. Validation: test-agent-governance-check, test-deploy-target-registry, test-platform-adapter, test-remote-deploy-transport, test-pr-body-evidence, test-self-referential-bootstrap and test-ifc-fixture-pin (15/15) all pass; ci.yml parses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…ier (B10) Decision D-20. The canonical test deployment is now the Linux target, so the Windows path only survives if "on demand" has a machine definition - otherwise nobody remembers until the day it is needed and it has already rotted. scripts/lib/windows-verification-scope.ps1 derives the owed tier from changed paths: tier 1 platform_unit scripts/lib/platform/** seconds tier 2 deploy_dryrun deploy path / libs / compose minutes tier 3 kit_gpu Kit sources + build toolchain heavy, rare Only the highest match is owed (tier 3 subsumes 2 subsumes 1), so a Kit-source PR is not asked for three separate proofs. Docs, tests-only changes and the Linux-only adapter branch are deliberately exempt: demanding Windows evidence where Windows behavior cannot change is how a gate teaches people to route around it. Enforced in check-pr-body-evidence: the declared tier must EQUAL the machine-derived tier, so a PR can neither self-select an easier tier nor claim a heavier one it did not run; weasel evidence values (none / n/a / TBD) are rejected. PR template and agent-governance CI wired up. Dogfooded: the gate says this very branch owes deploy_dryrun (it touches deploy.ps1 and three scripts/lib libraries), so the evidence was actually produced rather than asserted: .\scripts\deploy.ps1 -DryRun -> EXIT=0 resolved profile: local-windows (PUBLIC_HOST 192.168.10.105, storage root under the Windows worktree) That run also independently corroborates B4's claim that moving the deploy-target constants into the registry preserved Windows behavior: the Windows resolution branch still produces the same target values. Validation: test-windows-verification-scope (tier selection, subsumption, exemptions, path normalization, PR-body enforcement), test-pr-body-evidence, test-self-referential-bootstrap, test-agent-governance-check, test-pr-review-agent all pass; agent-governance.yml parses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…unning The Codex tri-adversarial gate (GPT-5.5 family, a different model family from the four Claude rounds) returned NO-SHIP with 7 confirmed findings. The worst one shows the round-3 "trusted revision" fix silently disabled the gate for this very PR. L1-correctness-2 (high) - capability detection tested one file instead of the capability. A PR that ADDS the bootstrap gate MODIFIES check-pr-body-evidence.ps1 rather than creating it, so "does base have the checker?" was vacuously true, the old base checker ran, and PR #459 was evaluated without the bootstrap declaration, ledger transition, PR binding or debt logic it introduces - while head-side unit tests stayed green. Detection now requires the whole capability at base (library present + checker dot-sources it + assertion actually invoked), and when the base cannot adjudicate, the workflow demands an explicit "Self-referential bootstrap | yes" declaration via a tiny dependency-free inline check before running the head copy, marking GATE_SOURCE. L1-correctness-3 (high) - declared verification_mechanism_paths only had to appear in ChangedPaths, not to BE classified mechanism paths. A PR could change a real mechanism file plus an unrelated file, declare only the unrelated one, and later close the debt against that unrelated path, leaving the change that triggered the gate outside the ledger binding. Declared paths must now be classified mechanism paths AND cover every mechanism path the PR changes. L1-correctness-4 (medium) - evidence chronology called `git log -1 -- <ref>` with no revision, so it walked from ambient HEAD (the synthetic merge ref in a pull_request checkout). It now walks from the supplied HeadSha and refuses closure when HeadSha is absent. $entry residual (surfaced by the apex while refuting L1-correctness-1) - the closure path read $entry, still bound by this function's earlier foreach loops, so with more than one ledger entry it validated against the LAST head entry. Now uses $head. TG-2 - the workflow's base-materialization decision is extracted to scripts/lib/detect-base-gate-capability.sh so tests execute the same logic instead of replicating it, and test-base-gate-capability.ps1 builds real base/head revisions asserting the shipped regression shape (checker-only base) is now classified incomplete and that a weakened head cannot change an earlier complete base's verdict. TG-3 - the ledger tests gain a real differential fixture: ledgers are committed at exact revisions, closure runs against a supplied HeadSha, and one case makes the working tree disagree with the head revision to prove the gate follows the SHA, not the checkout. TG-1 (low) - test-preflight-prnumber-forwarding asserts via AST that -PrNumber is a real argument (not a comment) and proves behaviorally that a mismatched PR number fails, so the forwarding is load-bearing. SEC-001 (high) remains an owner infra decision and is NOT auto-changed; docs/agents/self-referential-bootstrap.md §4.1 now carries the Codex argument verbatim, including the warning that a pull_request_target design must never execute head code or head-controlled actions while privileged. Validation: test-self-referential-bootstrap, test-base-gate-capability, test-preflight-prnumber-forwarding, test-pr-body-evidence, test-agent-governance-check and test-pr-review-agent all pass; both workflow YAMLs parse. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…ledger' into feat/remote-linux-deploy-target # Conflicts: # .github/workflows/agent-governance.yml
…deploy-target migration Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…igration (B11) Adds the controlled capability the plan's step (3) needs: verifying an UNMERGED revision on the canonical target. The deploy contract forbids this in general, so -BootstrapRef is gated by Assert-BootstrapRefAllowed: the caller must name a ledger entry that exists, is open, and declares scripts/deploy.ps1 among its mechanism paths. Without a ref the script is byte-identical to before - reset to the freshly fetched origin/main. Opens the ledger entry authorising it. The gate requires the entry to cover EVERY mechanism path this PR changes, so all 11 are declared, and the evidence ref is a committed blob. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…dapter The first real remote deploy failed in Phase 2 with '.venv/Scripts/python.exe is not recognized' - the platform adapter existed (B2) but deploy.ps1 and preflight-host-native.ps1 still hardcoded the Windows shapes, so on Linux the venv interpreter was unresolvable and Kit artifacts would have reported NEEDS_BUILD forever. - deploy.ps1 (3 sites) and preflight-host-native.ps1 (1 site) now call Resolve-PlatformVenvPython; the adapter is dot-sourced first so every later phase can use it. - Get-KitRuntimeBuildArtifacts derives launcher path, Kit binary name and build command from the deploy-target registry for the current platform instead of the windows-x86_64/.bat/kit.exe literals. - Registry build_command for local-windows restored to '.\repo.bat build': the bare name was a real regression risk, since a bare repo.bat has been observed to fail PATHEXT resolution on some hosts (the existing preflight test asserts the '.\' form, and it was right). Validation: test-preflight-host-native, test-deploy-dryrun, test-deploy-target-registry, test-platform-adapter and test-rebuild-test-deploy all pass; Windows resolution is byte-identical to before (verified by dry-run resolving the same windows-x86_64 paths). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…reation Second real-deploy failure, one layer deeper than the first. The venv path was correct after the adapter wiring, but the venv never existed: the remote ships python3 with no python, and `& python -m venv` under ErrorActionPreference 'Continue' merely prints when the name does not resolve while leaving $LASTEXITCODE stale from an earlier command. The guard therefore passed, creation silently no-opped, and the run failed later with a confusing 'venv python not recognized'. That is fail-open in the deploy path. - Resolve-PlatformSystemPython probes candidates in platform order (linux: python3 then python; windows: python then python3, because Windows ships a python3 Store-alias stub that exits doing nothing) and requires one that actually reports a version, else returns null. - New-DeployVenv resolves it, creates the venv, then VERIFIES the resulting interpreter exists before returning - failing closed with a precise message instead of deferring to a later phase. All three creation sites use it; their now-dead LASTEXITCODE guards are removed. - test-platform-adapter asserts the resolved name is actually runnable, not merely non-empty. Validation: test-platform-adapter, test-deploy-dryrun, test-preflight-host-native, test-deploy-nullderef-guard and test-deploy-governance-static all pass; deploy.ps1 parses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
Third real-deploy failure was a provisioning gap, not a code defect:
Debian/Ubuntu split ensurepip into python3.12-venv, so `python3 -m venv`
failed - and the fail-closed guard added in the previous commit reported
it at the right place with the real reason instead of deferring to a
confusing later error.
scripts/dev/provision-linux-deploy-target.sh captures the provisioning
that was previously done by hand, so a second target is reproducible.
Every package is justified by an observed failure rather than a guess,
and the version-matched python${minor}-venv is derived from the target's
own interpreter instead of pinned.
Deliberately NOT in the script, with reasons inline: the NVIDIA driver
(the correct package is per-GPU via `ubuntu-drivers devices`; a pin
would rot), the service-account SSH key, and enabling ufw (changes the
posture of a shared host - an owner decision).
Validation: bash -n passes on the real Ubuntu 24.04 target.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
…p test The fourth real deployment to 192.168.20.181 died at `pip install` with "No module named pip". `python3 -m venv` creates bin/python BEFORE running ensurepip, so the run that failed on a missing python3-venv left a directory that passed every existence check and then blew up one phase later. - preflight now probes for pip and reports a pip-less venv as MISSING, so the existing recreate path runs instead of trusting the corpse. The probe is injectable like its siblings and is exception-safe: an unusable interpreter must answer "no pip", not throw out of the audit. - New-DeployVenv removes any existing .venv before recreating and verifies pip afterwards, so a half-built venv cannot survive a second time. - test-self-referential-bootstrap's wire-up case pointed base and head at the live repo HEAD. The gate reads both ledgers with `git show <sha>:...`, so the moment a real open entry landed the case silently changed meaning from "clean transition" to "inherited open debt". It now builds a fixture revision (the HEAD tree with the ledger swapped), honouring this file's own stated contract that gate tests use fixture ledgers only. Verified: test-preflight-host-native (incl. a new half-built-venv regression case), test-self-referential-bootstrap, test-deploy-target-registry, test-platform-adapter, test-remote-deploy-transport, test-windows-verification-scope, test-rebuild-test-deploy, test-production-boundary-contract, test-agent-governance-check - all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
Deployment attempt 5 cleared Phase 2 and died at Phase 4a: the governance service reported "governance PID=" as [ok] and then failed its health check 30 seconds later. Root cause was the same class as the venv bug - Windows paths hardcoded in a shared library: - all three launchers (governance, kit-manager-api, conversion-service) each carried their own copy of the interpreter lookup, hardcoded to the Windows venv layout .venv\Scripts\python.exe. That never matches on Linux (.venv/bin/python), so every one fell through to the bare name 'python', which the target does not have (python3 only). Replaced by one Resolve-HostNativePython used by all three; Windows keeps preferring the system 3.12 install, and the helper throws instead of returning a name the shell cannot run. - Start-HostNativeService passed -WindowStyle unconditionally, which PowerShell rejects off Windows, and returned an object whose Pid was absent when Start-Process produced nothing. It now passes -WindowStyle only on Windows and throws on an empty start, so the failure is reported where it happens instead of as a health-check timeout. - the conversion service launched via 'powershell.exe' (Windows-only); it now resolves to pwsh off Windows. - Stop-HostNativeService's child lookup called Win32_Process directly, so on Linux the CIM error was swallowed and the tree walk degraded to killing only the wrapper, leaving children holding their ports. It now goes through the adapter, which reads /proc there. 'scripts\.run' is deliberately left as a literal: every site that reads or writes those PID files uses the same string, so they agree even though Linux treats the backslash as part of the name. Normalising one site alone would split the PID directory in two. Recorded as a follow-up, not smuggled in here. Also ticks B1/B2/B3 in the plan (delivered earlier, checkboxes never updated). B3's Linux-side equivalence run is explicitly marked as still owed. Verified: test-deploy-governance-static, test-rebuild-test-deploy, test-platform-adapter, test-preflight-host-native, test-deploy-target-registry, test-self-referential-bootstrap - all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/deploy.ps1 (1)
1141-1154: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftUse the target registry Kit build command instead of the Windows launcher default.
Phase 2 always reports
$hostNative.kitBuildCommand, butInvoke-KitRepoBuildstill uses its Windowscmd.exe/repo.batdefault. For targets likeremote-linux-181, the registry already declares./repo.sh build; route the resolved command into the build phase or fail clearly before reaching the Linux SSH target. Update the timeout/failure messages so they report the configured build command rather than always namingrepo.bat.🤖 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/deploy.ps1` around lines 1141 - 1154, The Phase 2 Kit build currently ignores the configured $hostNative.kitBuildCommand and hard-codes repo.bat in its status messages. Update the Invoke-KitRepoBuild call to use the resolved registry command, fail clearly if the command is unavailable before targeting Linux over SSH, and revise the timeout and failure messages to report $hostNative.kitBuildCommand instead of repo.bat.
🧹 Nitpick comments (13)
scripts/tests/test-platform-adapter.ps1 (2)
31-35: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSpawn the current PowerShell host instead of assuming
pwshexists.Both branches start
pwsh. The Windows deploy target runs Windows PowerShell 5.1 in several paths, andpwshis not guaranteed to be installed there.Start-Processthen throws and the whole suite fails before it reaches the ownership assertions. Use the running host executable.♻️ Proposed change
+$hostExe = (Get-Process -Id $PID).Path $child = if ($platform -eq 'windows') { - Start-Process -FilePath 'pwsh' -ArgumentList @('-NoProfile', '-Command', 'Start-Sleep 60') -PassThru -WindowStyle Hidden + Start-Process -FilePath $hostExe -ArgumentList @('-NoProfile', '-Command', 'Start-Sleep 60') -PassThru -WindowStyle Hidden } else { - Start-Process -FilePath 'pwsh' -ArgumentList @('-NoProfile', '-Command', 'Start-Sleep 60') -PassThru + Start-Process -FilePath $hostExe -ArgumentList @('-NoProfile', '-Command', 'Start-Sleep 60') -PassThru }🤖 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-platform-adapter.ps1` around lines 31 - 35, Update the child process creation in the platform adapter test to use the currently running PowerShell host executable rather than hardcoding `pwsh` in either `Start-Process` branch. Preserve the existing arguments and Windows-specific `-WindowStyle Hidden` behavior.
56-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDeclare
$portbefore thetryblock.
$portis assigned at Line 60 insidetry, and Line 67 reads it after thefinally.Set-StrictMode -Version Latestis active. If Line 60 fails, Line 67 raises an unassigned-variable error that hides the original failure.🤖 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-platform-adapter.ps1` around lines 56 - 67, Declare and initialize $port before the try block in the TCP listener ownership test, then assign the actual listener port inside try as currently done. Preserve the post-finally null-owner assertion while ensuring a failure before assignment does not trigger an unassigned-variable error under strict mode.scripts/lib/deploy-target-registry.ps1 (1)
75-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winValidate
host_native_bind_hostin the registry schema.
Get-HostNativeBindHostinscripts/lib/host-native-launcher.ps1(Lines 81-86) requireshost_native_bind_hoston the resolved target and throws when it is blank. The registry validator does not check this field. A target that omits it passes validation and fails later during Phase 4 service startup. The file header states that invariants are encoded at schema level so they cannot regress. Add the field to the required-field list.♻️ Proposed change
- foreach ($field in @('deploy_root', 'runtime_data_root', 'public_host', 'edge_site_id', 'env_file')) { + foreach ($field in @('deploy_root', 'runtime_data_root', 'public_host', 'edge_site_id', 'env_file', 'host_native_bind_host')) {🤖 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/lib/deploy-target-registry.ps1` around lines 75 - 79, Update the required-field list in the registry validator’s foreach loop to include host_native_bind_host, ensuring blank or missing values are rejected during schema validation before service startup.scripts/lib/host-native-launcher.ps1 (1)
8-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a guarded load for
deploy-target-registry.ps1.
Get-HostNativeBindHost(Line 81) callsGet-DeployTargetForCurrentPlatform, which is defined inscripts/lib/deploy-target-registry.ps1. This file only loadsplatform/platform-adapter.ps1. A consumer that dot-sources this library alone therefore fails with a command-not-found error at Line 81.scripts/lib/preflight-host-native.ps1(Lines 11-13) already adds the same guarded load for this exact reason.♻️ Proposed change
if (-not (Get-Command -Name 'Resolve-PlatformVenvPython' -ErrorAction SilentlyContinue)) { . (Join-Path $PSScriptRoot 'platform/platform-adapter.ps1') } +if (-not (Get-Command -Name 'Get-DeployTargetForCurrentPlatform' -ErrorAction SilentlyContinue)) { + . (Join-Path $PSScriptRoot 'deploy-target-registry.ps1') +}🤖 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/lib/host-native-launcher.ps1` around lines 8 - 12, Add a guarded dot-source for deploy-target-registry.ps1 alongside the existing platform-adapter.ps1 load, checking for Get-DeployTargetForCurrentPlatform before loading it. Ensure standalone consumers of Get-HostNativeBindHost resolve the command without reloading the registry when it is already available.scripts/lib/platform/platform-adapter.ps1 (1)
220-227: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winWrap the version probe in try/catch to keep the documented "returns $null" contract.
The comment states the function returns
$nullwhen no candidate works.Get-Commandcan resolve a candidate that fails to launch. In that case& $candidate --versionraises an error record. Callers that set$ErrorActionPreference = 'Stop'(for examplescripts/tests/test-platform-adapter.ps1Line 9) then terminate instead of receiving$null. The sibling probes inscripts/lib/preflight-host-native.ps1already use try/catch for the same reason.♻️ Proposed change
foreach ($candidate in $candidates) { if ($null -eq (Get-Command -Name $candidate -ErrorAction SilentlyContinue)) { continue } - $version = (& $candidate --version 2>&1 | Out-String) - if ($LASTEXITCODE -eq 0 -and $version -match '\d+\.\d+') { return $candidate } + try { + $version = (& $candidate --version 2>&1 | Out-String) + } catch { continue } + if ($LASTEXITCODE -eq 0 -and $version -match '\d+\.\d+') { return $candidate } }🤖 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/lib/platform/platform-adapter.ps1` around lines 220 - 227, Wrap the version probe inside the candidate loop in the function containing Get-PlatformName with try/catch, including the `& $candidate --version` invocation and its validation. On any launch or probe error, continue checking remaining candidates; if none succeeds, preserve the function’s documented `return $null` behavior, including when `$ErrorActionPreference` is `Stop`.scripts/tests/test-deploy-target-registry.ps1 (1)
58-84: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSelect mutation targets by kind, not by array index.
These negative tests assume
targets[0]is the Windows target andtargets[1]is the Linux target. If a target is added or reordered inscripts/deploy-target-registry.json, each mutation lands on a different target. The F-1, F-2, POSIX-path, and duplicate-role assertions then stop testing the invariant they name, and they can still pass for the wrong reason. Lines 93-94 already resolve targets bykind; use the same approach here.♻️ Example for one case
Assert-Throws -Context 'linux target without --no-window' -MessagePattern 'F-1' -Action { - Get-DeployTargetRegistry -Path (Write-Mutated 'nowindow.json' { param($r) $r.targets[1].kit.extra_launch_args = @() }) + Get-DeployTargetRegistry -Path (Write-Mutated 'nowindow.json' { + param($r) + $t = @($r.targets | Where-Object { [string]$_.kind -eq 'linux_host_native' })[0] + $t.kit.extra_launch_args = @() + }) }🤖 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-target-registry.ps1` around lines 58 - 84, Update the negative-test mutations in the Assert-Throws cases to locate targets by their kind, matching the existing kind-based resolution near lines 93-94, instead of relying on targets[0] or targets[1]. Apply the appropriate Windows or Linux target lookup for duplicate IDs, reserved kind, F-1, F-2, SSH user, root-path, and canonical-role mutations so each assertion continues testing its named invariant after target reordering or additions.scripts/lib/ifc-fixture-pin.mjs (2)
46-54: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
logical_nameaccepts.and...The regex rejects separators, so traversal is not possible from this value alone. A consumer that joins
logical_nameto a cache directory would still resolve..to the parent directory and.to the directory itself. Reject both names here, where the fail-closed contract lives.♻️ Proposed change
- if (!/^[^\\/]{1,128}$/.test(name)) { + if (!/^[^\\/]{1,128}$/.test(name) || name === '.' || name === '..') { throw new Error(`ifc_fixture_pin: logical_name '${name}' must be a bare file name.`); }🤖 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/lib/ifc-fixture-pin.mjs` around lines 46 - 54, Update the logical_name validation in the manifest.entries loop to explicitly reject the exact names "." and ".." in addition to the existing bare-file-name checks. Preserve the current validation and duplicate detection behavior for all other names.
147-152: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSet explicit S3 HTTP timeouts and retries in
headPin.This
S3Clientruns on@aws-sdk/client-s3/@smithy/node-http-handler, where disabled request and connection timeouts can let an unreachable MinIO host block the live HEAD until the OS socket timeout. Add explicitrequestHandlertimeouts andmaxAttemptsso CI/deploy jobs fail or retry instead of hanging.🤖 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/lib/ifc-fixture-pin.mjs` around lines 147 - 152, Update the S3Client construction in headPin to configure an explicit Node HTTP request handler with finite connection and request timeouts, and set maxAttempts to the intended retry limit. Preserve the existing endpoint, region, path-style, and credentials configuration..github/workflows/ci.yml (1)
154-155: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd
timeout-minutesto the job.The
agent-governancejob setstimeout-minutes: 30. This job sets none, so it inherits the 360-minute default. A hung listener probe or process-ownership test in the adapter suite would hold a runner for six hours.♻️ Proposed change
runs-on: ubuntu-latest + timeout-minutes: 15 steps:🤖 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 @.github/workflows/ci.yml around lines 154 - 155, Add a finite timeout-minutes setting to the job containing the runs-on: ubuntu-latest and steps block, matching the 30-minute timeout used by agent-governance. Keep the existing job steps unchanged.scripts/tests/test-ifc-fixture-pin.mjs (1)
104-109: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winThis test pins the sidecar gap in place.
Line 106 builds a sidecar with
'a'.repeat(64)as the downloaded-bytes digest and assertscache_valid. No cached file exists in the test, and no digest is compared. The assertion therefore confirms thatevaluateCacheSidecarignoressidecar.sha256, which is the defect raised onscripts/lib/ifc-fixture-pin.mjslines 98-128.When you make the digest load-bearing, add a case that asserts
stale_cachefor a sidecar whosesha256does not match the observed bytes.🤖 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-ifc-fixture-pin.mjs` around lines 104 - 109, Update the test around evaluateCacheSidecar to make the sha256 digest load-bearing: provide or configure observed cached bytes, then add an assertion that a sidecar with a mismatched sha256 returns stale_cache. Preserve the existing valid-digest and drifted-etag coverage while ensuring the test no longer passes without comparing sidecar.sha256.scripts/tests/test-deploy-governance-static.ps1 (1)
85-87: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winScope the
depends_oncheck to the coordinator service.The regex matches anywhere in
compose.host-kit.yml. The error message and the comment above describe a coordinator-specific requirement, but the check passes if any other service carriesdepends_on: !override []while the coordinator does not. Anchor the match to the coordinator block so the guard cannot be satisfied by an unrelated service.♻️ Proposed change
-if ($hostKitCompose -notmatch 'depends_on:\s*!override\s*\[\]') { +$coordinatorBlock = [regex]::Match( + $hostKitCompose, + '(?ms)^ coordinator:\r?\n(?:^(?: |\t).*\r?\n|^\r?\n)*' +).Value +if ([string]::IsNullOrWhiteSpace($coordinatorBlock)) { + throw 'compose.host-kit.yml must define a coordinator service' +} +if ($coordinatorBlock -notmatch 'depends_on:\s*!override\s*\[\]') { throw 'compose.host-kit.yml must clear coordinator depends_on (!override []) so hybrid mode does not start the containerised kit-manager-api on the host-native :8010' }🤖 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 85 - 87, Update the depends_on validation in the host-kit compose check to inspect only the coordinator service block, ensuring its coordinator-specific depends_on entry contains !override []. Do not allow a matching entry in another service to satisfy the guard, and preserve the existing failure message.scripts/lib/remote-deploy-transport.ps1 (1)
17-20: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSet
$ErrorActionPreference = 'Stop'in this library.The file sets
Set-StrictMode -Version Latestbut leaves the error preference at the caller's value.Invoke-RemoteTestDeployRebuildperformsGet-Content,New-Item, andSet-Contentcalls whose failures are non-terminating by default. If a caller runs withContinue, the function proceeds after a failed read and then evaluates$LASTEXITCODE, which still holds the value of an earlier native command. The sibling librariesscripts/lib/rebuild-test-deploy.ps1andscripts/tests/test-remote-deploy-transport.ps1both setStop.♻️ Proposed change
Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop'🤖 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/lib/remote-deploy-transport.ps1` around lines 17 - 20, Set $ErrorActionPreference = 'Stop' near Set-StrictMode in the remote deploy transport library so failures from Invoke-RemoteTestDeployRebuild operations such as Get-Content, New-Item, and Set-Content terminate immediately. Match the established configuration used by the sibling libraries without changing the function’s other behavior.scripts/tests/test-remote-deploy-transport.ps1 (1)
99-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the bootstrap authorization guard.
Assert-BootstrapRefAllowedis the control that permits deploying an unmerged revision to a real target. The suite does not exercise it. Four failure modes are cheap to test against a temporary ledger file:-BootstrapRefwithout-BootstrapLedgerEntry,-BootstrapLedgerEntrywithout-BootstrapRef, a ledger entry whosestatusis notopen, and an entry that omitsscripts/deploy.ps1fromverification_mechanism_paths. A positive case should also confirm thatNew-RemoteRebuildScript -BootstrapRefemitsrefs/remotes/origin/<ref>as the reset target.Do you want me to generate these test cases?
🤖 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-remote-deploy-transport.ps1` around lines 99 - 113, Add tests in the bootstrap authorization section using a temporary ledger file to cover Assert-BootstrapRefAllowed: reject BootstrapRef without BootstrapLedgerEntry, reject BootstrapLedgerEntry without BootstrapRef, reject entries whose status is not open, and reject entries missing scripts/deploy.ps1 from verification_mechanism_paths. Also add a successful case confirming New-RemoteRebuildScript with BootstrapRef resets to refs/remotes/origin/<ref>, cleaning up the temporary ledger afterward.
🤖 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 `@bim-streaming-server/scripts/start-streaming-server.ps1`:
- Around line 40-49: Validate each workspace helper path before dot-sourcing it
in the Get-PlatformName and Get-DeployTargetForCurrentPlatform setup. If the
expected dependency is absent and the command is not already available, throw an
actionable error naming the missing helper and workspace dependency instead of
allowing an implicit dot-source failure.
In
`@docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.md`:
- Around line 1-7: Add a document-nature declaration next to the existing
stack_kind line in the self-referential bootstrap README, using one of the
allowed labels: agent boundary, contract, wiki, runbook, spec design, or working
note. Keep the existing evidence classification and explanatory text unchanged.
In `@docs/plans/remote-linux-test-deploy-target.plan.md`:
- Line 281: Reconcile the B12 completion entry with the self-referential
bootstrap-gate status: update the plan or PR status so both consistently
indicate whether the `#459` prerequisite debt and post-merge fixpoint evidence
have closed the gate. Keep the attestation and verification details aligned with
the resulting governance state.
In `@scripts/deploy.ps1`:
- Around line 54-65: Update New-GovernanceRuntimeSignature to use the resolved
host from Get-HostNativeBindHost instead of hardcoding 127.0.0.1, so the
signature changes whenever the target profile’s host_native_bind_host changes.
Preserve the existing signature structure and ensure it matches the bind host
used by Start-HostNativeGovernance.
In `@scripts/dev/find-deploy-blockers.ps1`:
- Around line 1-6: Update the process filter in the deploy-blocker script to
construct the wildcard with Join-Path using $deployRoot and '*', replacing the
hard-coded backslash pattern. Preserve the existing process selection and output
behavior.
In `@scripts/ifc-fixture-manifest.json`:
- Around line 3-7: Update the authority endpoint in the fixture manifest to use
HTTPS instead of HTTP, ensuring requests made by headPin and the S3Client are
sent over TLS.
In `@scripts/lib/host-native-launcher.ps1`:
- Around line 228-242: Update the detachment validation around
Test-PlatformProcessDetached to poll until the process becomes a session leader,
using a short bounded deadline and brief delays between checks. Throw the
existing failure only when the deadline expires, while preserving the current
process-start validation and diagnostic details.
In `@scripts/lib/ifc-fixture-pin.mjs`:
- Around line 98-128: Update evaluateCacheSidecar to accept the cached file’s
observed SHA-256 digest and require it to match sidecar.sha256 before returning
cache_valid. Return an unverifiable_no_sidecar verdict with an appropriate
reason when the digest is missing or mismatched, while preserving the existing
schema and pin checks; update callers holding the cached file to pass its
digest.
In `@scripts/lib/preflight-ports.ps1`:
- Around line 65-74: Update Get-PidsFromRunDir’s default child-process lookup to
use the platform adapter instead of directly calling Get-CimInstance
Win32_Process. Ensure descendant PIDs for Linux Kit or Python listeners are
discovered so deployment-owned listeners remain classified as ourPidFile, while
preserving the existing port lookup behavior.
In `@scripts/lib/remote-deploy-transport.ps1`:
- Around line 339-346: The snapshot creation flow around
New-DeployTargetEnvSnapshot must pass the actual keys overridden by the remote
env.local layer instead of relying on the default empty -OverriddenKeys value.
Reuse the override set returned by the remote merge, or recompute it from the
pushed base layer and $values, then supply it when constructing $snapshot so
overridden_keys reflects the effective environment.
In `@scripts/lib/smoke-evidence.ps1`:
- Around line 149-153: Update the listener evidence flow around
Get-PlatformTcpListenerPid to verify that the discovered listener is bound to
BindAddress before reporting the host; extend the platform adapter with
endpoint-address lookup if available, otherwise omit host from the result
whenever the address cannot be confirmed.
In `@scripts/lib/windows-verification-scope.ps1`:
- Around line 28-44: Extend the tier pattern sets in the Windows verification
scope configuration to cover scripts/stop-all.ps1 and
scripts/dev/rebuild-test-deploy.ps1 under tier 2, and
bim-streaming-server/scripts/start-streaming-server.ps1 under tier 3. Add
matching assertions in test-windows-verification-scope.ps1 to verify each file
maps to the intended tier.
In `@scripts/self-referential-bootstrap-ledger.json`:
- Around line 57-89: The verification_contract in the ledger declares
command_ids without corresponding repository definitions and omits the
transport/registry files from verification_mechanism_paths. Inspect the actual
command definitions and transport/registry implementations, add their exact
identifiers and paths to the ledger, then recompute contract_sha256 from the
updated declared set.
In `@scripts/stop-all.ps1`:
- Around line 20-34: Update Get-ExpectedPortListeners and the final verification
flow to preserve listeners whose Get-PlatformTcpListenerPid result is -1 as
unattributed occupied ports. Track these ports separately in an
unattributedRemaining collection, exclude them from process lookup, and include
that collection in the warning branch so the script does not report all services
stopped while such ports remain bound.
In `@scripts/tests/test-ifc-fixture-pin.mjs`:
- Around line 32-36: Update the real manifest path construction in the “real
repo manifest loads” test to use node:url’s fileURLToPath on the file URL
instead of URL.pathname and the drive-letter regex. Preserve the existing
loadManifest and assertions while ensuring spaces, non-ASCII characters, and
Windows paths are decoded correctly.
In `@scripts/tests/test-preflight-host-native.ps1`:
- Around line 230-252: Move the pip-less virtual-environment test block
beginning with New-TestSandbox above the final “ALL PASSED” Write-Host banner so
the banner is emitted only after every test completes successfully. Rename its
comment from “Test 4” to “Test 11” to avoid colliding with the existing test
numbering, while preserving the test logic and assertions.
In `@scripts/tests/test-rebuild-test-deploy.ps1`:
- Around line 52-58: Change the Set-Content encoding in the sandbox registry
write within New-DeployEdgeVolumeHarness to UTF-8 instead of ASCII, preserving
non-ASCII characters in DeployRoot and PowerShell 7+ compatibility.
In `@scripts/verify-runtime-kit-launcher.ps1`:
- Around line 122-127: Apply the guarded-load pattern at both affected sites: in
scripts/verify-runtime-kit-launcher.ps1, load lib/preflight-host-native.ps1
before calling Test-PlatformTcpListening; in
scripts/lib/host-native-launcher.ps1, guard lib/preflight-host-native.ps1 before
Get-HostNativeBindHost calls Get-DeployTargetForCurrentPlatform. Reuse the
existing guarded-loading conventions and avoid duplicate dependency loads.
---
Outside diff comments:
In `@scripts/deploy.ps1`:
- Around line 1141-1154: The Phase 2 Kit build currently ignores the configured
$hostNative.kitBuildCommand and hard-codes repo.bat in its status messages.
Update the Invoke-KitRepoBuild call to use the resolved registry command, fail
clearly if the command is unavailable before targeting Linux over SSH, and
revise the timeout and failure messages to report $hostNative.kitBuildCommand
instead of repo.bat.
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 154-155: Add a finite timeout-minutes setting to the job
containing the runs-on: ubuntu-latest and steps block, matching the 30-minute
timeout used by agent-governance. Keep the existing job steps unchanged.
In `@scripts/lib/deploy-target-registry.ps1`:
- Around line 75-79: Update the required-field list in the registry validator’s
foreach loop to include host_native_bind_host, ensuring blank or missing values
are rejected during schema validation before service startup.
In `@scripts/lib/host-native-launcher.ps1`:
- Around line 8-12: Add a guarded dot-source for deploy-target-registry.ps1
alongside the existing platform-adapter.ps1 load, checking for
Get-DeployTargetForCurrentPlatform before loading it. Ensure standalone
consumers of Get-HostNativeBindHost resolve the command without reloading the
registry when it is already available.
In `@scripts/lib/ifc-fixture-pin.mjs`:
- Around line 46-54: Update the logical_name validation in the manifest.entries
loop to explicitly reject the exact names "." and ".." in addition to the
existing bare-file-name checks. Preserve the current validation and duplicate
detection behavior for all other names.
- Around line 147-152: Update the S3Client construction in headPin to configure
an explicit Node HTTP request handler with finite connection and request
timeouts, and set maxAttempts to the intended retry limit. Preserve the existing
endpoint, region, path-style, and credentials configuration.
In `@scripts/lib/platform/platform-adapter.ps1`:
- Around line 220-227: Wrap the version probe inside the candidate loop in the
function containing Get-PlatformName with try/catch, including the `& $candidate
--version` invocation and its validation. On any launch or probe error, continue
checking remaining candidates; if none succeeds, preserve the function’s
documented `return $null` behavior, including when `$ErrorActionPreference` is
`Stop`.
In `@scripts/lib/remote-deploy-transport.ps1`:
- Around line 17-20: Set $ErrorActionPreference = 'Stop' near Set-StrictMode in
the remote deploy transport library so failures from
Invoke-RemoteTestDeployRebuild operations such as Get-Content, New-Item, and
Set-Content terminate immediately. Match the established configuration used by
the sibling libraries without changing the function’s other behavior.
In `@scripts/tests/test-deploy-governance-static.ps1`:
- Around line 85-87: Update the depends_on validation in the host-kit compose
check to inspect only the coordinator service block, ensuring its
coordinator-specific depends_on entry contains !override []. Do not allow a
matching entry in another service to satisfy the guard, and preserve the
existing failure message.
In `@scripts/tests/test-deploy-target-registry.ps1`:
- Around line 58-84: Update the negative-test mutations in the Assert-Throws
cases to locate targets by their kind, matching the existing kind-based
resolution near lines 93-94, instead of relying on targets[0] or targets[1].
Apply the appropriate Windows or Linux target lookup for duplicate IDs, reserved
kind, F-1, F-2, SSH user, root-path, and canonical-role mutations so each
assertion continues testing its named invariant after target reordering or
additions.
In `@scripts/tests/test-ifc-fixture-pin.mjs`:
- Around line 104-109: Update the test around evaluateCacheSidecar to make the
sha256 digest load-bearing: provide or configure observed cached bytes, then add
an assertion that a sidecar with a mismatched sha256 returns stale_cache.
Preserve the existing valid-digest and drifted-etag coverage while ensuring the
test no longer passes without comparing sidecar.sha256.
In `@scripts/tests/test-platform-adapter.ps1`:
- Around line 31-35: Update the child process creation in the platform adapter
test to use the currently running PowerShell host executable rather than
hardcoding `pwsh` in either `Start-Process` branch. Preserve the existing
arguments and Windows-specific `-WindowStyle Hidden` behavior.
- Around line 56-67: Declare and initialize $port before the try block in the
TCP listener ownership test, then assign the actual listener port inside try as
currently done. Preserve the post-finally null-owner assertion while ensuring a
failure before assignment does not trigger an unassigned-variable error under
strict mode.
In `@scripts/tests/test-remote-deploy-transport.ps1`:
- Around line 99-113: Add tests in the bootstrap authorization section using a
temporary ledger file to cover Assert-BootstrapRefAllowed: reject BootstrapRef
without BootstrapLedgerEntry, reject BootstrapLedgerEntry without BootstrapRef,
reject entries whose status is not open, and reject entries missing
scripts/deploy.ps1 from verification_mechanism_paths. Also add a successful case
confirming New-RemoteRebuildScript with BootstrapRef resets to
refs/remotes/origin/<ref>, cleaning up the temporary ledger afterward.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 69ed2dd3-731a-4afd-a7eb-7a8d5304fba4
📒 Files selected for processing (41)
.github/PULL_REQUEST_TEMPLATE.md.github/workflows/agent-governance.yml.github/workflows/ci.ymlartifacts/deploy-reports/remote-linux-181/20260803T053658Z-effective-env.jsonbim-streaming-server/scripts/start-streaming-server.ps1compose.host-kit.ymldocs/agents/product-operability-and-script-contract.mddocs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.mddocs/evidence/remote-linux-deploy-target/self-referential-bootstrap/deploy-verified.txtdocs/plans/remote-linux-test-deploy-target.plan.mdscripts/deploy-target-registry.jsonscripts/deploy.ps1scripts/dev/find-deploy-blockers.ps1scripts/dev/provision-linux-deploy-target.shscripts/dev/rebuild-test-deploy.ps1scripts/dev/run-runtime-command-authority-host-native-evidence.ps1scripts/ifc-fixture-manifest.jsonscripts/lib/deploy-target-registry.ps1scripts/lib/host-native-launcher.ps1scripts/lib/ifc-fixture-pin.mjsscripts/lib/kit-log-probe.ps1scripts/lib/platform/platform-adapter.ps1scripts/lib/preflight-host-native.ps1scripts/lib/preflight-ports.ps1scripts/lib/rebuild-test-deploy.ps1scripts/lib/remote-deploy-transport.ps1scripts/lib/smoke-evidence.ps1scripts/lib/windows-verification-scope.ps1scripts/self-referential-bootstrap-ledger.jsonscripts/stop-all.ps1scripts/tests/check-pr-body-evidence.ps1scripts/tests/test-deploy-governance-static.ps1scripts/tests/test-deploy-target-registry.ps1scripts/tests/test-host-native-child-launch.ps1scripts/tests/test-ifc-fixture-pin.mjsscripts/tests/test-platform-adapter.ps1scripts/tests/test-preflight-host-native.ps1scripts/tests/test-rebuild-test-deploy.ps1scripts/tests/test-remote-deploy-transport.ps1scripts/tests/test-windows-verification-scope.ps1scripts/verify-runtime-kit-launcher.ps1
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc08b8576e
ℹ️ 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.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/deploy.ps1 (1)
56-66: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUse the selected target environment file as the default.
Get-DeployTargetForCurrentPlatformresolvesTarget.env_file, but that field is not copied into script scope. When-EnvFileis absent fromscripts/deploy.ps1,resolvedEnvFilefalls back to.env.web-plane.host-kit, bypassing the registry target’s env file such as.env.web-plane.host-kit.canonical-linux.Propagate
$script:DeployTargetProfile.env_fileand use it before the Docker/host native preflight fallbacks.🤖 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/deploy.ps1` around lines 56 - 66, The deploy script does not propagate the registry-selected env file, so the default bypasses the target profile. In the initialization block around Get-DeployTargetForCurrentPlatform, copy DeployTargetProfile.env_file into script scope, then update resolvedEnvFile selection to prefer that value when -EnvFile is not provided, before Docker or host-native fallback paths.Source: Coding guidelines
🧹 Nitpick comments (3)
scripts/tests/test-remote-deploy-transport.ps1 (1)
214-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse structured logging for the test result.
Replace this direct
Write-Hostcall with the structured logging helper fromscripts/lib/StructLog.psm1.As per coding guidelines: “Use
scripts/lib/StructLog.psm1for structured logging output; do not replace with bareWrite-Hostcalls”.🤖 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-remote-deploy-transport.ps1` at line 214, Replace the final Write-Host call in the test-remote-deploy-transport script with the structured logging helper imported from scripts/lib/StructLog.psm1, preserving the existing “all assertions passed” result message.Source: Coding guidelines
scripts/lib/deploy-target-registry.ps1 (1)
90-94: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRename
$matchesto avoid the automatic variable.
$Matchesis a PowerShell automatic variable, and PowerShell variable names are case-insensitive. The assignment at Line 90 overwrites it. The current code reads the value at Lines 91 and 94 before any-matchruns, so behaviour is correct today. A later-matchinserted between the assignment and the read would silently change the value.♻️ Proposed rename
- $matches = @($inventory.targets | Where-Object { [string]$_.id -eq [string]$Target.id }) - if ($matches.Count -ne 1) { + $mappings = @($inventory.targets | Where-Object { [string]$_.id -eq [string]$Target.id }) + if ($mappings.Count -ne 1) { throw "deploy_target_registry: private inventory must contain exactly one mapping for target '$($Target.id)'." } - $mapping = $matches[0] + $mapping = $mappings[0]🤖 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/lib/deploy-target-registry.ps1` around lines 90 - 94, Rename the local `$matches` variable in the target-mapping block to a non-reserved name, then update its `.Count` validation and `[0]` access consistently. Preserve the existing requirement that exactly one inventory target mapping is found before assigning `$mapping`.scripts/lib/remote-deploy-transport.ps1 (1)
184-192: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
DEPLOY_EXITcan only ever print0.The template sets
set -euo pipefailat Line 196. Ifdeploy.ps1 -Buildexits non-zero at Line 189, the script aborts and Line 190 never runs. The marker therefore reports success only, and a reader of the captured output can mistake it for a real exit-code record. The true exit code still reaches the caller through the ssh exit status.♻️ Proposed change
echo "== deploy.ps1 -Build ==" cd "$DEPLOY_ROOT" -pwsh -NoProfile -NonInteractive -File scripts/deploy.ps1 -Build -echo "DEPLOY_EXIT=$?" +DEPLOY_EXIT=0 +pwsh -NoProfile -NonInteractive -File scripts/deploy.ps1 -Build || DEPLOY_EXIT=$? +echo "DEPLOY_EXIT=$DEPLOY_EXIT" +exit "$DEPLOY_EXIT"🤖 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/lib/remote-deploy-transport.ps1` around lines 184 - 192, Remove the misleading DEPLOY_EXIT marker from the build command template in the $buildStep block, since set -e prevents it from running after a failed deploy.ps1 -Build. Preserve propagation of the actual deployment status through the SSH command’s exit status.
🤖 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
`@bim-streaming-server/source/extensions/ezplus.bim_review_stream.messaging/ezplus/bim_review_stream/messaging/ifc2usdc_powershell_adapter.py`:
- Around line 90-96: The constructor currently defaults powershell_exe to
powershell.exe, which breaks Linux adapters when no environment override is
provided. Update the adapter constructor to use _default_powershell_exe() as its
default, while preserving adapter_from_env() behavior and existing Windows
resolution.
In `@scripts/lib/remote-deploy-transport.ps1`:
- Around line 382-397: Update the snapshot parsing flow around $snapshot and the
effective-env markers to throw when the remote reports success but the snapshot
section is absent, instead of setting $snapshotPath to an empty string and
continuing with ExitCode 0. Preserve the existing invalid-JSON exception and
only write the report after a valid snapshot has been found.
---
Outside diff comments:
In `@scripts/deploy.ps1`:
- Around line 56-66: The deploy script does not propagate the registry-selected
env file, so the default bypasses the target profile. In the initialization
block around Get-DeployTargetForCurrentPlatform, copy
DeployTargetProfile.env_file into script scope, then update resolvedEnvFile
selection to prefer that value when -EnvFile is not provided, before Docker or
host-native fallback paths.
---
Nitpick comments:
In `@scripts/lib/deploy-target-registry.ps1`:
- Around line 90-94: Rename the local `$matches` variable in the target-mapping
block to a non-reserved name, then update its `.Count` validation and `[0]`
access consistently. Preserve the existing requirement that exactly one
inventory target mapping is found before assigning `$mapping`.
In `@scripts/lib/remote-deploy-transport.ps1`:
- Around line 184-192: Remove the misleading DEPLOY_EXIT marker from the build
command template in the $buildStep block, since set -e prevents it from running
after a failed deploy.ps1 -Build. Preserve propagation of the actual deployment
status through the SSH command’s exit status.
In `@scripts/tests/test-remote-deploy-transport.ps1`:
- Line 214: Replace the final Write-Host call in the
test-remote-deploy-transport script with the structured logging helper imported
from scripts/lib/StructLog.psm1, preserving the existing “all assertions passed”
result message.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 2c4d402a-2ab5-481b-a755-d36dc97892b1
📒 Files selected for processing (41)
.env.web-plane.host-kit.canonical-linux.example.github/workflows/agent-governance.yml.github/workflows/ci.yml.gitignorebim-streaming-server/scripts/convert-ifc-to-usdc.ps1bim-streaming-server/scripts/start-streaming-server.ps1bim-streaming-server/scripts/tests/test-convert-ifc-to-usdc.ps1bim-streaming-server/source/extensions/ezplus.bim_review_stream.messaging/ezplus/bim_review_stream/messaging/ifc2usdc_powershell_adapter.pybim-streaming-server/tests/test_host_native_conversion_service.pydocs/agents/product-operability-and-script-contract.mddocs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.mddocs/evidence/remote-linux-deploy-target/self-referential-bootstrap/deploy-verified.txtdocs/plans/remote-linux-test-deploy-target.plan.mdscripts/deploy-target-registry.jsonscripts/deploy.ps1scripts/dev/find-deploy-blockers.ps1scripts/dev/provision-linux-deploy-target.shscripts/dev/rebuild-test-deploy.ps1scripts/ifc-fixture-manifest.jsonscripts/lib/deploy-target-registry.ps1scripts/lib/host-native-launcher.ps1scripts/lib/ifc-fixture-pin.mjsscripts/lib/platform/platform-adapter.ps1scripts/lib/preflight-host-native.ps1scripts/lib/preflight-ports.ps1scripts/lib/rebuild-test-deploy.ps1scripts/lib/remote-deploy-transport.ps1scripts/lib/windows-verification-scope.ps1scripts/stop-all.ps1scripts/target.local.example.jsonscripts/tests/test-deploy-dryrun.ps1scripts/tests/test-deploy-governance-static.ps1scripts/tests/test-deploy-target-registry.ps1scripts/tests/test-host-native-launcher.ps1scripts/tests/test-ifc-fixture-pin.mjsscripts/tests/test-platform-adapter.ps1scripts/tests/test-preflight-host-native.ps1scripts/tests/test-preflight-ports.ps1scripts/tests/test-rebuild-test-deploy.ps1scripts/tests/test-remote-deploy-transport.ps1scripts/tests/test-windows-verification-scope.ps1
🚧 Files skipped from review as they are similar to previous changes (16)
- scripts/ifc-fixture-manifest.json
- scripts/dev/find-deploy-blockers.ps1
- .github/workflows/agent-governance.yml
- docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/deploy-verified.txt
- .github/workflows/ci.yml
- scripts/lib/rebuild-test-deploy.ps1
- bim-streaming-server/scripts/start-streaming-server.ps1
- docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.md
- scripts/tests/test-ifc-fixture-pin.mjs
- scripts/stop-all.ps1
- scripts/tests/test-preflight-host-native.ps1
- scripts/lib/preflight-host-native.ps1
- scripts/lib/windows-verification-scope.ps1
- scripts/lib/platform/platform-adapter.ps1
- scripts/tests/test-windows-verification-scope.ps1
- docs/agents/product-operability-and-script-contract.md
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 436f0e3116
ℹ️ 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.
Actionable comments posted: 2
🤖 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/agents/github-workflow.md`:
- Around line 144-147: Declare both changed runbooks as “Document type: runbook”
and explicitly distinguish agent instructions from runtime/product behavior
truth. In docs/agents/github-workflow.md lines 144-147 and
docs/agents/sub-repo-verify-commands.md lines 71-91, add the same declaration
and distinction without changing the existing verification guidance.
In `@scripts/lib/self-referential-bootstrap.ps1`:
- Around line 23-25: Update the remote-linux-deploy-target
verification_mechanism_paths ledger using the complete case-sensitive path list
returned by Get-SelfReferentialMechanismPaths, ensuring every changed
verification-mechanism path in this PR is declared, including the paths shown in
the diff.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 7e06b790-53c2-4a96-956f-74c40d889f76
📒 Files selected for processing (21)
.github/workflows/ci.ymlAGENTS.mddocs/agents/github-workflow.mddocs/agents/sub-repo-verify-commands.mddocs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.mdscripts/dev/provision-linux-deploy-target.shscripts/ifc-fixture-manifest.jsonscripts/lib/deploy-target-registry.ps1scripts/lib/remote-deploy-transport.ps1scripts/lib/self-referential-bootstrap.ps1scripts/lib/smoke-evidence.ps1scripts/lib/windows-verification-scope.ps1scripts/tests/check-pr-body-evidence.ps1scripts/tests/test-agent-governance-check.ps1scripts/tests/test-deploy-governance-static.ps1scripts/tests/test-deploy-target-registry.ps1scripts/tests/test-ifc-fixture-pin.mjsscripts/tests/test-remote-deploy-transport.ps1scripts/tests/test-self-referential-bootstrap.ps1scripts/tests/test-smoke-evidence.ps1scripts/tests/test-windows-verification-scope.ps1
🚧 Files skipped from review as they are similar to previous changes (11)
- scripts/ifc-fixture-manifest.json
- .github/workflows/ci.yml
- scripts/tests/check-pr-body-evidence.ps1
- docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.md
- scripts/tests/test-deploy-governance-static.ps1
- scripts/tests/test-deploy-target-registry.ps1
- scripts/tests/test-windows-verification-scope.ps1
- scripts/lib/remote-deploy-transport.ps1
- scripts/lib/deploy-target-registry.ps1
- scripts/tests/test-remote-deploy-transport.ps1
- scripts/tests/test-ifc-fixture-pin.mjs
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 4f8aa842779fa88dd7f350b17defdc9cf5855d24. This is the mechanism the GitHub App cannot satisfy: an App's approving review does not count toward required_approving_review_count.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4f8aa84277
ℹ️ 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".
| -WorkingDirectory $serviceRoot ` | ||
| -FilePath $pythonExe ` | ||
| -ArgumentList @('-m','uvicorn','app:app','--host','127.0.0.1','--port',"$Port") ` | ||
| -ArgumentList @('-m','uvicorn','app:app','--host',(Get-HostNativeBindHost -RepoRoot $RepoRoot),'--port',"$Port") ` |
There was a problem hiding this comment.
Keep Docker API bases aligned with host-native binds
When the private inventory sets host_native_bind_host to any allowed private address other than Docker's host-gateway address, governance and kit-manager bind to that address here, but the web-plane still sends the coordinator to host.docker.internal (HOST_GOVERNANCE_API_BASE is hard-coded in deploy.ps1, and KIT_MANAGER_API_BASE defaults the same way in compose.host-kit.yml). In that configuration the host health probes can pass on the bind address while the coordinator container connects to a different host IP where nothing is listening, so /api/governance/* and /api/kit/* regress to 502; either constrain the inventory value to the Docker gateway or derive the container API bases from the same resolved bind address.
Useful? React with 👍 / 👎.
| "$DEPLOY_ROOT/.windsurf" \ | ||
| "$DEPLOY_ROOT/.github/skills" \ | ||
| "$DEPLOY_ROOT/.github/prompts" \ | ||
| "$DEPLOY_ROOT/docs" \ |
There was a problem hiding this comment.
Preserve design asset sources for fresh remote builds
When this transport runs on a fresh remote deployment checkout with no ignored web-viewer-sample/public/design-assets residue, it deletes all of $DEPLOY_ROOT/docs after preserving only ai-bim-governance.css. deploy.ps1 -Build later calls Sync-DeploymentDesignAssets, whose only source dirs are docs/plans/assets and docs/plans/uploads; with those gone and no tracked prestaged manifest, the design-asset step throws before the web-plane image build. Preserve/copy the PNG asset dirs or stage the manifest alongside the CSS so first-time canonical Linux rebuilds are reproducible.
AGENTS.md reference: AGENTS.md:L47-L47
Useful? React with 👍 / 👎.
| } elseif ($bindAddress.AddressFamily -eq [Net.Sockets.AddressFamily]::InterNetworkV6) { | ||
| $bindAddress.Equals([Net.IPAddress]::IPv6Loopback) -or | ||
| (($bindBytes[0] -band 0xFE) -eq 0xFC) -or | ||
| ($bindBytes[0] -eq 0xFE -and ($bindBytes[1] -band 0xC0) -eq 0x80) -or | ||
| ($bindBytes[0] -eq 0x20 -and $bindBytes[1] -eq 0x01 -and $bindBytes[2] -eq 0x0D -and $bindBytes[3] -eq 0xB8) |
There was a problem hiding this comment.
Reject or bracket IPv6 private bind addresses
If the private inventory uses an IPv6 target-scoped bind address such as a ULA/link-local address, this branch marks it as allowed, but the deploy path later passes the value through Resolve-HostNameOnly, which rejects unbracketed hosts containing : as if they included a port; bracketed IPv6 is also impossible because the inventory shape regex rejects leading [. The result is an inventory that passes registry validation and then fails before starting host-native services, so either disallow IPv6 here or carry bracket-aware host formatting through the deploy health URLs.
Useful? React with 👍 / 👎.
…ked fixpoint (#472) * fix(governance): close the deploy-migration debt with its rebuild-backed fixpoint Fulfils obligation 3 for the remote-linux-deploy-target entry via the owner-directed closure procedure (plan B14): owner-authorized remote inventory provisioning, a NORMAL rebuild with no BootstrapRef through the private-inventory path (remote exit=0; coordinator/viewer/Kit/conversion/ governance/kit-manager all verified, snapshot semantics re-derived: 34 keys), then the ledger's ordered 12-command verification_contract, every command EXIT=0, attested per self-referential-fixpoint-attestation/v1 against the unchanged opening contract digest. mechanism_commit = 591f930 (PR #467's squash, first-parent mainline, subject binds #467). Closure is single-purpose: within the mechanism surface this commit touches only the ledger; evidence lives outside it. The fixpoint's first live run surfaced a deterministic transcript-parse defect in the transport wrapper (snapshot written without trailing newline, end marker fused, regex can never match) AFTER the remote deployment had succeeded. The debt gate correctly forbids fixing a mechanism file before closure; per owner ruling (option A) the rebuild is adjudged passed on the remote exit code plus independent verification, the defect is recorded in the evidence summary, and its fix is the first mechanism PR after this closure. Supersedes #471 (suites-only, closed un-merged). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV * docs(evidence): declare document nature; name the runtime transport-lib copy precisely Addresses the CodeRabbit document-nature guideline and Copilot's observation that no repo file is named transport-lib.ps1: the snapshot re-derivation ran through the transport-pushed runtime copy at <runtime_data_root>/transport-lib.ps1, which the operator dispatch ships from scripts/lib/remote-deploy-transport.ps1 so both sides share one merge implementation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV * fix(governance): restore the closure that 3b05eb3 erroneously reverted 3b05eb3 was my error, twice over: its message claimed the review-thread edits while containing none of them, and it silently committed a stale PRE-closure ledger (status open, fixpoint null) that was sitting in the index after an aborted edit script - the chain lacked an && guard and the only pre-commit check run was whitespace-level. That commit un-closed the entry this PR exists to close. This commit makes the tree state true again: - ledger restored byte-for-byte to the e631d97 closure (status closed, fixpoint bound to mechanism_commit 591f930 with the attestation + summary refs) - the two review edits actually applied to fixpoint/summary.md: the document nature is declared (evidence), and the snapshot re-derivation names the transport-pushed runtime copy at <runtime_data_root>/transport-lib.ps1 precisely (Copilot: no repo file bears that name; correct - the operator dispatch ships scripts/lib/remote-deploy-transport.ps1 there). History is not rewritten; 3b05eb3 remains on the branch with this correction after it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…nd B14 closure record (#473) * feat(deploy): tag every successful deployment (plan B13) Owner directive (2026-08-05): deployments to the canonical target must be tagged, name format 日期+timer ticker+序號. - Get-DeployTagName: pure builder, deploy-<yyyyMMdd>-<UtcTicks>-<NNN>, sequence 1..999 enforced. - New-RemoteDeployTag: annotated tag on the commit the TARGET actually checked out (parsed from the remote transcript's "HEAD is now at", resolved to the full sha operator-side), sequence from the day's existing tags after a tags fetch, collision retries with the next number, and a failed push is a hard error - a tag origin never saw would silently lie. Tag name and message carry no host/account/network detail (policy A). Git is driven only through an injectable runner. - Invoke-RemoteTestDeployRebuild tags on EXIT=0 non-dry-run only, returns DeployTag; an unresolvable deployed sha downgrades to a warning rather than failing a deployment that succeeded. - plan B13 recorded; tests cover the exact name shape, sequence bounds, collision retry, push discipline, and sha validation. The existing dispatch fixture already exercises the no-sha downgrade path. Verified: test-remote-deploy-transport passes (including the pre-existing assignments); lib parses clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV * docs(plan): record the owner-directed fixpoint closure procedure (B14) Supersedes the suites-only closure attempt (#471, closed): the ledger entry stays open until an actual remote rebuild through the private-inventory path plus the ordered 12-command contract all pass. Remote target.local.json is owner-provisioned out-of-band only - transport never uploads private topology. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GV5yjuGdAgJ9PYPRam6biV * fix(deploy): keep the env snapshot end marker on its own line (F-17) The compressed snapshot JSON is written without a trailing newline, so the remote template's cat glued the end marker onto the JSON line; the operator-side parser never matched and every successful real rebuild was rejected as "no effective env snapshot section". The template now emits a bare echo after cat, the parse moves into ConvertFrom-DeployEnvSnapshotTranscript (single implementation), and regression tests pin the real byte shape the fake-ssh transcript cannot reproduce. Also restores the backtick-escaped newline split in New-RemoteDeployTag's sequence counting: the committed literal-newline regex never split the tag list, so the B13 collision-retry test failed at baseline. Plan records F-17 (new 4.4.2), checks off B14 with the #472 fixpoint closure, and marks #467/#472 done in the sequence table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QTVFY89rS2xRwRB2TpFP6 * fix(deploy): enforce the B13 tag contract per review round Review round (CodeRabbit + Codex connector + Copilot) on the B13 tag block: - a failed tag push now deletes the local tag before throwing (B13: never leave a local-only tag; a stale local tag would poison later sequence counts) - bootstrap rebuilds are never tagged - their evidence is never deploy-target evidence - tagging is restricted to the canonical target (role canonical_test_deploy, taken from the in-hand target object) and, once eligible, mandatory: a missing or unresolvable deployed sha is a hard error after one fetch retry, not a silently skipped record - the successful tag path is covered end-to-end by shadowing git next to the fake ssh (asserts the returned DeployTag and exactly one push), and a bootstrap dispatch asserts DeployTag stays empty Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QTVFY89rS2xRwRB2TpFP6 --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…mmand (#487) * fix(deploy): stop Start-Process from shredding the Linux Kit build command The bash launch path passed the whole build command as one -c string with embedded quotes. Start-Process joins its ArgumentList into a single Arguments string and re-tokenizes it, so bash actually received only the repo.sh path as the command: repo.sh printed its usage with no arguments and exited 0, the build argument and the log redirect were silently dropped, and deploy.ps1 took the fake exit 0 as a successful build until the artifact recheck failed with a far less diagnosable message. This was latent since the Linux migration (#467) — every earlier rebuild found the deployment checkout unchanged and skipped the build phase — and first fired on the post-#484 fixpoint rebuild, which reset the checkout and cleaned _build. Write the launch command into a wrapper script instead, so the command line carries exactly one plain path argument that no platform's argument re-quoting can damage. Also fail closed when the build process exits 0 without ever creating its log file: that combination means the launch line was shredded and nothing ran. Verified on the canonical Linux host (isolated probe): repo.sh received exactly 'build', the redirect created the log, exit 0. The full canonical rebuild through this path lands with the ledger fixpoint after merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QTVFY89rS2xRwRB2TpFP6 * fix(deploy): feed the build wrapper via stdin and scope the log to this launch Review round: feed the wrapper script to bash on stdin so the command line carries no argument at all — a deploy_root with spaces has nothing left to shred. Remove any stale kit-repo-build.log before launching so the exit-0-must-have-a-log guard proves this build created it, not an earlier one. Mark the test fixture repo.sh executable on POSIX hosts where exec would otherwise fail with EACCES, and run the dynamic bash test inside a directory with spaces. Verified again on the canonical Linux host: exit 0, args seen=[build], log created, from a 'deploy root with spaces' directory. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QTVFY89rS2xRwRB2TpFP6 * fix(deploy): escape shell metacharacters in the wrapper's embedded paths The registry accepts deploy_root values containing $ and backtick; embedding those raw inside the wrapper's double-quoted sh strings lets the shell perform parameter/command substitution on the path, so exec or the log redirect targets a different location. Escape the four double-quote-special characters when composing the wrapper, and run Test 15c from a directory carrying spaces, $, and a backtick (fails with exit 127 without the escaping). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Gn3PJQ96Krb3adAErpXGu * test(deploy): make launcher regressions portable on Windows * fix(deploy): fail closed on stale build logs --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…#570) * fix(governance): cover scripts/lib modules in the Windows deploy tier tier-2 (deploy_dryrun) 的 pattern 只比對 scripts/lib 底下的 .ps1,漏掉 .psm1: '^scripts/lib/(?!platform/)[^/]+\.ps1$' 後果是 scripts/lib/rebuild-test-deploy.ps1(tier 2)import 的 scripts/lib/StructLog.psm1 落在 tier 0——deploy 函式庫自己的依賴不欠任何 Windows 證據。以 origin/main 的 checker 原件實測確認(見 evidence)。 判定為疏漏而非刻意排除: - StructLog.psm1 於 2026-05-27 (#126) 就存在,pattern 是 2026-08-05 (#467) 才寫,撰寫時 .psm1 已在該目錄。 - test-windows-verification-scope.ps1 自述涵蓋 deliberate exclusions, 但全檔未出現 psm1 或 module。 - 該 pattern 本來就把 scripts/lib 底下每個非 platform 的 .ps1 都當 deploy 函式庫(含 design-system-gate、pr-review-agent 等非 deploy 檔),設計本身 接受過度涵蓋;唯一逃掉的偏偏是 module 型別。 改為 '\.psm?1$',並補三組測試:StructLog.psm1 必須落 deploy_dryrun、 platform/ 底下的 .psm1 仍是 tier 1、.psd1 與巢狀路徑不得被收進來。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(governance): open the windows-tier-lib-modules bootstrap debt (PR #570) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(governance): declare the open entry's explicit null fixpoint Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: PR Body Evidence Test <pr-body-evidence@example.invalid>
…) (#622) - 重建指令補上 -TargetId local-windows:自 #467 起 registry canonical_target=canonical-linux,缺此旗標會誤走 SSH remote 而非重建本機 local-windows 部署(比照 docs/agents/sub-repo-verify-commands.md 已驗證寫法)。 - 改寫 dirty deployment / HEAD 差異 / broad ACL 段落:自 #591 起 local-windows 是 development_verification role,這些條件不再 fail-closed,改記錄為 integrity_notes 後繼續執行;只有 canonical_test_deploy role 才 fail-closed。明確標註此路徑產出 evidence_class=development_verification,不得引為 delivery-grade proof。 Co-authored-by: PR Body Evidence Test <pr-body-evidence@example.invalid> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
What
Adds a canonical remote-Linux test-deploy path while keeping private target
topology outside the public repository.
scripts/deploy-target-registry.jsonremains schema v1 and now describes only public behavior; an owner-controlled,
repo-external
target.local.jsonsupplies the host, account, network mapping,and deploy/runtime roots. The canonical deploy/rebuild path dispatches Windows
and Linux behavior through explicit platform adapters.
This PR is now retargeted to
mainand includes the completed dependency chain:ad7a50c)01098ef)3c8e761)origin/mainand closed the bounded deploy,runtime, transport, fixture, security/privacy, and Windows PowerShell 5.1
review findings
Current exact head:
4f8aa842779fa88dd7f350b17defdc9cf5855d24.Authorized one-time formal-preflight exception
Formal local preflight is currently fail-closed.
origin/mainalready containsthe open
remote-linux-deploy-targetentry because it was carried into the #459squash before #467 merged. The current checker therefore rejects both legal body
declarations:
yesis rejected because feat(deploy): migrate the persistent test deploy area to remote Linux (PR-B) #467 does not add the entry relative to its base;nois rejected because the same open debt blocks mechanism-changing PRs.The append-only ledger cannot delete the orphan, and a valid closure requires
#467's merge commit to already be on the base first-parent history. On
2026-08-05 the operator explicitly authorized a one-time exception for this
orphan-ledger formal-preflight deadlock only: normal exact-head approval and
protected merge remain required, with no admin bypass, force-push, or branch
protection change. Post-merge fixpoint closure must follow immediately from a
freshly fetched
origin/main.Exact-head repair
The final bounded repair keeps production fail-closed behavior and adds these
review closures:
scripts/deploy.ps1as UTF-8 with BOM for Windows PowerShell 5.1;revision-signature, stop-all, and precise legacy-cleanup gaps;
repo-external private inventory and validates its schema, paths, and network
fields;
transport; Codex/transport never uploads or overwrites it;
to the published/connection endpoint, and rejects override-only env keys;
while preserving the production CSS dependency and GitHub workflows;
including unknown key names, and persists only the redacted marker payload;
and bootstrap evidence while retaining result-only verification statements;
machine-checkable GitHub Actions run URL; and
pwsh, fails closedwhen a successful remote rebuild omits its redacted env snapshot, and audits
host-native kit-manager port 8010 before launch and again at Phase 3;
surface and classifies registry/conversion-launch changes into the applicable
Windows verification tier without mutating the immutable open ledger entry;
7 and Windows PowerShell 5.1.
The existing
local-windowsregistry contract is intentionally unchanged.Current local verification
the authority for the PowerShell 5.1 half
PowerShell 7 and Windows PowerShell 5.1 -> PASS
child launch, host-native/port preflight, Kit log probe, and Windows
verification-scope contracts -> PASS
git diff --check-> PASSbash -nand non-mutating--dry-run-> PASSpytest -> 3 PASS / 74 deselected
GitNexus exact-worktree impact/detect_changes is unavailable: this linked
worktree has no
.gitnexus/run.cjs; the global runner found multiple indexes,none for this sibling worktree. No unrelated or stale index is presented as a
pass.
Existing bootstrap evidence
The tracked self-referential bootstrap record is now de-identified. It retains
only result-level evidence: four independently detached host-native services,
nine successful endpoint probes, a listening headless Kit stream with GPU
runtime present, and a passing Linux platform-adapter contract. It contains no
private host, account, deploy path, endpoint, PID, or hardware identifier.
Evidence:
docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/deploy-verified.txt.The current exact head has not been redeployed as an unmerged branch: the
canonical contract only rebuilds the test deployment from freshly fetched
origin/main. Post-merge fixpoint verification must close the openremote-linux-deploy-targetledger entry.Review and governance
docs/plans/remote-linux-test-deploy-target.plan.mdsection 6docs/plans/remote-linux-test-deploy-target.plan.md(approved 2026-07-31)remote-linux-deploy-targetorigin/main, so the changed canonical deploy path cannot receive its post-merge verification before merge.scripts/deploy.ps1updated.\scripts\deploy.ps1 -DryRunorigin/mainrebuild.\scripts\verify-all.ps1