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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +77 -3
- package/dist/commands/server.js.map +1 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +5 -0
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +104 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PrHandLease.d.ts +151 -0
- package/dist/core/PrHandLease.d.ts.map +1 -0
- package/dist/core/PrHandLease.js +448 -0
- package/dist/core/PrHandLease.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +12 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/instarSettingsHooks.d.ts.map +1 -1
- package/dist/core/instarSettingsHooks.js +10 -0
- package/dist/core/instarSettingsHooks.js.map +1 -1
- package/dist/core/multiMachineCapstoneMatrix.d.ts +49 -0
- package/dist/core/multiMachineCapstoneMatrix.d.ts.map +1 -0
- package/dist/core/multiMachineCapstoneMatrix.js +131 -0
- package/dist/core/multiMachineCapstoneMatrix.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +2 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +36 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +2 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +2 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +7 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +251 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +2 -0
- package/upgrades/1.3.600.md +44 -0
- package/upgrades/1.3.601.md +38 -0
- package/upgrades/side-effects/live-test-capstone-runner-route.md +89 -0
- 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.)
|