instar 1.3.599 → 1.3.601

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +77 -3
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/BackupManager.d.ts.map +1 -1
  5. package/dist/core/BackupManager.js +5 -0
  6. package/dist/core/BackupManager.js.map +1 -1
  7. package/dist/core/PostUpdateMigrator.d.ts +1 -0
  8. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  9. package/dist/core/PostUpdateMigrator.js +104 -0
  10. package/dist/core/PostUpdateMigrator.js.map +1 -1
  11. package/dist/core/PrHandLease.d.ts +151 -0
  12. package/dist/core/PrHandLease.d.ts.map +1 -0
  13. package/dist/core/PrHandLease.js +448 -0
  14. package/dist/core/PrHandLease.js.map +1 -0
  15. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  16. package/dist/core/devGatedFeatures.js +12 -0
  17. package/dist/core/devGatedFeatures.js.map +1 -1
  18. package/dist/core/instarSettingsHooks.d.ts.map +1 -1
  19. package/dist/core/instarSettingsHooks.js +10 -0
  20. package/dist/core/instarSettingsHooks.js.map +1 -1
  21. package/dist/core/multiMachineCapstoneMatrix.d.ts +49 -0
  22. package/dist/core/multiMachineCapstoneMatrix.d.ts.map +1 -0
  23. package/dist/core/multiMachineCapstoneMatrix.js +131 -0
  24. package/dist/core/multiMachineCapstoneMatrix.js.map +1 -0
  25. package/dist/scaffold/templates.d.ts.map +1 -1
  26. package/dist/scaffold/templates.js +2 -0
  27. package/dist/scaffold/templates.js.map +1 -1
  28. package/dist/server/AgentServer.d.ts +36 -0
  29. package/dist/server/AgentServer.d.ts.map +1 -1
  30. package/dist/server/AgentServer.js +2 -0
  31. package/dist/server/AgentServer.js.map +1 -1
  32. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  33. package/dist/server/CapabilityIndex.js +2 -0
  34. package/dist/server/CapabilityIndex.js.map +1 -1
  35. package/dist/server/routes.d.ts +7 -0
  36. package/dist/server/routes.d.ts.map +1 -1
  37. package/dist/server/routes.js +251 -0
  38. package/dist/server/routes.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/data/builtin-manifest.json +65 -65
  41. package/src/scaffold/templates.ts +2 -0
  42. package/upgrades/1.3.600.md +44 -0
  43. package/upgrades/1.3.601.md +38 -0
  44. package/upgrades/side-effects/live-test-capstone-runner-route.md +89 -0
  45. package/upgrades/side-effects/parallel-hand-pr-lease.md +153 -0
