instar 1.3.600 → 1.3.602

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 (44) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +29 -1
  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/LiveTestSlackCaller.d.ts +72 -0
  8. package/dist/core/LiveTestSlackCaller.d.ts.map +1 -0
  9. package/dist/core/LiveTestSlackCaller.js +114 -0
  10. package/dist/core/LiveTestSlackCaller.js.map +1 -0
  11. package/dist/core/PostUpdateMigrator.d.ts +1 -0
  12. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  13. package/dist/core/PostUpdateMigrator.js +104 -0
  14. package/dist/core/PostUpdateMigrator.js.map +1 -1
  15. package/dist/core/PrHandLease.d.ts +151 -0
  16. package/dist/core/PrHandLease.d.ts.map +1 -0
  17. package/dist/core/PrHandLease.js +448 -0
  18. package/dist/core/PrHandLease.js.map +1 -0
  19. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  20. package/dist/core/devGatedFeatures.js +6 -0
  21. package/dist/core/devGatedFeatures.js.map +1 -1
  22. package/dist/core/instarSettingsHooks.d.ts.map +1 -1
  23. package/dist/core/instarSettingsHooks.js +10 -0
  24. package/dist/core/instarSettingsHooks.js.map +1 -1
  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 +1 -0
  29. package/dist/server/AgentServer.d.ts.map +1 -1
  30. package/dist/server/AgentServer.js +1 -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 +1 -0
  34. package/dist/server/CapabilityIndex.js.map +1 -1
  35. package/dist/server/routes.d.ts +4 -0
  36. package/dist/server/routes.d.ts.map +1 -1
  37. package/dist/server/routes.js +100 -1
  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.602.md +87 -0
  43. package/upgrades/side-effects/parallel-hand-pr-lease.md +153 -0
  44. package/upgrades/side-effects/xoxc-slack-caller.md +99 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * PrHandLease — per-branch ownership lease so two of the agent's OWN concurrent
