5.9 KiB
T1 report: canonical ungated Claude base and lease overlay
Changed
- Replaced
packages/mosaic/framework/runtime/claude/settings.jsonwith the canonical ungated base. It retains the model, QA hooks, plugins, command allowlist, permissions, andmcpServers.sequential-thinking. - Added
packages/mosaic/framework/runtime/claude/lease-overlay.json. It contains onlyhooksand the six removed lease hook entries. - Added the byte-identical pre-split source fixture at
packages/mosaic/src/runtime/fixtures/claude-settings.gated.pre-split.json. - Added
packages/mosaic/src/runtime/claude-settings-base.spec.ts.
framework-manifest.txt already declares runtime/**, so the new overlay is framework-owned and shipped without a manifest change.
Lease-hook enumeration
The actual template has six lease hook entries, matching fred's refined boundary:
PreToolUsematcher.*:mutator-gate.pyStop: one combined command containingreceipt-observer-client.pythenpromote-complete.pyUserPromptSubmitmatcher^/mosaic-promote$:promote-begin.pyPreCompact:revoke-lease.py --reason pre-compactSessionStartmatchercompact:revoke-lease.py --reason session-start-compactSessionStartmatcherresume|clear:revoke-lease.py --reason session-start-rollover --bump-generation
There is no delta from the refined six-entry enumeration. The Stop entry contains the receipt-observer and promote-complete commands together, rather than as two separate hook objects.
Tests and checks
pnpm install --frozen-lockfile was run first because node_modules was absent. It completed successfully.
Red-first run before artifacts existed:
RUN v2.1.9 .../packages/mosaic
❯ src/runtime/claude-settings-base.spec.ts (4 tests | 4 failed)
× keeps every lease command out of the ungated base
→ mutator-gate: expected true to be false
× reconstructs the pre-split gated hooks while retaining the canonical MCP correction
→ ENOENT: .../lease-overlay.json
× ships sequential-thinking in the base
→ expected undefined to deeply equal { 'sequential-thinking': ... }
× limits the overlay to lease hook entries
→ ENOENT: .../lease-overlay.json
Final focused acceptance run:
RUN v2.1.9 .../packages/mosaic
✓ src/runtime/claude-settings-base.spec.ts (4 tests) 19ms
Test Files 1 passed (1)
Tests 4 passed (4)
pnpm --filter @mosaicstack/mosaic lint passed:
> @mosaicstack/[email protected] lint
> eslint src
pnpm --filter @mosaicstack/mosaic typecheck failed on pre-existing workspace resolution and unrelated package errors. The new spec no longer appears in the error list. Initial failures include missing @mosaicstack/{brain,forge,log,macp,memory,queue,storage,quality-rails,db,config,prdy,types} declarations, followed by existing fleet-backlog.ts, gateway-doctor.ts, and TUI implicit-any errors. Exit status: 2.
A focused legacy consumer run confirms an existing assumption that settings.json itself is gated:
pnpm --filter @mosaicstack/mosaic exec vitest run src/mutator-gate/mutator-gate.acceptance.spec.ts
❯ src/mutator-gate/mutator-gate.acceptance.spec.ts (20 tests | 6 failed)
× non-dangerous parser residual is denied by the global all-tools hook without a lease
→ expected all-tools mutator-gate command in settings.json
× Claude and Pi compaction observer wiring is complete and fail-closed
→ expected PreCompact/SessionStart revoke-lease hooks in settings.json
The other four failures in that focused run reported STALE_GENERATION where the test expected MUTATOR_UNVERIFIED, plus one successful-gate assertion. I did not redesign this legacy suite because the task explicitly says to report consumers that assume the base is gated.
Consumers found
Direct runtime/claude/settings.json path consumers found by the required repository grep:
packages/mosaic/framework/tools/_scripts/mosaic-link-runtime-assets: copies the base to~/.claude/settings.json.packages/mosaic/src/commands/install-ordering-guard.tsand.spec.ts: documentation and behavior assume the source embeds enforcement hooks.packages/mosaic/framework/tools/_scripts/test-install-ordering-guard.sh: comments and assertions expectmutator-gate.pyandreceipt-observer-client.pyin the base.packages/mosaic/src/mutator-gate/mutator-gate.acceptance.spec.ts: reads the base and asserts mutator, promotion, and compaction lease wiring.packages/mosaic/src/lease-broker/promotion_trigger_unittest.py: reads the base and asserts promotion wiring.packages/mosaic/src/lease-broker/recovery_runtime_unittest.py: reads the base.packages/mosaic/src/runtime/update-checker.tsand.spec.ts: references the path in settings wiring/update checks.- Documentation-only references:
docs/compaction-refresh/probes/p6_constrained_recovery.py,docs/plans/agent-reflection-loop-PRD.md,docs/tasks/544-agent-reflection-loop.md, and the framework QA documentation/scripts found by grep.
I did not change these consumers. The install/link and lease acceptance consumers must be taught to select and compose lease-overlay.json when a gated promotion seat is requested. That composition behavior is outside T1.
Ambiguity handled
The exact pre-split template fixture has no mcpServers key (SHA-256 44e74ea1e9d424fffa020ee666402662ac856b88bf6ae7f3b8931eed29dc75a4). The task simultaneously requires a byte-for-byte pre-split fixture, mcpServers.sequential-thinking in the base, and deep-merge(base, overlay) == original. Those three conditions cannot all hold because a merge cannot remove the required MCP key.
The acceptance test preserves the exact fixture and asserts that the normalized merge equals the pre-split template plus the required canonical mcpServers.sequential-thinking correction. It verifies all original hook content is reconstructed and the base carries the required MCP. Production three-layer merge semantics remain W-F1 work.