Deliver guard bundle to nodes
Epic 5896 · child C5a of the Architectural Guard. Full design: architecture (Task 5898 `architecture`) §5.1; PRD Task 5897 `prd`.
## Epic requirement (verbatim)
> Build a solution for this (an advanced feature of Compliance - Advanced Compliance Plan activated):
> In large enterprises, improvisation breaks design systems. Jaah can win by offering **absolute, unyielding architectural enforcement**.
> - **The Mechanism:** Instead of just checking if code passes a test, Jaah enforces rigid code blueprints, strict linting arrays, and structural enterprise design standards across thousands of repositories simultaneously.
> - **The Breakthrough Feature:** A real-time "Architectural Guard." If an agent tries to write a functioning feature but uses an unapproved API pattern or deviates from the corporate Monorepo style guide, Jaah's harness blocks the agent mid-execution, rewrites its context, and forces compliance before a single line is even committed to a branch.
## This child's requirements (PRD verbatim)
- R1.3: Turning the switch off, or Compliance off, stops enforcement in every session that starts after the change. A running session stops enforcing within 5 minutes. Stored rules and modes are kept, not deleted.
- R2.3: A rule edit (mode, scope, content) takes effect in every new session on every in-scope repo, and in running sessions within 5 minutes.
- R6.1 (delivery leg): every denying/erroring decision is recorded — decisions the hook could not send are spooled and shipped.
## Approach (env contract producer)
- `apps/jaahd/guard_bundle.py`, mirroring `apps/jaahd/project_memory_bundle.py`: fetch via `guard.bundle` within a 2 s budget; atomic file write as the session user; `refresh_step` every 60 s; 204 keeps the file; 410 empties it (hooks enforce nothing).
- Runner path: `lease_slot` payload's `guard: {project_id}` stanza (C4a) → `fleet_dispatch.launch_runner` calls `guard_bundle.prepare_launch`. Console path: console-open intent stanza → `clauded_wt.py` calls the same `prepare_launch` before `console_inner`.
- Exports: `JAAH_GUARD_BUNDLE=<_meta>/<project_id>.guard.json` (its presence is the only switch hooks read), `JAAH_GUARD_RUNTIME=<runtime>`, `JAAH_GUARD_BASE=<integration_branch>` (from the slot record), `JAAH_GUARD_STATE=<_meta>/<ref_key>.guard-state.json`.
- Ordering (pinned): `prepare_launch` runs BEFORE `fleet_launch._lifecycle_chains` / `build_settings` and passes them an explicit `guard: bool` argument (the chains never read the daemon env). Add the parameter (default False, no chain change yet — C5b wires the entries).
- Fetch miss at launch writes `{"revision": null, "error": "unfetched"}` (hook treats as undecidable → open + recorded, G-1); refresh keeps retrying.
- `guard.refresh` verb handler: runs `refresh_step` for the caller's project immediately, returns once the file is rewritten (2 s budget).
- Spool: hook appends unsent decisions to `<_meta>/<project_id>.guard-spool.jsonl`; `refresh_step` ships them via `guard.record`.
## Acceptance criteria (architecture §10 unit oracle for this child)
200 rewrites the file, 204 keeps it, 410 empties it within one 60 s refresh; `guard.refresh` rewrites on demand; the spool ships on refresh. (Feeds PRD AC-15: "turning the switch or the rule off, or changing its mode, takes effect in new sessions immediately and in running sessions within 5 minutes", proven live by C5b.)
Credentials & reach: none — the oracles are unit tests in the worktree.
Out of scope: Do NOT change any launch for an unavailable org — no stanza → no file, no env export, byte-identical launch. No per-repo setup or committed file in managed repos. Do NOT weaken or replace existing deny hooks. No hook script (C5b), no CP routes (C4a). No merge-time block.
Done means:
- Unit tests: 200 → file rewritten atomically with session-user ownership; 204 → file untouched; 410 → file emptied within one refresh; fetch miss at launch → `{"revision": null, "error": "unfetched"}` and retried; `guard.refresh` rewrites synchronously; spool lines shipped and truncated on refresh; env exports present only with a stanza; `_lifecycle_chains`/`build_settings` receive `guard=True/False` explicitly.
- `./dc check full` green; after merge, Jaah Deploy to dev succeeds.
Outcome
Guard bundle delivery shipped (guard_bundle.py: fetch, 60s refresh 200/204/410, spool, guard.refresh; runner and console paths). Full gate green, PR #5181 merged, dev Jaah Deploy succeeded. Deferred: spool task attribution, MicroVM stanza.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)