Skip to content

feat(deploy): migrate the persistent test deploy area to remote Linux (PR-B) - #467

Merged
monkey1sai merged 74 commits into
mainfrom
feat/remote-linux-deploy-target
Aug 5, 2026
Merged

monkey1sai merged 74 commits into
mainfrom
feat/remote-linux-deploy-target

Conversation

@monkey1sai

@monkey1sai monkey1sai commented Aug 3, 2026 •

Copy link
Copy Markdown
Owner

What

Adds a canonical remote-Linux test-deploy path while keeping private target
topology outside the public repository. scripts/deploy-target-registry.json
remains schema v1 and now describes only public behavior; an owner-controlled,
repo-external target.local.json supplies 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 main and includes the completed dependency chain:

  1. feat(governance): self-referential bootstrap ledger and debt gate (PR-A) #459 merged (ad7a50c)
  2. bootstrap fixpoint fix(governance): close the bootstrap gate's debt with its fixpoint (B12) #470 merged (01098ef)
  3. governance trust closure fix(governance): close AI coding trust gaps #469 merged (3c8e761)
  4. this branch merged current origin/main and 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/main already contains
the open remote-linux-deploy-target entry because it was carried into the #459
squash before #467 merged. The current checker therefore rejects both legal body
declarations:

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:

  • preserves scripts/deploy.ps1 as UTF-8 with BOM for Windows PowerShell 5.1;
  • closes cross-platform conversion, Kit launch, custom-port, Compose ownership,
    revision-signature, stop-all, and precise legacy-cleanup gaps;
  • validates the IFC fixture over HTTPS with its pinned SHA before cache reuse;
  • makes canonical Linux selection fail closed without an absolute,
    repo-external private inventory and validates its schema, paths, and network
    fields;
  • requires owner/provisioning to create the mode-0600 remote inventory before
    transport; Codex/transport never uploads or overwrites it;
  • rejects private-inventory bind addresses that are public, wildcard, or equal
    to the published/connection endpoint, and rejects override-only env keys;
  • removes tracked agent/tooling surfaces from the remote deployment checkout
    while preserving the production CSS dependency and GitHub workflows;
  • records every effective-env value only as sha256-8 fingerprint plus length,
    including unknown key names, and persists only the redacted marker payload;
  • removes the PR-added raw effective-env report and de-identifies tracked plans
    and bootstrap evidence while retaining result-only verification statements;
  • binds Windows verification declarations to the exact reviewed SHA and a
    machine-checkable GitHub Actions run URL; and
  • resolves direct Linux PowerShell construction through pwsh, fails closed
    when a successful remote rebuild omits its redacted env snapshot, and audits
    host-native kit-manager port 8010 before launch and again at Phase 3;
  • classifies the Windows evidence resolver as a self-adjudicating bootstrap
    surface and classifies registry/conversion-launch changes into the applicable
    Windows verification tier without mutating the immutable open ledger entry;
  • adds placeholder-only public examples and regression coverage for PowerShell
    7 and Windows PowerShell 5.1.

The existing local-windows registry contract is intentionally unchanged.

Current local verification

  • full rebuild harness: PowerShell 7 -> PASS; the exact-head CI Windows job is
    the authority for the PowerShell 5.1 half
  • deploy-target registry: PowerShell 7 and Windows PowerShell 5.1 -> PASS
  • remote transport, including fake-SSH live dispatch / redacted report:
    PowerShell 7 and Windows PowerShell 5.1 -> PASS
  • deploy dry-run, governance static, platform adapter, host-native launcher and
    child launch, host-native/port preflight, Kit log probe, and Windows
    verification-scope contracts -> PASS
  • conversion PowerShell contract -> PASS
  • IFC fixture pin Node contract -> 16/16 PASS
  • host-native conversion service Python suite -> 76 PASS
  • changed PowerShell AST parse -> 25 files PASS
  • PowerShell static suite and git diff --check -> PASS
  • provisioning script bash -n and non-mutating --dry-run -> PASS
  • direct-constructor PowerShell resolver contract -> PASS; independent focused
    pytest -> 3 PASS / 74 deselected
  • tracked private-literal sweep and deploy UTF-8 BOM guard -> PASS

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 open
remote-linux-deploy-target ledger entry.

Review and governance

