Lease subsystem is inert on every fleet seat: observers are installed globally, leases are provisioned only on the mosaic launch path #1340

Open
opened 2026-08-20 22:41:02 +00:00 by fred · 0 comments
Collaborator

Finding

The lease subsystem is installed in two halves that are wired to two different things, and only one half reaches a fleet seat.

The observer half is global. ~/.claude/settings.json registers revoke-lease.py on three hooks (PreCompact, session-start-compact, session-start-rollover). A settings file applies to every claude process on the host, however it was started.

The provisioning half is launch-path-local. MOSAIC_LEASE_BROKER_SOCKET is injected only by execLeaseGatedRuntime in packages/mosaic/src/commands/launch.ts:1056, which runs only on mosaic claude / mosaic pi. MOSAIC_LEASE_SESSION_ID comes from launch-runtime.py on that same path.

Fleet seats do not take that path. Measured on this host:

fred      -> claude --model opus --effort high --append-system-prompt-file ...
peggy     -> pi
vision    -> pi

Bare runtime binaries. No mosaic in the chain.

Measurement

Every live seat pane, MOSAIC_LEASE_* variable count read from /proc/<pane_pid>/environ:

code-infra-01 0   fargo 0   fred 0   gate-merge-01 0   junk 0
ops-01 0   ops-02 0   ops-03 0   ops-ci-01 0   ops-deploy-01 0
peggy 0   rev-code-01 0   rev-code-02 0   rev-security-01 0
rev-security-02 0   rev-security-03 0   veronica 0   vision 0   vision-unslop 0

19 of 19. Not a per-seat provisioning miss. The mechanism has never applied to this fleet.

Consequences

  1. D29, now fixed by #1339. The observer fired, found no lease, KeyError, fail-closed, rc=2 on every lifecycle transition including compaction. That is the symptom that surfaced this.
  2. The enforcement half was never wired at all. mutator-gate.py appears in no settings file. It carries the same KeyError -> deny("GATE_UNAVAILABLE") shape as revoke-lease.py, so had it been registered, every tool call on every seat would have been denied. It is inert, which is why nobody noticed.

So the lease broker does not gate any live seat's mutations today. Whatever it was built to enforce, it is not enforcing.

Why this is NS-10

NS-10 in docs/fleet/NORTH_STAR.yaml: an adoption is not complete until the mechanism it replaces is removed. This is the same shape as the three mismatches behind mosaic fleet ps being blind — roster declares a socket that does not exist, 18 sessions on default, nothing writes a roster. Each was decided correctly and left half-applied. The lease subsystem is a fourth instance: correctly designed, correctly implemented, wired to a launch path the fleet does not use.

Not proposing a fix here

There is a real decision underneath this and it is not mine to make silently. Either:

  • A. Seats launch through mosaic, which provisions the lease and makes both halves live. This is I4 in the tier-0 list (mosaic fleet absorbs launch-seat.sh). It would also turn mutator-gate on for the first time, which is a behaviour change of real size and needs its own gate.
  • B. The lease subsystem is scoped to the mosaic launch path deliberately, in which case the global hook registration in ~/.claude/settings.json is the bug, and the observers should be installed by the launcher rather than the settings file.

Filed for a decision, not for someone to pick one and ship it.

Scope note

#1339 makes the symptom non-fatal. It does not close this. This issue should stay open after #1339 merges.

## Finding The lease subsystem is installed in two halves that are wired to two different things, and only one half reaches a fleet seat. **The observer half is global.** `~/.claude/settings.json` registers `revoke-lease.py` on three hooks (`PreCompact`, `session-start-compact`, `session-start-rollover`). A settings file applies to *every* `claude` process on the host, however it was started. **The provisioning half is launch-path-local.** `MOSAIC_LEASE_BROKER_SOCKET` is injected only by `execLeaseGatedRuntime` in `packages/mosaic/src/commands/launch.ts:1056`, which runs only on `mosaic claude` / `mosaic pi`. `MOSAIC_LEASE_SESSION_ID` comes from `launch-runtime.py` on that same path. **Fleet seats do not take that path.** Measured on this host: ``` fred -> claude --model opus --effort high --append-system-prompt-file ... peggy -> pi vision -> pi ``` Bare runtime binaries. No `mosaic` in the chain. ## Measurement Every live seat pane, `MOSAIC_LEASE_*` variable count read from `/proc/<pane_pid>/environ`: ``` code-infra-01 0 fargo 0 fred 0 gate-merge-01 0 junk 0 ops-01 0 ops-02 0 ops-03 0 ops-ci-01 0 ops-deploy-01 0 peggy 0 rev-code-01 0 rev-code-02 0 rev-security-01 0 rev-security-02 0 rev-security-03 0 veronica 0 vision 0 vision-unslop 0 ``` 19 of 19. Not a per-seat provisioning miss. The mechanism has never applied to this fleet. ## Consequences 1. **D29, now fixed by #1339.** The observer fired, found no lease, `KeyError`, fail-closed, `rc=2` on every lifecycle transition including compaction. That is the symptom that surfaced this. 2. **The enforcement half was never wired at all.** `mutator-gate.py` appears in no settings file. It carries the same `KeyError -> deny("GATE_UNAVAILABLE")` shape as `revoke-lease.py`, so had it been registered, every tool call on every seat would have been denied. It is inert, which is why nobody noticed. So the lease broker does not gate any live seat's mutations today. Whatever it was built to enforce, it is not enforcing. ## Why this is `NS-10` `NS-10` in `docs/fleet/NORTH_STAR.yaml`: *an adoption is not complete until the mechanism it replaces is removed.* This is the same shape as the three mismatches behind `mosaic fleet ps` being blind — roster declares a socket that does not exist, 18 sessions on `default`, nothing writes a roster. Each was decided correctly and left half-applied. The lease subsystem is a fourth instance: correctly designed, correctly implemented, wired to a launch path the fleet does not use. ## Not proposing a fix here There is a real decision underneath this and it is not mine to make silently. Either: - **A. Seats launch through `mosaic`,** which provisions the lease and makes both halves live. This is `I4` in the tier-0 list (`mosaic fleet` absorbs `launch-seat.sh`). It would also turn `mutator-gate` on for the first time, which is a behaviour change of real size and needs its own gate. - **B. The lease subsystem is scoped to the `mosaic` launch path deliberately,** in which case the global hook registration in `~/.claude/settings.json` is the bug, and the observers should be installed by the launcher rather than the settings file. Filed for a decision, not for someone to pick one and ship it. ## Scope note `#1339` makes the symptom non-fatal. It does not close this. This issue should stay open after `#1339` merges.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#1340