mosaic-link-runtime-assets copies runtime/claude/settings.json — which
embeds the PreToolUsemutator-gate.py hook and the Stop receipt-observer-client.py hook — straight into ~/.claude/settings.json,
unconditionally. If the ACTIVATION half of the lease broker (C1's leaseEnforcementActivatable(): a CLI build advertising launch-runtime
activation + a running broker supervisor) can't be confirmed on the host, the
fail-closed gate then denies every tool call with GATE_UNAVAILABLE — a
bricked host.
This adds the install-ordering guard: before the enforcement hooks are
wired, call leaseEnforcementActivatable() (imported directly from the C1
module, lease-activation-probe.ts) and decide whether they may be written.
The guard (packages/mosaic/src/commands/install-ordering-guard.ts)
guardClaudeSettingsWiring() / runInstallOrderingGuard() — pure,
injectable-deps functions; a hidden CLI subcommand
(mosaic __link-claude-settings <src> <dest>) bridges the bash wiring
point to this TS logic.
activatable -> wire the hooks as-is, exit 0.
NOT activatable, no opt-out (default) -> strip only the two enforcement
hook entries (every other hook — PreCompact/SessionStart revoke-lease, prevent-memory-write.sh, the PostToolUse qa/typecheck hooks, and the
sibling reflect-stop-hook.sh inside the same Stop trigger — is left
byte-identical), exit 1, print an actionable message:
enforcement requested but activation half absent — needs a published CLI
carrying launch-runtime activation + a broker supervisor; refusing to
wire a dead gate (see #869)
NOT activatable + explicit opt-out -> wire anyway, with a loud warning.
The opt-out: --allow-inactive-enforcement
A real CLI flag, threaded through install.sh -> mosaic-link-runtime-assets
-> the hidden mosaic __link-claude-settings subcommand's own --allow-inactive-enforcement option. Deliberately never an environment
variable — it can't sit as a silently-inherited default in a shell profile.
Absent (the default) => hard fail-loud path. When set on a non-activatable
host, wiring proceeds but a loud, explicit warning is always logged.
Wiring point located
packages/mosaic/framework/tools/_scripts/mosaic-link-runtime-assets is
where settings.json (and thus the enforcement hooks baked into it) actually
gets copied to ~/.claude/, called from both framework/install.sh's
post-install step and the wizard's finalize.ts. Both callers previously
swallowed stderr (2>&1 >/dev/null / stdio: 'pipe' with no surfacing) —
fixed so the guard's message actually reaches the operator instead of being
silent.
What's unchanged
mutator-gate.py's own fail-closed-on-absent-identity runtime behavior is
untouched — this guard only decides whether the hook gets wired, never
touches the gate's own denial logic. runtime_tools_unittest.py and fail-closed-regression.spec.ts remain green, unmodified.
Red-first tests
install-ordering-guard.spec.ts (19 tests): probe=false => hooks stripped,
exit 1, actionable message; probe=false + opt-out => hooks wired, loud warn,
exit 0; probe=true => hooks wired normally, no logs; file-level runInstallOrderingGuard exercised against temp dirs only (backup-on-change
parity with copy_file_managed, idempotent no-op writes) — never touches
the real ~/.claude/settings.json.
test-install-ordering-guard.sh (5 shell scenarios, added to test:framework-shell): exercises the actual bash wiring in mosaic-link-runtime-assets (fake mosaic stub + the no-mosaic-on-PATH
python3 fallback), including that every other runtime file still gets
copied when the settings.json guard degrades.
## Summary
`mosaic-link-runtime-assets` copies `runtime/claude/settings.json` — which
embeds the `PreToolUse` `mutator-gate.py` hook and the `Stop`
`receipt-observer-client.py` hook — straight into `~/.claude/settings.json`,
unconditionally. If the ACTIVATION half of the lease broker (C1's
`leaseEnforcementActivatable()`: a CLI build advertising launch-runtime
activation + a running broker supervisor) can't be confirmed on the host, the
fail-closed gate then denies every tool call with `GATE_UNAVAILABLE` — a
bricked host.
This adds the install-ordering guard: before the enforcement hooks are
wired, call `leaseEnforcementActivatable()` (imported directly from the C1
module, `lease-activation-probe.ts`) and decide whether they may be written.
### The guard (`packages/mosaic/src/commands/install-ordering-guard.ts`)
- `guardClaudeSettingsWiring()` / `runInstallOrderingGuard()` — pure,
injectable-deps functions; a hidden CLI subcommand
(`mosaic __link-claude-settings <src> <dest>`) bridges the bash wiring
point to this TS logic.
- **activatable** -> wire the hooks as-is, exit 0.
- **NOT activatable, no opt-out (default)** -> strip only the two enforcement
hook entries (every other hook — `PreCompact`/`SessionStart` revoke-lease,
`prevent-memory-write.sh`, the `PostToolUse` qa/typecheck hooks, and the
sibling `reflect-stop-hook.sh` inside the same `Stop` trigger — is left
byte-identical), exit 1, print an actionable message:
> enforcement requested but activation half absent — needs a published CLI
> carrying launch-runtime activation + a broker supervisor; refusing to
> wire a dead gate (see #869)
- **NOT activatable + explicit opt-out** -> wire anyway, with a loud warning.
### The opt-out: `--allow-inactive-enforcement`
A real CLI flag, threaded through `install.sh` -> `mosaic-link-runtime-assets`
-> the hidden `mosaic __link-claude-settings` subcommand's own
`--allow-inactive-enforcement` option. Deliberately **never** an environment
variable — it can't sit as a silently-inherited default in a shell profile.
Absent (the default) => hard fail-loud path. When set on a non-activatable
host, wiring proceeds but a loud, explicit warning is always logged.
### Wiring point located
`packages/mosaic/framework/tools/_scripts/mosaic-link-runtime-assets` is
where `settings.json` (and thus the enforcement hooks baked into it) actually
gets copied to `~/.claude/`, called from both `framework/install.sh`'s
post-install step and the wizard's `finalize.ts`. Both callers previously
swallowed stderr (`2>&1 >/dev/null` / `stdio: 'pipe'` with no surfacing) —
fixed so the guard's message actually reaches the operator instead of being
silent.
### What's unchanged
`mutator-gate.py`'s own fail-closed-on-absent-identity runtime behavior is
untouched — this guard only decides whether the hook gets **wired**, never
touches the gate's own denial logic. `runtime_tools_unittest.py` and
`fail-closed-regression.spec.ts` remain green, unmodified.
## Red-first tests
- `install-ordering-guard.spec.ts` (19 tests): probe=false => hooks stripped,
exit 1, actionable message; probe=false + opt-out => hooks wired, loud warn,
exit 0; probe=true => hooks wired normally, no logs; file-level
`runInstallOrderingGuard` exercised against temp dirs only (backup-on-change
parity with `copy_file_managed`, idempotent no-op writes) — never touches
the real `~/.claude/settings.json`.
- `test-install-ordering-guard.sh` (5 shell scenarios, added to
`test:framework-shell`): exercises the actual bash wiring in
`mosaic-link-runtime-assets` (fake `mosaic` stub + the no-`mosaic`-on-PATH
python3 fallback), including that every *other* runtime file still gets
copied when the settings.json guard degrades.
## Test plan
- [x] `pnpm typecheck` (via turbo, `--filter=@mosaicstack/mosaic`) — pass
- [x] `pnpm lint` (via turbo, `--filter=@mosaicstack/mosaic`) — pass
- [x] `pnpm format:check` — pass
- [x] `pnpm --filter @mosaicstack/mosaic test` (vitest + `test:framework-shell`,
including the new shell regression) — pass, 1484+ tests
- [x] `fail-closed-regression.spec.ts` / `runtime_tools_unittest.py` — unchanged, green
- [x] Full-repo typecheck/lint/format:check via the pre-push hook — pass
Part of #869 (Point-1 C2)
mosaic-link-runtime-assets wired the PreToolUse mutator-gate.py and Stop
receipt-observer-client.py hooks into ~/.claude/settings.json unconditionally.
If the activation half (C1's leaseEnforcementActivatable()) can't be
confirmed on the host, the fail-closed gate then denies every tool call —
bricking it.
Add a TS install-ordering guard (guardClaudeSettingsWiring /
runInstallOrderingGuard, hidden CLI bridge `mosaic __link-claude-settings`)
that imports leaseEnforcementActivatable() directly and decides whether the
enforcement hooks get wired:
- activatable -> wire as-is.
- NOT activatable, no opt-out -> strip the enforcement hooks, exit
non-zero with an actionable message
(default, fail-loud).
- NOT activatable + explicit
--allow-inactive-enforcement -> wire anyway, loud warning logged.
--allow-inactive-enforcement is a real CLI flag threaded through
install.sh -> mosaic-link-runtime-assets -> the hidden subcommand —
deliberately never an environment variable, so it can't sit as a
silently-inherited default. Wire mosaic-link-runtime-assets's guarded
settings.json copy to call out to the CLI (with a python3 fallback if
`mosaic` isn't resolvable at all), and stop swallowing stderr in
install.sh/finalize.ts so the guard's message actually reaches the operator.
mutator-gate.py's own fail-closed-on-absent-identity runtime behavior is
untouched (this only gates the WIRING); runtime_tools_unittest.py and
fail-closed-regression.spec.ts remain green.
Part of #869 (Point-1 C2)
Ghost
approved these changes 2026-07-23 18:48:32 +00:00
Independent review, all 8 PASS -- verified against the REAL production runtime/claude/settings.json template (not just fixtures): (1) guard strips ONLY the 2 enforcement hooks per-entry, siblings survive, activatable round-trip byte-identical, C1 probe read-only; (2) opt-out --allow-inactive-enforcement CLI-flag-ONLY (no env escape, strict ===true), absent->hard-fail(exit1,strip,actionable), present-on-non-activatable->wired+loud-warn, no silent path; (3) fail-loud reaches operator (install.sh stderr no longer swallowed); (4) bash wiring routes settings.json through guard + python3 fallback; (5) fail-closed locks untouched+green; (6) red-first 19 vitest + 5 shell, mktemp isolation; (7) firewall/scope clean (8 files, operator-agnostic).
The guard: wires the 2 enforcement hooks ONLY if leaseEnforcementActivatable(), else strips + fails LOUD -- the install-ordering half of the #828/#869 brick-prevention.
6-CHECK re-verified by Mos @b6f365642249390b7dbe9ff983fbc514220b7aab: open/not-merged/mergeable onto current main/base main; CI success wp1988 (8/8) @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Part of #869'; queue clear.
## Gate-16 APPROVE -- stamped by Mos (id-11)
PR author jason.woltje(id2) != approver Mos(id11); commit-author mosaic-coder != reviewer -> no Finding-0. Registers the independent reviewer's APPROVE (#869 Point-1 C2, install-ordering guard); MS-LEAD relayed, Mos stamps+merges.
VERDICT: APPROVE @ b6f365642249390b7dbe9ff983fbc514220b7aab (Part of #869 -- Point-1 card, umbrella stays open).
Independent review, all 8 PASS -- verified against the REAL production runtime/claude/settings.json template (not just fixtures): (1) guard strips ONLY the 2 enforcement hooks per-entry, siblings survive, activatable round-trip byte-identical, C1 probe read-only; (2) opt-out --allow-inactive-enforcement CLI-flag-ONLY (no env escape, strict ===true), absent->hard-fail(exit1,strip,actionable), present-on-non-activatable->wired+loud-warn, no silent path; (3) fail-loud reaches operator (install.sh stderr no longer swallowed); (4) bash wiring routes settings.json through guard + python3 fallback; (5) fail-closed locks untouched+green; (6) red-first 19 vitest + 5 shell, mktemp isolation; (7) firewall/scope clean (8 files, operator-agnostic).
The guard: wires the 2 enforcement hooks ONLY if leaseEnforcementActivatable(), else strips + fails LOUD -- the install-ordering half of the #828/#869 brick-prevention.
6-CHECK re-verified by Mos @b6f365642249390b7dbe9ff983fbc514220b7aab: open/not-merged/mergeable onto current main/base main; CI success wp1988 (8/8) @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Part of #869'; queue clear.
Ghost
merged commit d351caad36 into main2026-07-23 18:48:34 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
mosaic-link-runtime-assetscopiesruntime/claude/settings.json— whichembeds the
PreToolUsemutator-gate.pyhook and theStopreceipt-observer-client.pyhook — straight into~/.claude/settings.json,unconditionally. If the ACTIVATION half of the lease broker (C1's
leaseEnforcementActivatable(): a CLI build advertising launch-runtimeactivation + a running broker supervisor) can't be confirmed on the host, the
fail-closed gate then denies every tool call with
GATE_UNAVAILABLE— abricked host.
This adds the install-ordering guard: before the enforcement hooks are
wired, call
leaseEnforcementActivatable()(imported directly from the C1module,
lease-activation-probe.ts) and decide whether they may be written.The guard (
packages/mosaic/src/commands/install-ordering-guard.ts)guardClaudeSettingsWiring()/runInstallOrderingGuard()— pure,injectable-deps functions; a hidden CLI subcommand
(
mosaic __link-claude-settings <src> <dest>) bridges the bash wiringpoint to this TS logic.
hook entries (every other hook —
PreCompact/SessionStartrevoke-lease,prevent-memory-write.sh, thePostToolUseqa/typecheck hooks, and thesibling
reflect-stop-hook.shinside the sameStoptrigger — is leftbyte-identical), exit 1, print an actionable message:
The opt-out:
--allow-inactive-enforcementA real CLI flag, threaded through
install.sh->mosaic-link-runtime-assets-> the hidden
mosaic __link-claude-settingssubcommand's own--allow-inactive-enforcementoption. Deliberately never an environmentvariable — it can't sit as a silently-inherited default in a shell profile.
Absent (the default) => hard fail-loud path. When set on a non-activatable
host, wiring proceeds but a loud, explicit warning is always logged.
Wiring point located
packages/mosaic/framework/tools/_scripts/mosaic-link-runtime-assetsiswhere
settings.json(and thus the enforcement hooks baked into it) actuallygets copied to
~/.claude/, called from bothframework/install.sh'spost-install step and the wizard's
finalize.ts. Both callers previouslyswallowed stderr (
2>&1 >/dev/null/stdio: 'pipe'with no surfacing) —fixed so the guard's message actually reaches the operator instead of being
silent.
What's unchanged
mutator-gate.py's own fail-closed-on-absent-identity runtime behavior isuntouched — this guard only decides whether the hook gets wired, never
touches the gate's own denial logic.
runtime_tools_unittest.pyandfail-closed-regression.spec.tsremain green, unmodified.Red-first tests
install-ordering-guard.spec.ts(19 tests): probe=false => hooks stripped,exit 1, actionable message; probe=false + opt-out => hooks wired, loud warn,
exit 0; probe=true => hooks wired normally, no logs; file-level
runInstallOrderingGuardexercised against temp dirs only (backup-on-changeparity with
copy_file_managed, idempotent no-op writes) — never touchesthe real
~/.claude/settings.json.test-install-ordering-guard.sh(5 shell scenarios, added totest:framework-shell): exercises the actual bash wiring inmosaic-link-runtime-assets(fakemosaicstub + the no-mosaic-on-PATHpython3 fallback), including that every other runtime file still gets
copied when the settings.json guard degrades.
Test plan
pnpm typecheck(via turbo,--filter=@mosaicstack/mosaic) — passpnpm lint(via turbo,--filter=@mosaicstack/mosaic) — passpnpm format:check— passpnpm --filter @mosaicstack/mosaic test(vitest +test:framework-shell,including the new shell regression) — pass, 1484+ tests
fail-closed-regression.spec.ts/runtime_tools_unittest.py— unchanged, greenPart of #869 (Point-1 C2)
mosaic-link-runtime-assets wired the PreToolUse mutator-gate.py and Stop receipt-observer-client.py hooks into ~/.claude/settings.json unconditionally. If the activation half (C1's leaseEnforcementActivatable()) can't be confirmed on the host, the fail-closed gate then denies every tool call — bricking it. Add a TS install-ordering guard (guardClaudeSettingsWiring / runInstallOrderingGuard, hidden CLI bridge `mosaic __link-claude-settings`) that imports leaseEnforcementActivatable() directly and decides whether the enforcement hooks get wired: - activatable -> wire as-is. - NOT activatable, no opt-out -> strip the enforcement hooks, exit non-zero with an actionable message (default, fail-loud). - NOT activatable + explicit --allow-inactive-enforcement -> wire anyway, loud warning logged. --allow-inactive-enforcement is a real CLI flag threaded through install.sh -> mosaic-link-runtime-assets -> the hidden subcommand — deliberately never an environment variable, so it can't sit as a silently-inherited default. Wire mosaic-link-runtime-assets's guarded settings.json copy to call out to the CLI (with a python3 fallback if `mosaic` isn't resolvable at all), and stop swallowing stderr in install.sh/finalize.ts so the guard's message actually reaches the operator. mutator-gate.py's own fail-closed-on-absent-identity runtime behavior is untouched (this only gates the WIRING); runtime_tools_unittest.py and fail-closed-regression.spec.ts remain green. Part of #869 (Point-1 C2)Gate-16 APPROVE -- stamped by Mos (id-11)
PR author jason.woltje(id2) != approver Mos(id11); commit-author mosaic-coder != reviewer -> no Finding-0. Registers the independent reviewer's APPROVE (#869 Point-1 C2, install-ordering guard); MS-LEAD relayed, Mos stamps+merges.
VERDICT: APPROVE @
b6f3656422(Part of #869 -- Point-1 card, umbrella stays open).Independent review, all 8 PASS -- verified against the REAL production runtime/claude/settings.json template (not just fixtures): (1) guard strips ONLY the 2 enforcement hooks per-entry, siblings survive, activatable round-trip byte-identical, C1 probe read-only; (2) opt-out --allow-inactive-enforcement CLI-flag-ONLY (no env escape, strict ===true), absent->hard-fail(exit1,strip,actionable), present-on-non-activatable->wired+loud-warn, no silent path; (3) fail-loud reaches operator (install.sh stderr no longer swallowed); (4) bash wiring routes settings.json through guard + python3 fallback; (5) fail-closed locks untouched+green; (6) red-first 19 vitest + 5 shell, mktemp isolation; (7) firewall/scope clean (8 files, operator-agnostic).
The guard: wires the 2 enforcement hooks ONLY if leaseEnforcementActivatable(), else strips + fails LOUD -- the install-ordering half of the #828/#869 brick-prevention.
6-CHECK re-verified by Mos @b6f365642249390b7dbe9ff983fbc514220b7aab: open/not-merged/mergeable onto current main/base main; CI success wp1988 (8/8) @exact head; reviewed==CI==head; independent APPROVE author!=reviewer no REQUEST-CHANGES; body 'Part of #869'; queue clear.