Item Result
Change lane G
Behavior contract changed yes
Linked issue none - plan-approved work item: docs/plans/remote-linux-test-deploy-target.plan.md section 6
Requirement source docs/plans/remote-linux-test-deploy-target.plan.md (approved 2026-07-31)
CODEOWNERS / owner review requested; exact-head approval required
GitNexus evidence unavailable in this linked worktree; direct source, executable contracts, and exact-head CI are the fallback evidence
Browser E2E evidence not user-facing
Agent workflow changed? yes: Windows verification-tier classifier and deploy-target registry; rollback is revert of this PR
Required checks expected CI / Agent Governance / PR Metadata Contract; Governance Base Audit is diagnostic
Self-referential bootstrap yes
Bootstrap ledger entry remote-linux-deploy-target
Bootstrap reason The deploy contract rebuilds only from freshly fetched origin/main, so the changed canonical deploy path cannot receive its post-merge verification before merge.
Item Result
Affects runtime / docker / Kit / viewer / ports / env? yes
Canonical deploy path updated? scripts/deploy.ps1 updated
New root script added? no
Deploy dry-run command .\scripts\deploy.ps1 -DryRun
Full deploy tested existing de-identified bootstrap evidence only; exact current head awaits the required post-merge origin/main rebuild
Verify command .\scripts\verify-all.ps1
Frontend URL verified existing de-identified evidence records a successful UI probe; private URL intentionally omitted
Windows verification tier kit_gpu
Windows verification evidence reviewed head 4f8aa84; local PowerShell 7 contracts passed; exact-head Windows PowerShell 5.1/rebuild contract authority: https://github.com/monkey1sai/AI-BIM-governance/actions/runs/30983398428 ; full local-Windows GPU/WebRTC/conversion execution was not observed because Plan A keeps local-windows explicitly on-demand/out of scope, so no full Windows runtime pass is claimed

monkey1sai and others added 30 commits July 31, 2026 17:43
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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 lift

Use the target registry Kit build command instead of the Windows launcher default.

Phase 2 always reports $hostNative.kitBuildCommand, but Invoke-KitRepoBuild still uses its Windows cmd.exe/repo.bat default. For targets like remote-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 naming repo.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 win

Spawn the current PowerShell host instead of assuming pwsh exists.

Both branches start pwsh. The Windows deploy target runs Windows PowerShell 5.1 in several paths, and pwsh is not guaranteed to be installed there. Start-Process then 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 value

Declare $port before the try block.

$port is assigned at Line 60 inside try, and Line 67 reads it after the finally. Set-StrictMode -Version Latest is 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 win

Validate host_native_bind_host in the registry schema.

Get-HostNativeBindHost in scripts/lib/host-native-launcher.ps1 (Lines 81-86) requires host_native_bind_host on 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 win

Add a guarded load for deploy-target-registry.ps1.

Get-HostNativeBindHost (Line 81) calls Get-DeployTargetForCurrentPlatform, which is defined in scripts/lib/deploy-target-registry.ps1. This file only loads platform/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 win

Wrap the version probe in try/catch to keep the documented "returns $null" contract.

The comment states the function returns $null when no candidate works. Get-Command can resolve a candidate that fails to launch. In that case & $candidate --version raises an error record. Callers that set $ErrorActionPreference = 'Stop' (for example scripts/tests/test-platform-adapter.ps1 Line 9) then terminate instead of receiving $null. The sibling probes in scripts/lib/preflight-host-native.ps1 already 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 win

Select mutation targets by kind, not by array index.

These negative tests assume targets[0] is the Windows target and targets[1] is the Linux target. If a target is added or reordered in scripts/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 by kind; 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_name accepts . and ...

The regex rejects separators, so traversal is not possible from this value alone. A consumer that joins logical_name to 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 win

Set explicit S3 HTTP timeouts and retries in headPin.

This S3Client runs 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 explicit requestHandler timeouts and maxAttempts so 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 win

Add timeout-minutes to the job.

The agent-governance job sets timeout-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 win

This test pins the sidecar gap in place.

Line 106 builds a sidecar with 'a'.repeat(64) as the downloaded-bytes digest and asserts cache_valid. No cached file exists in the test, and no digest is compared. The assertion therefore confirms that evaluateCacheSidecar ignores sidecar.sha256, which is the defect raised on scripts/lib/ifc-fixture-pin.mjs lines 98-128.

When you make the digest load-bearing, add a case that asserts stale_cache for a sidecar whose sha256 does 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 win

Scope the depends_on check 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 carries depends_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 win

Set $ErrorActionPreference = 'Stop' in this library.

The file sets Set-StrictMode -Version Latest but leaves the error preference at the caller's value. Invoke-RemoteTestDeployRebuild performs Get-Content, New-Item, and Set-Content calls whose failures are non-terminating by default. If a caller runs with Continue, the function proceeds after a failed read and then evaluates $LASTEXITCODE, which still holds the value of an earlier native command. The sibling libraries scripts/lib/rebuild-test-deploy.ps1 and scripts/tests/test-remote-deploy-transport.ps1 both set Stop.