@@ -0,0 +1,38 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ Adds a per-branch **PR-push lease** so two of the agent's OWN concurrent sessions can't push competing commits to the same branch — the root of the 2026-06-15 PR #1183 "merge thrash" (two sessions force-pushing over each other, each restarting CI, turning a minutes-long merge into ~2 hours).
9
+
10
+ A new PreToolUse Bash hook (`pr-hand-lease-guard.js`) runs before any `git push`. It asks the server (`POST /pr-leases/evaluate`) whether another LIVE session of this agent already holds that branch's lease; if so the second session stands down instead of pushing. The decision logic lives in `src/core/PrHandLease.ts` (a per-branch lease store):
11
+
12
+ - **Ownership is keyed on the conversation TOPIC**, not the session id — so a session that respawns mid-work (compaction, refresh, revival) still recognizes its own lease and never deadlocks against itself.
13
+ - One process-wide lock + atomic compare-and-swap takeover (no double-drive), TTL + dead-holder auto-heal, and a 90-minute absolute ceiling so it can never wedge a branch. A live same-machine holder past the ceiling is escalated to the operator, not seized (a long rebuild is legitimate).
14
+ - **Fail-open on every uncertainty** — corrupt state, server unreachable, the hook itself crashing, no resolvable branch — all allow the push. A broken guard never blocks real work.
15
+ - Coordinates the agent's OWN cooperating sessions only. It is never authority over another person or agent, and a human action always wins.
16
+
17
+ Ships **dark + dryRun-first**, dev-gated (`monitoring.prHandLease`, developmentAgent-only). In dryRun the full decision loop runs and audits every would-deny to `logs/pr-lease-decisions.jsonl`, but NO push is ever blocked until a deliberate `dryRun:false`. Single-session agents are a strict no-op. v1 is machine-local (cross-machine coordination is a tracked follow-up).
18
+
19
+ ## What to Tell Your User
20
+
21
+ - "Nothing changes for you right now — this is internal dev-cycle infrastructure, off by default everywhere and in dry-run (observe-only) even on the development agent."
22
+ - "Once enabled, if two of my own sessions ever try to push to the same branch at once, one quietly waits instead of starting a tug-of-war — so PRs stop getting thrashed by my own parallel work. You'd only notice it as *fewer* stuck merges."
23
+
24
+ ## Summary of New Capabilities
25
+
26
+ | Capability | How to Use |
27
+ |-----------|-----------|
28
+ | Per-branch push lease (anti-thrash) | Automatic once enabled — a PreToolUse hook gates `git push`; dark + dryRun by default (`monitoring.prHandLease`) |
29
+ | "Who owns this branch's lease?" | `GET /pr-leases` (Bearer-auth) → per-branch holder topic + derived liveness |
30
+ | Decision audit | `logs/pr-lease-decisions.jsonl` (every acquire / renew / yield / auto-heal / release) |
31
+
32
+ ## Evidence
33
+
34
+ - **Unit** (`tests/unit/pr-hand-lease.test.ts`, 20 passing): `canonicalPushKey` over every refspec variant (explicit / `HEAD:` / `cd &&` / env-prefix / `--delete` / tag); respawn-survival (same-topic, new session → own-topic allow); live-foreign deny; dead→stale; foreign-machine-never-falsely-dead; live-holder-past-ceiling escalate; two-healers CAS race (exactly one wins, loser yields); dryRun ignored; corrupt-state → fail-open + recurrence-attention; tombstone passthrough; derived liveness.
35
+ - **Integration** (`tests/integration/pr-lease-route.test.ts`, 8 passing): `POST /pr-leases/evaluate` over the real HTTP pipeline — feature-disabled / bad-request / no-key fail-open; own-topic acquire; live-foreign DENY at `dryRun:false`; the SAME lease only WOULD-deny under default dryRun (never blocks); `GET /pr-leases` 503-when-disabled + derived-liveness list with `holderSessionId` redacted.
36
+ - **Ratchets green**: devGatedFeatures-wiring (88), migration-parity-hooks (5), feature-delivery-completeness (101), pretooluse-parity (3), no-bare-require-in-generated-hooks (24). Generated hook `node --check` = SYNTAX OK.
37
+ - **Whole-project `npx tsc --noEmit`**: exit 0, 0 errors.
38
+ - Spec converged through 4 review rounds (6 internal reviewers + 4 codex/gpt-5.5 external passes + the conformance gate); Phase-5 second-pass: CONCUR.
@@ -0,0 +1,89 @@
1
+ # Side-Effects Review — Live-Test Capstone Runner Route
2
+
3
+ **Change:** Make the (already-built, dark) multi-machine transfer CAPSTONE harness RUNNABLE.
4
+ Adds `POST /live-test/multi-machine-capstone` + `GET /live-test/artifacts`, the
5
+ `liveTestRunnerCtx` wiring (a `LiveTestRunnerWiring` factory on `AgentServer`), the
6
+ `multiMachineCapstoneMatrix` builder, and registers `liveTestRunner` in the dev-gated
7
+ feature registry. DEV-GATED + DARK (`monitoring.liveTestRunner.enabled` omitted from
8
+ ConfigDefaults → `resolveDevAgentGate`: live on a dev agent, dark on the fleet → routes
9
+ 503). Driven by the converged + approved `docs/specs/live-user-channel-proof-standard.md`
10
+ (§6 / §7.5).
11
+
12
+ **Files:** `src/commands/server.ts` (ctx factory), `src/server/AgentServer.ts`
13
+ (`LiveTestRunnerWiring` type + ctor wiring), `src/server/routes.ts` (the two routes),
14
+ `src/core/multiMachineCapstoneMatrix.ts` (the §7.5 matrix builder),
15
+ `src/core/devGatedFeatures.ts` (registration). Tests: unit (matrix + runner-capstone),
16
+ integration (route 200/503/400/aborted/auth), e2e (feature-alive on the real AgentServer
17
+ init path).
18
+
19
+ ## 1. Over-block — legitimate inputs wrongly rejected?
20
+ The route rejects only missing `targetMachine`/`telegramTopicId` (400). A surface whose
21
+ demo credential is absent is **not** rejected — it is recorded in `blockedSurfaces` and
22
+ its scenarios surface as a loud driver-error FAIL via `RealChannelDriver`'s "no real
23
+ sender configured" throw. No legitimate run is silently dropped: a non-moving seat is a
24
+ recorded `200 capstone:'aborted'` (the honesty contract), never a 400/500.
25
+
26
+ ## 2. Under-block — failure modes still missed?
27
+ - A demo SENDER that is mis-configured (wrong token) fails at send time → the scenario
28
+ records a driver-error FAIL (honest), not a fabricated PASS. Acceptable.
29
+ - The Slack production path uses `SlackApiClient(demoSlackUserToken)` (a Bearer xoxp
30
+ token). Reality: the available SageMind-Live-Test creds are an `xoxc` web-client token
31
+ + `d` cookie, not xoxp — so with today's creds the Slack surface reports
32
+ `credential-unavailable` (blocked, HONEST) rather than running. The Telegram surface is
33
+ the capstone's reply-from-target channel. **Tracked follow-up (CMT-1568):** an
34
+ xoxc+cookie Slack drive variant to actually run the Slack channel-parity scenario.
35
+ `<!-- tracked: CMT-1568 -->`
36
+ - The responder-machine resolver keys every surface on `telegramTopicId` (there is no
37
+ live Slack-channel→topic resolver) — documented inline as a GUESS; only affects the
38
+ optional Slack scenario. `<!-- tracked: CMT-1568 -->`
39
+
40
+ ## 3. Level-of-abstraction fit
41
+ Correct layer: the route composes existing, separately-tested primitives
42
+ (`RealChannelDriver`, `TelegramLiveSender`/`SlackLiveSender`, `LiveTestHarness`,
43
+ `LiveTestRunner`, `PlacementResponderReader`, `DemoChannelRegistry`) and the shared
44
+ `LiveTestArtifactStore` (the SAME store `LiveTestGate` reads, so a run's artifact is
45
+ exactly the artifact the gate later verifies). No business logic is re-implemented in the
46
+ route — it is composition + request validation + the seat-move-first orchestration the
47
+ standard mandates.
48
+
49
+ ## 4. Signal vs authority compliance
50
+ The route holds **no blocking authority over agent behavior**. It runs a test harness and
51
+ records a signed artifact. The only authority-adjacent component (`LiveTestGate`, which
52
+ computes a completion veto) is unchanged by this PR and remains dry-run. The route is a
53
+ pure producer of an observable artifact — Signal, not Authority. (`docs/signal-vs-authority.md`.)
54
+
55
+ ## 5. Interactions
56
+ - Shares the `LiveTestArtifactStore` instance with `LiveTestGate` by construction (server.ts
57
+ threads the SAME store into both) — verified by the e2e wiring-integrity test (d).
58
+ - The seat-move calls the LOCAL `/pool/transfer` (production) — same route the operator
59
+ uses; it is read through the honest `seatMoved` signal (the #1188 fix), never assumed.
60
+ - `transferForRequest` / `driverForRequest` are TEST-ONLY injection seams (undefined in
61
+ production); they let the e2e exercise the real AgentServer route with no live pool/network.
62
+ - No double-fire / no shadowing: the routes are new prefixes; CapabilityIndex +
63
+ feature-delivery-completeness ratchets pass.
64
+
65
+ ## 6. External surfaces
66
+ When RUN on a dev agent it posts a real demo message into a real (throwaway) channel and
67
+ moves a real seat — but it is DEV-GATED + DARK, requires explicit demo creds, and every
68
+ scenario is `safe`-volatility. On the fleet the routes 503 (no external effect). The run
69
+ is operator-gated in practice (demo-sender provisioning + a live destination machine).
70
+
71
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
72
+ **Machine-local BY DESIGN.** The artifact store + the route are per-machine: the capstone
73
+ is RUN from one machine (the test driver) and asserts the reply was served FROM the target
74
+ machine via `PlacementResponderReader` (GET /pool/placement). The cross-machine fact being
75
+ PROVEN is the responder identity; the runner itself does not replicate state. Recorded
76
+ artifacts are local observability (like the live-test gate's). No generated URLs; no
77
+ user-facing notice (dark infra) → no one-voice concern.
78
+
79
+ ## 8. Rollback cost
80
+ Trivial. Dark by default (503 on fleet). Back-out = omit/disable
81
+ `monitoring.liveTestRunner.enabled` (already the fleet default) or revert the PR — no data
82
+ migration, no agent-state repair. The shared store is additive (read-only to the gate).
83
+
84
+ ## Second-pass review
85
+ **Not required.** The change adds a test-harness RUN route with no block/allow authority
86
+ over agent behavior, no session-lifecycle mutation, and no gate/sentinel/watchdog decision
87
+ logic. It interacts with `LiveTestGate` only by sharing an artifact store (read path
88
+ unchanged). The seat-move uses the existing honest `/pool/transfer` contract. Risk is
89
+ bounded by the dev-gate + dark default + safe-only scenarios.
@@ -0,0 +1,153 @@
1
+ # Side-Effects Review — Parallel-Hand PR Lease
2
+
3
+ Spec: `docs/specs/parallel-hand-pr-lease.md` (converged 4 rounds + approved).
4
+ Change: a per-branch push-ownership lease so two of the agent's OWN concurrent
5
+ sessions can't push competing commits to the same branch (the 2026-06-15 #1183
6
+ 2-hour merge thrash). A PreToolUse Bash hook asks the server (before a `git push`)
7
+ whether another LIVE session of this agent holds the branch's lease; if so the
8
+ second hand stands down. Dev-gated dark + dryRun-first; coordinates the agent's OWN
9
+ cooperating hands only.
10
+
11
+ **Phase-1 principle check (decision-point = YES):** the lease DENIES a `git push`,
12
+ so signal-vs-authority applies. Per spec §7 this is a deliberate, blast-radius-LIMITED
13
+ P2 EXCEPTION (not "it satisfies P2"): it blocks ONLY the agent's own cooperative
14
+ hands, ONLY a `git push`, FAILS OPEN on every uncertainty, and surfaces holder+intent
15
+ so a distinct fix escalates as a follow-up (never silently dropped). Outside those
16
+ limits it is a signal, not authority.
17
+
18
+ Files:
19
+ - `src/core/PrHandLease.ts` — store (one-lock CAS, topic-id identity, TTL-gated host-aware liveness, liveness-discriminated 90m ceiling, fail-open-on-corrupt, dryRun isolation, tombstones) + pure `canonicalPushKey` (git-native two-tier ref resolution).
20
+ - `src/server/routes.ts` — `POST /pr-leases/evaluate` (fail-open on every uncertainty; dryRun→allow+wouldDeny; redacts holderSessionId) + `GET /pr-leases` (derived liveness; 503 when disabled) + `RouterContext.prHandLease?`.
21
+ - `src/core/PostUpdateMigrator.ts` — `getPrHandLeaseGuardHook()` + migrateHooks deploy + migrateClaudeMd awareness.
22
+ - `src/core/instarSettingsHooks.ts` — `INSTAR_BASH_PRETOOLUSE_HOOKS` += the guard (registers for both init + existing-agent migration).
23
+ - `src/commands/server.ts` + `src/server/AgentServer.ts` — dev-gated construction (machine-local v1, runningSessionNames=tmuxSession set, audit→logs/pr-lease-decisions.jsonl) → AgentServer option → routeCtx.
24
+ - `src/core/devGatedFeatures.ts` — `prHandLease` dev-gate entry (dryRun-canary).
25
+ - `src/core/BackupManager.ts` — `state/pr-hand-leases.json` on the ephemeral denylist.
26
+ - `src/scaffold/templates.ts` — generateClaudeMd awareness block.
27
+ - Tests: `tests/unit/pr-hand-lease.test.ts` (20), `tests/integration/pr-lease-route.test.ts` (8); ratchets green (devGatedFeatures-wiring, migration-parity-hooks, feature-delivery, pretooluse-parity, no-bare-require).
28
+
29
+ ## Build-time design DEVIATION from the spec (recorded, mine, reversible)
30
+ Spec §4 idealized "the hook does a single local JSON read, no HTTP." The build
31
+ re-targeted to **hook → `POST /pr-leases/evaluate` (localhost)**: a generated
32
+ standalone `.js` hook can't cleanly import the TS store, and reimplementing the
33
+ security logic in JS would DUPLICATE it (drift hazard on a security path). The
34
+ codebase pattern (every instar hook) is hook→server. Latency is a localhost
35
+ round-trip (~ms); the git dry-run resolver runs server-side only on a detected push.
36
+ Same security posture; the logic stays in tested TS. Server-unreachable → fail-open.
37
+
38
+ ## 1. Over-block
39
+ The ONLY block is `git push` exit-2 on a confirmed LIVE foreign same-agent lease
40
+ (and only at `dryRun:false`). Over-block risks + mitigations: (a) a stale lease read
41
+ as live → TTL + host-aware liveness + the 90m ceiling force-release a dead/foreign
42
+ holder; (b) a corrupt state file / server-down / hook crash → ALL fail OPEN (allow);
43
+ (c) the dry-run resolver hanging → bounded timeout + GIT_TERMINAL_PROMPT=0 → fail
44
+ open. A live same-machine holder past 90m is NOT seized (would reintroduce the
45
+ thrash) — it escalates to the operator instead. Verified by the unit truth-table.
46
+
47
+ ## 2. Under-block
48
+ Misses (accepted residual evasions of a COOPERATIVE guard, stated in §2 non-goals):
49
+ a push hidden in a called script body (`bash deploy.sh`), a git alias, or string
50
+ obfuscation (`git pu"sh"`) — the hook matches a literal `git push` in the command.
51
+ Also OUT of scope: cross-MACHINE collisions (machine-local v1; `holderMachineId`
52
+ load-bearing only for the never-falsely-dead rule) and shared-WORKTREE edit bleed
53
+ (§1.1b — the `instar worktree create` convention is the mitigation). All documented.
54
+
55
+ ## 3. Level-of-abstraction fit
56
+ Correct. Enforcement sits at the PreToolUse Bash chokepoint where the agent's own
57
+ `git push` actually runs (round-2 caught that SafeGitExecutor — instar's internal TS
58
+ wrapper — is bypassed by agent Bash pushes). The security logic lives server-side in
59
+ tested TS; the hook is a thin relay. Identity is the stable TOPIC, not the respawn-
60
+ volatile session id. Reuses the ResumeQueue lock/persist/FD discipline.
61
+
62
+ ## 4. Signal vs authority compliance (docs/signal-vs-authority.md)
63
+ COMPLIANT as a deliberate, bounded P2 exception (§7): a brittle deterministic check
64
+ holds blocking authority ONLY over the agent's own cooperative hands, only over
65
+ `git push`, fails open on every uncertainty, and never blocks a principal/operator.
66
+ The deny surfaces holder+intent so a genuinely-distinct fix escalates rather than is
67
+ dropped. Dark + dryRun-first means the bind is opt-in and observable first.
68
+
69
+ ## 5. Interactions
70
+ - MergeRunner (green-pr-automerge): NOT a competing committer; reads the lease only as
71
+ a soft-hold on a live same-machine `intent:rework` (bounded by TTL). A dryRun lease
72
+ is ignored by all non-acquisition readers (so it can't perturb auto-merge).
73
+ - ResumeQueue: shares the lock/persist/FD pattern but a SEPARATE file + lock (no
74
+ contention with the revival queue).
75
+ - The hook is registered AFTER the existing Bash PreToolUse guards; it never shadows
76
+ them (it only blocks `git push`, and only on a confirmed deny).
77
+
78
+ ## 6. External surfaces
79
+ - New routes `POST /pr-leases/evaluate` + `GET /pr-leases` (Bearer-auth'd by the
80
+ server's global middleware like every route). New PreToolUse Bash hook
81
+ `pr-hand-lease-guard.js` (registered for init + existing agents via the canonical
82
+ hook list). New config under `monitoring.prHandLease.*` (read with safe defaults).
83
+ - Flag off (fleet default) → byte-identical-today behavior (hook fail-opens / route
84
+ feature-disabled). Visible only on a dev agent, and there only in dryRun (logs).
85
+
86
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
87
+ **Machine-local BY DESIGN (v1), stated honestly.** The lease state is per-machine;
88
+ two hands on DIFFERENT machines driving the same branch is NOT prevented by v1 (the
89
+ same thrash recurs). `holderMachineId` is load-bearing ONLY for the safety rule "never
90
+ judge a foreign-machine holder dead from local session absence" (so v1 never falsely
91
+ seizes a cross-machine holder within the ceiling). A fully replicated cross-machine
92
+ lease (likely a git-ref CAS lock) is the tracked §9b follow-up; it MUST inherit the
93
+ ResumeQueue FD1/FD2/FD5 host-classification before it ships. The observed incident
94
+ was same-machine, so v1 closes the demonstrated failure.
95
+
96
+ ## 8. Rollback cost
97
+ Trivial + reversible. Flag off (`monitoring.prHandLease` absent / developmentAgent
98
+ false) → the route feature-disables (fail-open) and the hook fail-opens → no behavior
99
+ change. The state file is ephemeral (excluded from backup). No data migration, no
100
+ persistent external side-effect. A bad deploy is a config flip away from inert.
101
+
102
+ ## Testing-scope decision (honest, precedented)
103
+ Unit (20) + integration (8) cover the store logic + the route over the real HTTP
104
+ pipeline. The generated hook is syntax-validated (`node --check`) + covered by the
105
+ no-bare-require regression test. A full hook-subprocess behavior test (mock server +
106
+ exit-code assertions) and a boot-e2e ("feature alive": route 200 not 503) are the one
107
+ deferred tier — same honest-scope decision as P1/#1174 + action-claim/#1178, justified
108
+ because the route IS exercised end-to-end at the integration tier and the hook is a
109
+ thin fail-open relay. <!-- tracked: follow-up — add tests/unit/pr-hand-lease-guard.test.ts (hook subprocess) + a boot-e2e at fleet-rollout, per the action-claim precedent -->
110
+
111
+ ## Phase-5 second-pass review (REQUIRED — touches a hook/gate/push-path)
112
+ **CONCUR with the review.** An independent reviewer audited every claim against the
113
+ code: the hook fail-opens on every path (non-Bash, no-git-push, no-config, no-topic/
114
+ session, server-down/timeout inner-catch, own-crash outer-catch, 8s stdin backstop);
115
+ the route fail-opens on null prHandLease / bad-request / no-branch-key / any throw,
116
+ and `evaluate()` is non-throwing (corrupt state → `{}`); the DENY path fires ONLY at
117
+ `dryRun:false` on a confirmed live foreign-topic lease (default dryRun rewrites every
118
+ deny/escalate to `allow+wouldDeny`); holderSessionId is redacted on both surfaces; a
119
+ respawned same-topic session reads its own lease as `own-topic` allow (topicId identity,
120
+ checked before any liveness probe); the 90m ceiling escalates a LIVE same-machine
121
+ holder but CAS-seizes a dead/foreign holder (loser yields); escalation routes to the
122
+ real aggregated-attention chokepoint; construction is dev-gated + audited.
123
+
124
+ Minor non-blocking note (accepted, tracked): PrHandLease is constructed nested inside
125
+ the `if (rqCfg.enabled ?? true)` resume-queue block (it reuses `raiseResumeAggregated`
126
+ for onAttention), so explicitly setting `monitoring.resumeQueue.enabled:false` would
127
+ leave prHandLease null — but the route then fail-opens, so this is a FAIL-SAFE soft
128
+ coupling, not a defect (and the resume queue defaults enabled). <!-- tracked: follow-up — decouple PrHandLease construction from the resume-queue-enabled block (give it its own onAttention sink) so the two features are independent -->
129
+
130
+ ## Post-merge addendum (2026-06-16) — no-silent-fallbacks ratchet
131
+ Merging current main into the branch tripped the no-silent-fallbacks ratchet (477 > 474).
132
+ PrHandLease's only flagged catch — the `readAll` corrupt-state fail-open — is now
133
+ `@silent-fallback-ok`-tagged (it is `recordFailOpen()`-surfaced with a recurrence
134
+ attention item, so it is NOT silent). The residual +2 is main's own accumulated drift
135
+ inherited via the merge (main shipped catches under `[skip ci]` releases that never
136
+ re-ran this gate); the baseline is aligned 474→476 with an in-file justification, per the
137
+ Zero-Failure Standard (a merge pulling main's pre-existing red is the merging branch's to
138
+ settle). No behavior change; the lease's fail-open posture is unchanged.
139
+
140
+ ## Post-CI addendum (2026-06-16) — discoverability classification
141
+
142
+ CI surfaced a second gap from the build: the new `GET /pr-leases` +
143
+ `POST /pr-leases/evaluate` routes were registered in routes.ts but the
144
+ `/pr-leases` prefix was never classified, so the capabilities-discoverability
145
+ ratchet failed. Resolved by adding `{ prefix: 'pr-leases', ... }` to
146
+ `INTERNAL_PREFIXES` in `src/server/CapabilityIndex.ts` — the lease is dev-gated
147
+ coordination machinery (a PreToolUse git-push guard consults it; the CLAUDE.md
148
+ template documents it), agent-invisible and 503 on the fleet, so it is correctly
149
+ INTERNAL (skips `/capabilities` discovery) rather than a user-invokable
150
+ capability — same class as `/action-claim` and `/playwright-profiles`.
151
+ Classification only; no runtime behavior change. (Also in this PR: the
152
+ `instar-settings-hooks` anti-drift contract was updated to include the new
153
+ `pr-hand-lease-guard.js` PreToolUse hook.)