3
+ * sessions ("hands") cannot push competing commits to the same branch.
4
+ *
5
+ * Spec: docs/specs/parallel-hand-pr-lease.md (review-convergence + approved).
6
+ * Closes the push-competition cause of the 2026-06-15 PR #1183 merge thrash.
7
+ *
8
+ * Design anchors (from the converged spec):
9
+ * - Identity = holderTopicId (stable across the constant session respawns);
10
+ * holderSessionId is a LIVENESS-PROBE HANDLE ONLY, never the identity (§3.2/B2).
11
+ * - ONE process-wide lock guards the single JSON file (matching ResumeQueue's
12
+ * single lockPath); per-record compare-and-swap under that lock (§3.3/B3/M-D).
13
+ * - Liveness probe is TTL-gated (only when a lease is past ttl) and reads the
14
+ * in-memory running set — never a tmux exec (§3.5/M4).
15
+ * - A holder on a DIFFERENT machine is NEVER judged dead from local session
16
+ * absence (§3.5/M6); foreign-machine holder → fail-closed → treat as live.
17
+ * - maxHold ceiling is liveness-discriminated: a DEAD/foreign-unverified holder
18
+ * past the ceiling is CAS-seized + attention; a LIVE same-machine holder past
19
+ * the ceiling is NOT seized — the caller escalates instead (§3.9/codex#3).
20
+ * - Unreadable/corrupt/absent state file → fail-OPEN (treat as no lease) (§3.4/M10).
21
+ * - dryRun leases carry dryRun:true; non-acquisition readers MUST ignore them (§5).
22
+ *
23
+ * This module is the STORE + the pure key-derivation helpers. The PreToolUse Bash
24
+ * hook (pr-hand-lease-guard.js) and the GET /pr-leases route consume it. Enforcement
25
+ * is the hook's (the real chokepoint where agent `git push` runs); SafeGitExecutor
26
+ * carries the same check as defense-in-depth for instar's own internal pushes.
27
+ */
28
+ export type LeaseIntent = 'build' | 'rework' | 'merge';
29
+ export type LeaseTombstoneStatus = 'released' | 'merged' | 'abandoned';
30
+ export interface PrHandLeaseRecord {
31
+ /** STABLE logical owner — the conversation topic. Survives session respawn. */
32
+ holderTopicId: number;
33
+ /** Liveness-probe handle ONLY (tmux session name). NEVER the identity. */
34
+ holderSessionId: string;
35
+ /** Load-bearing for the never-falsely-dead rule (§3.5/M6). */
36
+ holderMachineId: string;
37
+ /** Secondary metadata once the PR exists; never the primary key. */
38
+ prNumber?: number | null;
39
+ intent: LeaseIntent;
40
+ acquiredAt: number;
41
+ renewedAt: number;
42
+ ttlMs: number;
43
+ maxHoldMs: number;
44
+ /** Observe-only soak: a dryRun lease MUST be ignored by all non-acquisition readers. */
45
+ dryRun?: boolean;
46
+ /** Set only on a tombstoned (released) record; a tombstone is never a live holder. */
47
+ tombstone?: {
48
+ status: LeaseTombstoneStatus;
49
+ at: number;
50
+ };
51
+ }
52
+ /** Derived liveness reported by GET /pr-leases — honest, computed, never the raw record. */
53
+ export type DerivedLeaseLiveness = 'live' | 'stale-dead' | 'stale-ttl' | 'tombstoned' | 'foreign-machine';
54
+ export interface LeaseEvalResult {
55
+ /** What the push chokepoint should do. */
56
+ decision: 'allow' | 'deny' | 'escalate';
57
+ reason: string;
58
+ holder?: PrHandLeaseRecord;
59
+ }
60
+ export interface PrHandLeaseDeps {
61
+ stateDir: string;
62
+ machineId: string;
63
+ /** Returns the tmux session names currently running (in-memory; no tmux exec). */
64
+ runningSessionNames: () => string[];
65
+ /** ms clock (injectable for tests). */
66
+ now?: () => number;
67
+ /** Emitted on a forced release / fail-open recurrence for the attention surface. */
68
+ onAttention?: (item: {
69
+ kind: string;
70
+ detail: string;
71
+ }) => void;
72
+ /** Structured audit sink — one row per acquire/renew/yield/auto-heal/release. */
73
+ onAudit?: (row: Record<string, unknown>) => void;
74
+ }
75
+ /**
76
+ * Canonicalize a `git push` command's destination ref to `refs/heads/<name>`.
77
+ * Two-tier (§3.1): explicit refspec / local @{push} fast path (no network);
78
+ * `git push --dry-run --porcelain` only for the ambiguous case, timeout-bounded.
79
+ * Returns null when no branch key is derivable (detached, tag, delete, resolver
80
+ * error/timeout) → the caller FAILS OPEN (does not gate).
81
+ */
82
+ export declare function canonicalPushKey(command: string, cwd: string, opts?: {
83
+ execTimeoutMs?: number;
84
+ }): string | null;
85
+ export declare class PrHandLease {
86
+ private readonly d;
87
+ private readonly filePath;
88
+ private readonly lockPath;
89
+ private readonly now;
90
+ private failOpenCounts;
91
+ constructor(deps: PrHandLeaseDeps);
92
+ /** The path the BackupManager denylist must exclude (ephemeral, never restored). */
93
+ static stateFileRelPath(): string;
94
+ /**
95
+ * Evaluate a push against the lease for `key` (from canonicalPushKey).
96
+ * Returns the chokepoint decision. NEVER throws — any internal error resolves
97
+ * to allow (the hook's own-crash fail-open is the outer guarantee; this is the
98
+ * inner one).
99
+ */
100
+ evaluate(key: string, myTopicId: number, mySessionId: string): LeaseEvalResult;
101
+ /** Acquire or renew the lease for `key` for my (topic, session, machine). Atomic CAS under the lock. */
102
+ acquireOrRenew(key: string, holder: {
103
+ topicId: number;
104
+ sessionId: string;
105
+ intent?: LeaseIntent;
106
+ prNumber?: number | null;
107
+ dryRun?: boolean;
108
+ }): PrHandLeaseRecord;
109
+ /**
110
+ * Atomic-CAS takeover of a stale lease (§3.3/B3). Writes my record only if the
111
+ * on-disk record still matches the observed (holderTopicId, acquiredAt). Returns
112
+ * the new record on success, or null if another hand healed first (loser yields).
113
+ */
114
+ takeOverIfStale(key: string, observed: {
115
+ holderTopicId: number;
116
+ acquiredAt: number;
117
+ }, holder: {
118
+ topicId: number;
119
+ sessionId: string;
120
+ intent?: LeaseIntent;
121
+ prNumber?: number | null;
122
+ dryRun?: boolean;
123
+ }): PrHandLeaseRecord | null;
124
+ /** Release my lease (terminal status), tombstoned briefly to guard the re-grab race (§3.7). */
125
+ release(key: string, topicId: number, status: LeaseTombstoneStatus): void;
126
+ /** Read view for GET /pr-leases — each record + its DERIVED (honest) liveness. */
127
+ list(): Array<PrHandLeaseRecord & {
128
+ key: string;
129
+ liveness: DerivedLeaseLiveness;
130
+ }>;
131
+ private derivedLiveness;
132
+ /**
133
+ * Raw same-machine liveness probe: is the holder's tmux session in the
134
+ * in-memory running set? Same key at write + lookup (M-C). On probe error,
135
+ * fail toward LIVE (yield) — uncertainty about the probe must not seize a
136
+ * holder; the caller's past-ceiling path handles a persistently-wedged probe.
137
+ */
138
+ private isSessionRunning;
139
+ private acquireUnlocked;
140
+ /** Read with fail-OPEN on corrupt/absent (§3.4 step 1 / M10). */
141
+ private readAll;
142
+ private readAllUnlocked;
143
+ private pruneExpiredTombstones;
144
+ private persist;
145
+ /** One process-wide lock (matching ResumeQueue's single lockPath); 'wx' first-writer-wins. */
146
+ private withLock;
147
+ private recordFailOpen;
148
+ private attention;
149
+ private audit;
150
+ }
151
+ //# sourceMappingURL=PrHandLease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrHandLease.d.ts","sourceRoot":"","sources":["../../src/core/PrHandLease.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,448 @@
1
+ /**
2
+ * PrHandLease — per-branch ownership lease so two of the agent's OWN concurrent
3
+ * sessions ("hands") cannot push competing commits to the same branch.
4
+ *
5
+ * Spec: docs/specs/parallel-hand-pr-lease.md (review-convergence + approved).
6
+ * Closes the push-competition cause of the 2026-06-15 PR #1183 merge thrash.
7
+ *
8
+ * Design anchors (from the converged spec):
9
+ * - Identity = holderTopicId (stable across the constant session respawns);
10
+ * holderSessionId is a LIVENESS-PROBE HANDLE ONLY, never the identity (§3.2/B2).
11
+ * - ONE process-wide lock guards the single JSON file (matching ResumeQueue's
12
+ * single lockPath); per-record compare-and-swap under that lock (§3.3/B3/M-D).
13
+ * - Liveness probe is TTL-gated (only when a lease is past ttl) and reads the
14
+ * in-memory running set — never a tmux exec (§3.5/M4).
15
+ * - A holder on a DIFFERENT machine is NEVER judged dead from local session
16
+ * absence (§3.5/M6); foreign-machine holder → fail-closed → treat as live.
17
+ * - maxHold ceiling is liveness-discriminated: a DEAD/foreign-unverified holder
18
+ * past the ceiling is CAS-seized + attention; a LIVE same-machine holder past
19
+ * the ceiling is NOT seized — the caller escalates instead (§3.9/codex#3).
20
+ * - Unreadable/corrupt/absent state file → fail-OPEN (treat as no lease) (§3.4/M10).
21
+ * - dryRun leases carry dryRun:true; non-acquisition readers MUST ignore them (§5).
22
+ *
23
+ * This module is the STORE + the pure key-derivation helpers. The PreToolUse Bash
24
+ * hook (pr-hand-lease-guard.js) and the GET /pr-leases route consume it. Enforcement
25
+ * is the hook's (the real chokepoint where agent `git push` runs); SafeGitExecutor
26
+ * carries the same check as defense-in-depth for instar's own internal pushes.
27
+ */
28
+ import fs from 'node:fs';
29
+ import path from 'node:path';
30
+ import os from 'node:os';
31
+ import { SafeGitExecutor } from './SafeGitExecutor.js';
32
+ import { SafeFsExecutor } from './SafeFsExecutor.js';
33
+ const DEFAULT_TTL_MS = 30 * 60 * 1000; // 30m dead-holder backstop
34
+ const DEFAULT_MAX_HOLD_MS = 90 * 60 * 1000; // 90m absolute ceiling
35
+ const TOMBSTONE_WINDOW_MS = 5 * 1000; // ≤5s re-grab guard
36
+ /**
37
+ * Canonicalize a `git push` command's destination ref to `refs/heads/<name>`.
38
+ * Two-tier (§3.1): explicit refspec / local @{push} fast path (no network);
39
+ * `git push --dry-run --porcelain` only for the ambiguous case, timeout-bounded.
40
+ * Returns null when no branch key is derivable (detached, tag, delete, resolver
41
+ * error/timeout) → the caller FAILS OPEN (does not gate).
42
+ */
43
+ export function canonicalPushKey(command, cwd, opts) {
44
+ // Extract the git push invocation from a possibly-composite command
45
+ // (cd && git push, env-prefix, git -C path push, command git push, multiline).
46
+ // We do not try to defeat obfuscation/aliases/script-bodies — that is an
47
+ // accepted residual evasion of a cooperative guard (§2 non-goal).
48
+ const pushMatch = command.match(/\bgit\b[^\n;&|]*\bpush\b([^\n;&|]*)/);
49
+ if (!pushMatch)
50
+ return null;
51
+ const tail = pushMatch[1] ?? '';
52
+ // Ref deletion → no commit content → not gated.
53
+ if (/--delete\b/.test(tail) || /(^|\s):refs?\/|(^|\s):\S/.test(tail))
54
+ return null;
55
+ // -C <dir> overrides cwd for resolution.
56
+ const cMatch = command.match(/\bgit\b\s+(?:[^\n]*?\s)?-C\s+(\S+)/);
57
+ const effectiveCwd = cMatch ? resolveDir(cwd, stripQuotes(cMatch[1])) : cwd;
58
+ // Fast path: an explicit refspec token (last non-flag arg that isn't the remote).
59
+ const explicit = parseExplicitRefspec(tail);
60
+ if (explicit)
61
+ return `branch:${explicit}`;
62
+ // No explicit refspec → resolve the push destination locally via git's own
63
+ // ref resolution (no network — `rev-parse @{push}` reads config only). If that
64
+ // can't resolve it (no upstream / ambiguous push.default), FAIL-OPEN (no key):
65
+ // we never contact the remote from the push hot-path, and an unresolvable ref is
66
+ // the safe direction (the push proceeds). This deliberately drops the earlier
67
+ // remote-contacting `push --dry-run` fallback — it removes the hang surface the
68
+ // round-4 review flagged AND keeps the resolver off the network entirely.
69
+ const local = resolveLocalPushRef(effectiveCwd, opts?.execTimeoutMs);
70
+ return local ? `branch:${local}` : null;
71
+ }
72
+ function stripQuotes(s) {
73
+ return s.replace(/^['"]|['"]$/g, '');
74
+ }
75
+ function resolveDir(cwd, dir) {
76
+ return path.isAbsolute(dir) ? dir : path.resolve(cwd, dir);
77
+ }
78
+ /** Parse `origin foo` / `origin HEAD:foo` / `origin HEAD:refs/heads/foo` → refs/heads/foo. */
79
+ function parseExplicitRefspec(tail) {
80
+ // Drop flags; tokens left are [remote] [refspec...].
81
+ const tokens = tail.split(/\s+/).filter((t) => t && !t.startsWith('-'));
82
+ // remote is the first bare token; a refspec is any token containing ':' or a bare branch after the remote.
83
+ for (const tok of tokens) {
84
+ const t = stripQuotes(tok);
85
+ if (t.includes(':')) {
86
+ const dst = t.split(':')[1];
87
+ return normalizeHeadsRef(dst);
88
+ }
89
+ }
90
+ // bare `git push origin foo` → tokens [origin, foo]; foo is the destination branch.
91
+ if (tokens.length >= 2) {
92
+ return normalizeHeadsRef(stripQuotes(tokens[1]));
93
+ }
94
+ return null;
95
+ }
96
+ function normalizeHeadsRef(ref) {
97
+ if (!ref)
98
+ return null;
99
+ if (ref.startsWith('refs/heads/'))
100
+ return ref;
101
+ if (ref.startsWith('refs/'))
102
+ return null; // tag/non-heads → not gated
103
+ if (ref === 'HEAD')
104
+ return null; // unresolved here; local resolver handles it
105
+ return `refs/heads/${ref}`;
106
+ }
107
+ function resolveLocalPushRef(cwd, timeoutMs) {
108
+ try {
109
+ const out = SafeGitExecutor.readSync(['rev-parse', '--symbolic-full-name', '@{push}'], {
110
+ cwd,
111
+ operation: 'pr-hand-lease canonical-key resolve',
112
+ encoding: 'utf-8',
113
+ timeout: timeoutMs ?? 2000,
114
+ stdio: ['ignore', 'pipe', 'ignore'],
115
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
116
+ }).trim();
117
+ if (out.startsWith('refs/remotes/')) {
118
+ // refs/remotes/<remote>/<branch> → refs/heads/<branch>
119
+ const parts = out.split('/');
120
+ const branch = parts.slice(3).join('/');
121
+ return branch ? `refs/heads/${branch}` : null;
122
+ }
123
+ if (out.startsWith('refs/heads/'))
124
+ return out;
125
+ return null;
126
+ }
127
+ catch {
128
+ // @silent-fallback-ok: no upstream / unresolvable push.default → no key → caller FAILS OPEN
129
+ // (the push proceeds). We never contact the remote from the push hot-path.
130
+ return null;
131
+ }
132
+ }
133
+ export class PrHandLease {
134
+ d;
135
+ filePath;
136
+ lockPath;
137
+ now;
138
+ failOpenCounts = new Map();
139
+ constructor(deps) {
140
+ this.d = deps;
141
+ const root = path.join(deps.stateDir, 'state');
142
+ this.filePath = path.join(root, 'pr-hand-leases.json');
143
+ this.lockPath = path.join(root, 'pr-hand-leases.lock');
144
+ this.now = deps.now ?? (() => Date.now());
145
+ }
146
+ /** The path the BackupManager denylist must exclude (ephemeral, never restored). */
147
+ static stateFileRelPath() {
148
+ return 'state/pr-hand-leases.json';
149
+ }
150
+ /**
151
+ * Evaluate a push against the lease for `key` (from canonicalPushKey).
152
+ * Returns the chokepoint decision. NEVER throws — any internal error resolves
153
+ * to allow (the hook's own-crash fail-open is the outer guarantee; this is the
154
+ * inner one).
155
+ */
156
+ evaluate(key, myTopicId, mySessionId) {
157
+ try {
158
+ const all = this.readAll(); // fail-open on corrupt → {}
159
+ const rec = all[key];
160
+ if (!rec || rec.tombstone) {
161
+ return { decision: 'allow', reason: rec?.tombstone ? 'tombstoned-free' : 'no-lease' };
162
+ }
163
+ if (rec.holderTopicId === myTopicId) {
164
+ return { decision: 'allow', reason: 'own-topic', holder: rec };
165
+ }
166
+ // dryRun leases never block a different hand (observe-only).
167
+ if (rec.dryRun) {
168
+ return { decision: 'allow', reason: 'foreign-dryrun-ignored', holder: rec };
169
+ }
170
+ const t = this.now();
171
+ const sameMachine = rec.holderMachineId === this.d.machineId;
172
+ const ttlExpired = t - rec.renewedAt > rec.ttlMs;
173
+ const pastCeiling = t - rec.acquiredAt > rec.maxHoldMs;
174
+ // Ceiling override FIRST (§3.9 precedence — overrides foreign-machine conservatism).
175
+ if (pastCeiling) {
176
+ if (sameMachine && this.isSessionRunning(rec)) {
177
+ // LIVE same-machine holder past ceiling → escalate, do NOT seize (codex#3/§3.9).
178
+ return { decision: 'escalate', reason: 'live-holder-past-ceiling', holder: rec };
179
+ }
180
+ // DEAD same-machine OR foreign-machine-unverified past ceiling → stale → caller seizes (+attention).
181
+ return { decision: 'allow', reason: 'stale-past-ceiling', holder: rec };
182
+ }
183
+ // Within ceiling:
184
+ if (!ttlExpired) {
185
+ // TTL-gate: a fresh lease is live without probing.
186
+ return { decision: 'deny', reason: 'live-foreign-lease', holder: rec };
187
+ }
188
+ if (!sameMachine) {
189
+ // Foreign-machine holder within ceiling, past TTL → NEVER judged dead from local
190
+ // session absence → yield (§3.5/M6).
191
+ return { decision: 'deny', reason: 'foreign-machine-within-ceiling', holder: rec };
192
+ }
193
+ // Same-machine, TTL-expired → probe the in-memory running set.
194
+ if (this.isSessionRunning(rec)) {
195
+ return { decision: 'deny', reason: 'live-foreign-lease', holder: rec };
196
+ }
197
+ return { decision: 'allow', reason: 'stale-dead', holder: rec };
198
+ }
199
+ catch (err) {
200
+ // Inner fail-open — never block on an internal error.
201
+ this.audit({ event: 'evaluate-error', key, error: String(err) });
202
+ return { decision: 'allow', reason: 'eval-error-failopen' };
203
+ }
204
+ }
205
+ /** Acquire or renew the lease for `key` for my (topic, session, machine). Atomic CAS under the lock. */
206
+ acquireOrRenew(key, holder) {
207
+ return this.withLock(() => {
208
+ const all = this.readAllUnlocked();
209
+ const existing = all[key];
210
+ const t = this.now();
211
+ if (existing && !existing.tombstone && existing.holderTopicId === holder.topicId) {
212
+ existing.renewedAt = t;
213
+ existing.holderSessionId = holder.sessionId;
214
+ if (holder.intent)
215
+ existing.intent = holder.intent;
216
+ if (holder.prNumber !== undefined)
217
+ existing.prNumber = holder.prNumber;
218
+ if (holder.dryRun !== undefined)
219
+ existing.dryRun = holder.dryRun;
220
+ this.persist(all);
221
+ this.audit({ event: 'renew', key, topicId: holder.topicId, dryRun: !!holder.dryRun });
222
+ return existing;
223
+ }
224
+ const rec = {
225
+ holderTopicId: holder.topicId,
226
+ holderSessionId: holder.sessionId,
227
+ holderMachineId: this.d.machineId,
228
+ prNumber: holder.prNumber ?? null,
229
+ intent: holder.intent ?? 'build',
230
+ acquiredAt: t,
231
+ renewedAt: t,
232
+ ttlMs: DEFAULT_TTL_MS,
233
+ maxHoldMs: DEFAULT_MAX_HOLD_MS,
234
+ dryRun: holder.dryRun || undefined,
235
+ };
236
+ all[key] = rec;
237
+ this.persist(all);
238
+ this.audit({ event: 'acquire', key, topicId: holder.topicId, dryRun: !!holder.dryRun });
239
+ return rec;
240
+ });
241
+ }
242
+ /**
243
+ * Atomic-CAS takeover of a stale lease (§3.3/B3). Writes my record only if the
244
+ * on-disk record still matches the observed (holderTopicId, acquiredAt). Returns
245
+ * the new record on success, or null if another hand healed first (loser yields).
246
+ */
247
+ takeOverIfStale(key, observed, holder) {
248
+ return this.withLock(() => {
249
+ const all = this.readAllUnlocked();
250
+ const cur = all[key];
251
+ if (!cur || cur.tombstone) {
252
+ // Already free → acquire fresh.
253
+ return this.acquireUnlocked(all, key, holder);
254
+ }
255
+ // CAS precondition: the record must be exactly the one we observed as stale.
256
+ if (cur.holderTopicId !== observed.holderTopicId || cur.acquiredAt !== observed.acquiredAt) {
257
+ this.audit({ event: 'takeover-cas-lost', key });
258
+ return null; // another hand healed first → yield
259
+ }
260
+ const forcedRelease = this.now() - cur.acquiredAt > cur.maxHoldMs;
261
+ const rec = this.acquireUnlocked(all, key, holder);
262
+ this.audit({
263
+ event: forcedRelease ? 'auto-heal-ceiling' : 'auto-heal-dead',
264
+ key,
265
+ priorTopicId: observed.holderTopicId,
266
+ priorMachineId: cur.holderMachineId,
267
+ });
268
+ if (forcedRelease || cur.holderMachineId !== this.d.machineId) {
269
+ this.attention('pr-lease-forced-release', `Branch ${key} lease force-released from topic ${observed.holderTopicId} (machine ${cur.holderMachineId}).`);
270
+ }
271
+ return rec;
272
+ });
273
+ }
274
+ /** Release my lease (terminal status), tombstoned briefly to guard the re-grab race (§3.7). */
275
+ release(key, topicId, status) {
276
+ this.withLock(() => {
277
+ const all = this.readAllUnlocked();
278
+ const cur = all[key];
279
+ if (cur && cur.holderTopicId === topicId && !cur.tombstone) {
280
+ cur.tombstone = { status, at: this.now() };
281
+ this.persist(all);
282
+ this.audit({ event: 'release', key, topicId, status });
283
+ }
284
+ return undefined;
285
+ });
286
+ }
287
+ /** Read view for GET /pr-leases — each record + its DERIVED (honest) liveness. */
288
+ list() {
289
+ const all = this.readAll();
290
+ const out = [];
291
+ for (const [key, rec] of Object.entries(all)) {
292
+ out.push({ ...rec, key, liveness: this.derivedLiveness(rec) });
293
+ }
294
+ return out;
295
+ }
296
+ // ---- internals ----
297
+ derivedLiveness(rec) {
298
+ if (rec.tombstone)
299
+ return 'tombstoned';
300
+ if (rec.holderMachineId !== this.d.machineId)
301
+ return 'foreign-machine';
302
+ const ttlExpired = this.now() - rec.renewedAt > rec.ttlMs;
303
+ if (!ttlExpired)
304
+ return 'live';
305
+ return this.isSessionRunning(rec) ? 'live' : 'stale-dead';
306
+ }
307
+ /**
308
+ * Raw same-machine liveness probe: is the holder's tmux session in the
309
+ * in-memory running set? Same key at write + lookup (M-C). On probe error,
310
+ * fail toward LIVE (yield) — uncertainty about the probe must not seize a
311
+ * holder; the caller's past-ceiling path handles a persistently-wedged probe.
312
+ */
313
+ isSessionRunning(rec) {
314
+ try {
315
+ return this.d.runningSessionNames().includes(rec.holderSessionId);
316
+ }
317
+ catch {
318
+ // @silent-fallback-ok: probe unavailable → treat live (yield), bounded by the ceiling override.
319
+ return true;
320
+ }
321
+ }
322
+ acquireUnlocked(all, key, holder) {
323
+ const t = this.now();
324
+ const rec = {
325
+ holderTopicId: holder.topicId,
326
+ holderSessionId: holder.sessionId,
327
+ holderMachineId: this.d.machineId,
328
+ prNumber: holder.prNumber ?? null,
329
+ intent: holder.intent ?? 'build',
330
+ acquiredAt: t,
331
+ renewedAt: t,
332
+ ttlMs: DEFAULT_TTL_MS,
333
+ maxHoldMs: DEFAULT_MAX_HOLD_MS,
334
+ dryRun: holder.dryRun || undefined,
335
+ };
336
+ all[key] = rec;
337
+ this.persist(all);
338
+ return rec;
339
+ }
340
+ /** Read with fail-OPEN on corrupt/absent (§3.4 step 1 / M10). */
341
+ readAll() {
342
+ try {
343
+ if (!fs.existsSync(this.filePath))
344
+ return {};
345
+ const raw = fs.readFileSync(this.filePath, 'utf-8');
346
+ const parsed = JSON.parse(raw);
347
+ return this.pruneExpiredTombstones(parsed);
348
+ }
349
+ catch (err) {
350
+ // @silent-fallback-ok: corrupt/torn/absent lease file → fail-OPEN (treat as no lease,
351
+ // the push proceeds — a missing lease must NEVER block all pushes). NOT silent:
352
+ // recordFailOpen() logs every occurrence and raises an attention item on recurrence.
353
+ this.recordFailOpen('readAll');
354
+ return {};
355
+ }
356
+ }
357
+ readAllUnlocked() {
358
+ return this.readAll();
359
+ }
360
+ pruneExpiredTombstones(all) {
361
+ const t = this.now();
362
+ for (const [key, rec] of Object.entries(all)) {
363
+ if (rec.tombstone && t - rec.tombstone.at > TOMBSTONE_WINDOW_MS)
364
+ delete all[key];
365
+ }
366
+ return all;
367
+ }
368
+ persist(all) {
369
+ const dir = path.dirname(this.filePath);
370
+ fs.mkdirSync(dir, { recursive: true });
371
+ const tmp = `${this.filePath}.${process.pid}.tmp`;
372
+ const fd = fs.openSync(tmp, 'w');
373
+ try {
374
+ fs.writeSync(fd, JSON.stringify(all, null, 2));
375
+ fs.fsyncSync(fd);
376
+ }
377
+ finally {
378
+ fs.closeSync(fd);
379
+ }
380
+ fs.renameSync(tmp, this.filePath); // atomic swap — readers never see a torn file
381
+ }
382
+ /** One process-wide lock (matching ResumeQueue's single lockPath); 'wx' first-writer-wins. */
383
+ withLock(fn) {
384
+ const dir = path.dirname(this.lockPath);
385
+ fs.mkdirSync(dir, { recursive: true });
386
+ const deadline = this.now() + 5000;
387
+ // simple spin with O_EXCL; the critical section is a sub-ms file rewrite.
388
+ for (;;) {
389
+ let fd = null;
390
+ try {
391
+ fd = fs.openSync(this.lockPath, 'wx');
392
+ fs.writeSync(fd, JSON.stringify({ pid: process.pid, host: os.hostname(), at: this.now() }));
393
+ fs.closeSync(fd);
394
+ fd = null;
395
+ try {
396
+ return fn();
397
+ }
398
+ finally {
399
+ try {
400
+ SafeFsExecutor.safeUnlinkSync(this.lockPath, { operation: 'PrHandLease lock release' });
401
+ }
402
+ catch { /* @silent-fallback-ok: lock cleanup best-effort */ }
403
+ }
404
+ }
405
+ catch (err) {
406
+ if (fd !== null) {
407
+ try {
408
+ fs.closeSync(fd);
409
+ }
410
+ catch { /* ignore */ }
411
+ }
412
+ if (err?.code === 'EEXIST') {
413
+ // Stale lock recovery: if the lock is older than the deadline window, steal it.
414
+ if (this.now() > deadline) {
415
+ try {
416
+ SafeFsExecutor.safeUnlinkSync(this.lockPath, { operation: 'PrHandLease stale-lock steal' });
417
+ }
418
+ catch { /* ignore */ }
419
+ continue;
420
+ }
421
+ continue; // retry
422
+ }
423
+ throw err;
424
+ }
425
+ }
426
+ }
427
+ recordFailOpen(where) {
428
+ const n = (this.failOpenCounts.get(where) ?? 0) + 1;
429
+ this.failOpenCounts.set(where, n);
430
+ this.audit({ event: 'fail-open', where, count: n });
431
+ if (n >= 3) {
432
+ this.attention('pr-lease-failopen', `pr-hand-leases.json repeatedly unreadable (${n}x at ${where}) — lease protection degraded; check the state file.`);
433
+ }
434
+ }
435
+ attention(kind, detail) {
436
+ try {
437
+ this.d.onAttention?.({ kind, detail });
438
+ }
439
+ catch { /* @silent-fallback-ok */ }
440
+ }
441
+ audit(row) {
442
+ try {
443
+ this.d.onAudit?.({ ...row, ts: new Date(this.now()).toISOString() });
444
+ }
445
+ catch { /* @silent-fallback-ok */ }
446
+ }
447
+ }
448
+ //# sourceMappingURL=PrHandLease.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrHandLease.js","sourceRoot":"","sources":["../../src/core/PrHandLease.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"devGatedFeatures.d.ts","sourceRoot":"","sources":["../../src/core/devGatedFeatures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EAqT/C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,cAAc,GACd,gBAAgB,GAChB,sBAAsB,GACtB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAuHnD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAO5E"}
1
+ {"version":3,"file":"devGatedFeatures.d.ts","sourceRoot":"","sources":["../../src/core/devGatedFeatures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,kBAAkB,EAAE,eAAe,EA2T/C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,cAAc,GACd,gBAAgB,GAChB,sBAAsB,GACtB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EAuHnD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAO5E"}
@@ -120,6 +120,12 @@ export const DEV_GATED_FEATURES = [
120
120
  description: 'Playwright profile↔accounts registry + boot awareness + activate.',
121
121
  justification: 'Stores vault secret NAMES only (never values) + browser-profile metadata; reads are advisory signal; the only destructive op (activate: MCP-config rewrite + session restart) ships dryRun:true and is reversible; dev-dogfooded.',
122
122
  },
123
+ {
124
+ name: 'prHandLease',
125
+ configPath: 'monitoring.prHandLease.enabled',
126
+ description: 'Per-branch PR-push lease so two of the agent’s own concurrent sessions can’t push competing commits to the same branch (spec: parallel-hand-pr-lease).',
127
+ justification: 'Ships dryRun:true (the dry-run canary): on a dev agent the PreToolUse hook + the /pr-leases/evaluate route run the FULL decision loop and AUDIT every would-deny, but the route returns decision:allow (wouldDeny flag) while dryRun holds, so NO push is ever blocked until a deliberate dryRun:false. Coordinates the agent’s OWN cooperating hands only — never authority over a principal, never external egress; every uncertainty (corrupt state, server down, hook crash, no branch key) fails OPEN (allows the push). No spend, no destructive action while the canary holds. Same dogfooding posture as topicProfiles / credentialRepointing.',
128
+ },
123
129
  // ── multi-machine seamlessness coherence layers (WS3 / WS1.3 / WS4.1 / WS4.3),
124
130
  // MOVED from hardcoded `false` in ConfigDefaults on 2026-06-13 per operator
125
131
  // directive topic 13481 ("NOTHING should ship dark on development agents —