♻️ 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 win

Add coverage for the bootstrap authorization guard.

Assert-BootstrapRefAllowed is 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: -BootstrapRef without -BootstrapLedgerEntry, -BootstrapLedgerEntry without -BootstrapRef, a ledger entry whose status is not open, and an entry that omits scripts/deploy.ps1 from verification_mechanism_paths. A positive case should also confirm that New-RemoteRebuildScript -BootstrapRef emits refs/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

📥 Commits

Reviewing files that changed from the base of the PR and between 3c8e761 and cc08b85.

📒 Files selected for processing (41)
  • .github/PULL_REQUEST_TEMPLATE.md
  • .github/workflows/agent-governance.yml
  • .github/workflows/ci.yml
  • artifacts/deploy-reports/remote-linux-181/20260803T053658Z-effective-env.json
  • bim-streaming-server/scripts/start-streaming-server.ps1
  • compose.host-kit.yml
  • docs/agents/product-operability-and-script-contract.md
  • docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.md
  • docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/deploy-verified.txt
  • docs/plans/remote-linux-test-deploy-target.plan.md
  • scripts/deploy-target-registry.json
  • scripts/deploy.ps1
  • scripts/dev/find-deploy-blockers.ps1
  • scripts/dev/provision-linux-deploy-target.sh
  • scripts/dev/rebuild-test-deploy.ps1
  • scripts/dev/run-runtime-command-authority-host-native-evidence.ps1
  • scripts/ifc-fixture-manifest.json
  • scripts/lib/deploy-target-registry.ps1
  • scripts/lib/host-native-launcher.ps1
  • scripts/lib/ifc-fixture-pin.mjs
  • scripts/lib/kit-log-probe.ps1
  • scripts/lib/platform/platform-adapter.ps1
  • scripts/lib/preflight-host-native.ps1
  • scripts/lib/preflight-ports.ps1
  • scripts/lib/rebuild-test-deploy.ps1
  • scripts/lib/remote-deploy-transport.ps1
  • scripts/lib/smoke-evidence.ps1
  • scripts/lib/windows-verification-scope.ps1
  • scripts/self-referential-bootstrap-ledger.json
  • scripts/stop-all.ps1
  • scripts/tests/check-pr-body-evidence.ps1
  • scripts/tests/test-deploy-governance-static.ps1
  • scripts/tests/test-deploy-target-registry.ps1
  • scripts/tests/test-host-native-child-launch.ps1
  • scripts/tests/test-ifc-fixture-pin.mjs
  • scripts/tests/test-platform-adapter.ps1
  • scripts/tests/test-preflight-host-native.ps1
  • scripts/tests/test-rebuild-test-deploy.ps1
  • scripts/tests/test-remote-deploy-transport.ps1
  • scripts/tests/test-windows-verification-scope.ps1
  • scripts/verify-runtime-kit-launcher.ps1

Comment thread bim-streaming-server/scripts/start-streaming-server.ps1
Comment thread docs/plans/remote-linux-test-deploy-target.plan.md
Comment thread scripts/deploy.ps1
Comment thread scripts/dev/find-deploy-blockers.ps1
Comment thread scripts/stop-all.ps1
Comment thread scripts/tests/test-ifc-fixture-pin.mjs
Comment thread scripts/tests/test-preflight-host-native.ps1 Outdated
Comment thread scripts/tests/test-rebuild-test-deploy.ps1 Outdated
Comment thread scripts/verify-runtime-kit-launcher.ps1

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread scripts/deploy.ps1 Outdated
Comment thread scripts/self-referential-bootstrap-ledger.json
Comment thread scripts/deploy-target-registry.json Outdated
Comment thread compose.host-kit.yml

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Use the selected target environment file as the default.

Get-DeployTargetForCurrentPlatform resolves Target.env_file, but that field is not copied into script scope. When -EnvFile is absent from scripts/deploy.ps1, resolvedEnvFile falls 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_file and 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 win

Use structured logging for the test result.

Replace this direct Write-Host call with the structured logging helper from scripts/lib/StructLog.psm1.

As per coding guidelines: “Use scripts/lib/StructLog.psm1 for structured logging output; do not replace with bare Write-Host calls”.

🤖 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 value

Rename $matches to avoid the automatic variable.

$Matches is 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 -match runs, so behaviour is correct today. A later -match inserted 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_EXIT can only ever print 0.

The template sets set -euo pipefail at Line 196. If deploy.ps1 -Build exits 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

📥 Commits

Reviewing files that changed from the base of the PR and between cc08b85 and 436f0e3.

📒 Files selected for processing (41)
  • .env.web-plane.host-kit.canonical-linux.example
  • .github/workflows/agent-governance.yml
  • .github/workflows/ci.yml
  • .gitignore
  • bim-streaming-server/scripts/convert-ifc-to-usdc.ps1
  • bim-streaming-server/scripts/start-streaming-server.ps1
  • bim-streaming-server/scripts/tests/test-convert-ifc-to-usdc.ps1
  • bim-streaming-server/source/extensions/ezplus.bim_review_stream.messaging/ezplus/bim_review_stream/messaging/ifc2usdc_powershell_adapter.py
  • bim-streaming-server/tests/test_host_native_conversion_service.py
  • docs/agents/product-operability-and-script-contract.md
  • docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.md
  • docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/deploy-verified.txt
  • docs/plans/remote-linux-test-deploy-target.plan.md
  • scripts/deploy-target-registry.json
  • scripts/deploy.ps1
  • scripts/dev/find-deploy-blockers.ps1
  • scripts/dev/provision-linux-deploy-target.sh
  • scripts/dev/rebuild-test-deploy.ps1
  • scripts/ifc-fixture-manifest.json
  • scripts/lib/deploy-target-registry.ps1
  • scripts/lib/host-native-launcher.ps1
  • scripts/lib/ifc-fixture-pin.mjs
  • scripts/lib/platform/platform-adapter.ps1
  • scripts/lib/preflight-host-native.ps1
  • scripts/lib/preflight-ports.ps1
  • scripts/lib/rebuild-test-deploy.ps1
  • scripts/lib/remote-deploy-transport.ps1
  • scripts/lib/windows-verification-scope.ps1
  • scripts/stop-all.ps1
  • scripts/target.local.example.json
  • scripts/tests/test-deploy-dryrun.ps1
  • scripts/tests/test-deploy-governance-static.ps1
  • scripts/tests/test-deploy-target-registry.ps1
  • scripts/tests/test-host-native-launcher.ps1
  • scripts/tests/test-ifc-fixture-pin.mjs
  • scripts/tests/test-platform-adapter.ps1
  • scripts/tests/test-preflight-host-native.ps1
  • scripts/tests/test-preflight-ports.ps1
  • scripts/tests/test-rebuild-test-deploy.ps1
  • scripts/tests/test-remote-deploy-transport.ps1
  • scripts/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

Comment thread scripts/lib/remote-deploy-transport.ps1

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread scripts/deploy.ps1
Comment thread scripts/tests/check-pr-body-evidence.ps1
Comment thread scripts/lib/windows-verification-scope.ps1
Comment thread artifacts/deploy-reports/remote-linux-181/20260803T053658Z-effective-env.json Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 436f0e3 and f78289f.

📒 Files selected for processing (21)
  • .github/workflows/ci.yml
  • AGENTS.md
  • docs/agents/github-workflow.md
  • docs/agents/sub-repo-verify-commands.md
  • docs/evidence/remote-linux-deploy-target/self-referential-bootstrap/README.md
  • scripts/dev/provision-linux-deploy-target.sh
  • scripts/ifc-fixture-manifest.json
  • scripts/lib/deploy-target-registry.ps1
  • scripts/lib/remote-deploy-transport.ps1
  • scripts/lib/self-referential-bootstrap.ps1
  • scripts/lib/smoke-evidence.ps1
  • scripts/lib/windows-verification-scope.ps1
  • scripts/tests/check-pr-body-evidence.ps1
  • scripts/tests/test-agent-governance-check.ps1
  • scripts/tests/test-deploy-governance-static.ps1
  • scripts/tests/test-deploy-target-registry.ps1
  • scripts/tests/test-ifc-fixture-pin.mjs
  • scripts/tests/test-remote-deploy-transport.ps1
  • scripts/tests/test-self-referential-bootstrap.ps1
  • scripts/tests/test-smoke-evidence.ps1
  • scripts/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

Comment thread docs/agents/github-workflow.md
Comment thread scripts/lib/self-referential-bootstrap.ps1

@monkey1sai-blip monkey1sai-blip left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@monkey1sai
monkey1sai merged commit 591f930 into main Aug 5, 2026
34 of 81 checks passed
@monkey1sai
monkey1sai deleted the feat/remote-linux-deploy-target branch August 5, 2026 07:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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") `

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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" \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +155 to +159
} 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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

monkey1sai added a commit that referenced this pull request Aug 5, 2026
…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>
monkey1sai added a commit that referenced this pull request Aug 5, 2026
…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>
monkey1sai added a commit that referenced this pull request Aug 11, 2026
…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>
monkey1sai added a commit that referenced this pull request Aug 17, 2026
…#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>
monkey1sai added a commit that referenced this pull request Aug 19, 2026
…) (#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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants