dorfl 0.11.0 → 0.11.2

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 (72) hide show
  1. package/dist/arbiter-refs.d.ts +139 -0
  2. package/dist/arbiter-refs.d.ts.map +1 -0
  3. package/dist/arbiter-refs.js +114 -0
  4. package/dist/arbiter-refs.js.map +1 -0
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +32 -18
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts +73 -12
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +28 -0
  11. package/dist/config.js.map +1 -1
  12. package/dist/do-config.d.ts +21 -1
  13. package/dist/do-config.d.ts.map +1 -1
  14. package/dist/do-config.js +19 -0
  15. package/dist/do-config.js.map +1 -1
  16. package/dist/do.d.ts +14 -2
  17. package/dist/do.d.ts.map +1 -1
  18. package/dist/do.js +108 -14
  19. package/dist/do.js.map +1 -1
  20. package/dist/env-config.d.ts.map +1 -1
  21. package/dist/env-config.js +3 -0
  22. package/dist/env-config.js.map +1 -1
  23. package/dist/harness.d.ts +29 -0
  24. package/dist/harness.d.ts.map +1 -1
  25. package/dist/harness.js.map +1 -1
  26. package/dist/ledger-write.d.ts.map +1 -1
  27. package/dist/ledger-write.js +38 -3
  28. package/dist/ledger-write.js.map +1 -1
  29. package/dist/needs-attention.d.ts +43 -0
  30. package/dist/needs-attention.d.ts.map +1 -1
  31. package/dist/needs-attention.js +211 -50
  32. package/dist/needs-attention.js.map +1 -1
  33. package/dist/pi-harness.d.ts.map +1 -1
  34. package/dist/pi-harness.js +109 -13
  35. package/dist/pi-harness.js.map +1 -1
  36. package/dist/protocol/WORK-CONTRACT.md +11 -2
  37. package/dist/protocol/spec-template.md +1 -1
  38. package/dist/protocol/task-template.md +1 -1
  39. package/dist/reap-agent-tree.d.ts +108 -0
  40. package/dist/reap-agent-tree.d.ts.map +1 -0
  41. package/dist/reap-agent-tree.js +173 -0
  42. package/dist/reap-agent-tree.js.map +1 -0
  43. package/dist/repo-config.d.ts +1 -1
  44. package/dist/repo-config.d.ts.map +1 -1
  45. package/dist/repo-config.js +19 -2
  46. package/dist/repo-config.js.map +1 -1
  47. package/dist/run.d.ts.map +1 -1
  48. package/dist/run.js +4 -3
  49. package/dist/run.js.map +1 -1
  50. package/dist/skills/drive-tasks/SKILL.md +20 -2
  51. package/dist/skills/setup/protocol/WORK-CONTRACT.md +11 -2
  52. package/dist/skills/setup/protocol/spec-template.md +1 -1
  53. package/dist/skills/setup/protocol/task-template.md +1 -1
  54. package/dist/worktree-writer-lock.d.ts +99 -0
  55. package/dist/worktree-writer-lock.d.ts.map +1 -0
  56. package/dist/worktree-writer-lock.js +158 -0
  57. package/dist/worktree-writer-lock.js.map +1 -0
  58. package/package.json +1 -1
  59. package/src/arbiter-refs.ts +222 -0
  60. package/src/cli.ts +57 -17
  61. package/src/config.ts +90 -12
  62. package/src/do-config.ts +38 -0
  63. package/src/do.ts +158 -19
  64. package/src/env-config.ts +3 -0
  65. package/src/harness.ts +30 -0
  66. package/src/ledger-write.ts +41 -5
  67. package/src/needs-attention.ts +282 -59
  68. package/src/pi-harness.ts +109 -15
  69. package/src/reap-agent-tree.ts +221 -0
  70. package/src/repo-config.ts +19 -1
  71. package/src/run.ts +4 -3
  72. package/src/worktree-writer-lock.ts +217 -0
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from 'node:fs';
3
3
  import { NullHarness, pidAlive, registerHarness, } from './harness.js';
4
4
  import { generateSessionPath } from './session-path.js';
5
5
  import { lastAssistantText } from './watch-session.js';
6
+ import { reapProcessGroup } from './reap-agent-tree.js';
6
7
  /**
7
8
  * The **pi** harness adapter (ADR §5) — the first real agent harness
8
9
  * `dorfl` drives. It fulfils the harness seam introduced by
@@ -186,8 +187,47 @@ export class PiHarness {
186
187
  cwd: input.dir,
187
188
  env: input.env ?? process.env,
188
189
  stdio: ['pipe', 'pipe', 'pipe'],
190
+ // PROCESS-GROUP LEADER (observation
191
+ // `checkpoint-releases-lock-while-predecessor-agent-still-writes`): pi's
192
+ // pgid becomes its own pid, so the deadline stop can signal the WHOLE
193
+ // agent tree with `kill(-pgid)` and VERIFY it is gone. Without this,
194
+ // `child.kill()` reached exactly one pid: subagents / MCP servers / tool
195
+ // subshells survived, were re-parented to init (so no ppid walk could even
196
+ // find them), and kept writing into the worktree while a SUCCESSOR agent
197
+ // was already editing it. A pgid is inherited by every descendant and is
198
+ // unaffected by re-parenting, which is why it is the only usable handle.
199
+ // We deliberately do NOT `unref()` here: the parent keeps supervising the
200
+ // child (and forwards its own termination to the group, below).
201
+ detached: true,
189
202
  });
190
203
  record.pid = child.pid; // the liveness anchor, recorded like spawnSync.
204
+ // The group id equals the leader's pid because we spawned `detached`.
205
+ const pgid = child.pid;
206
+ // `detached: true` takes pi OUT of our terminal's foreground process group,
207
+ // so a Ctrl-C / `kill` aimed at the runner would no longer reach it — which
208
+ // would WIDEN the very "aborting `do` does not kill the spawned agent tree"
209
+ // gap this change is closing. Forward our own termination to the group for
210
+ // as long as the child is live, so detaching strictly improves reachability
211
+ // instead of trading one orphan class for another.
212
+ const forwardSignal = (signal) => () => {
213
+ if (pgid === undefined) {
214
+ return;
215
+ }
216
+ try {
217
+ process.kill(-pgid, signal);
218
+ }
219
+ catch {
220
+ // Already gone; nothing to forward to.
221
+ }
222
+ };
223
+ const onSigint = forwardSignal('SIGINT');
224
+ const onSigterm = forwardSignal('SIGTERM');
225
+ process.on('SIGINT', onSigint);
226
+ process.on('SIGTERM', onSigterm);
227
+ const stopForwarding = () => {
228
+ process.off('SIGINT', onSigint);
229
+ process.off('SIGTERM', onSigterm);
230
+ };
191
231
  let stderr = '';
192
232
  child.stderr?.on('data', (chunk) => {
193
233
  stderr += chunk.toString('utf8');
@@ -225,19 +265,32 @@ export class PiHarness {
225
265
  return;
226
266
  }
227
267
  timedOut = true;
268
+ // Signal the whole GROUP, not just pi: the descendants are exactly the
269
+ // processes that outlive it and keep writing to the worktree.
228
270
  try {
229
- child.kill('SIGTERM');
271
+ if (pgid !== undefined) {
272
+ process.kill(-pgid, 'SIGTERM');
273
+ }
274
+ else {
275
+ child.kill('SIGTERM');
276
+ }
230
277
  }
231
278
  catch {
232
- // Best-effort: a already-exited child throws ESRCH; the `exit`
233
- // handler will still settle the promise.
279
+ // Best-effort: an already-exited group throws ESRCH; the `exit`
280
+ // handler will still settle the promise, and the post-exit reap below
281
+ // is what actually VERIFIES the tree is gone.
234
282
  }
235
283
  hardTimer = setTimeout(() => {
236
284
  if (settled) {
237
285
  return;
238
286
  }
239
287
  try {
240
- child.kill('SIGKILL');
288
+ if (pgid !== undefined) {
289
+ process.kill(-pgid, 'SIGKILL');
290
+ }
291
+ else {
292
+ child.kill('SIGKILL');
293
+ }
241
294
  }
242
295
  catch {
243
296
  // Best-effort: see above.
@@ -253,6 +306,7 @@ export class PiHarness {
253
306
  }
254
307
  settled = true;
255
308
  clearDeadlineTimers();
309
+ stopForwarding();
256
310
  reject(new Error(`failed to spawn pi (${this.piBin}): ${err.message}`));
257
311
  });
258
312
  // Resolve on `exit` (pi itself terminated), NOT `close`: `close` waits for
@@ -265,6 +319,7 @@ export class PiHarness {
265
319
  }
266
320
  settled = true;
267
321
  clearDeadlineTimers();
322
+ stopForwarding();
268
323
  // Release our end of the stdio pipes so a leaked grandchild's inherited
269
324
  // FDs stop keeping our streams referenced; `unref` the child handle too.
270
325
  child.stdout?.destroy();
@@ -272,15 +327,56 @@ export class PiHarness {
272
327
  child.stdin?.destroy();
273
328
  child.unref?.();
274
329
  const status = code ?? -1;
275
- resolve({
276
- ok: status === 0 && !timedOut,
277
- record,
278
- detail: status === 0 && !timedOut ? undefined : stderr.trim() || undefined,
279
- timedOut: timedOut ? true : undefined,
280
- // Read the agent's ANSWER from the `.jsonl` at `exit` — the same
281
- // last-assistant-text read `launch` does at return (task
282
- // `harness-agent-output`); the process has exited so the log is final.
283
- output: readLastAssistantText(sessionFile),
330
+ const settleWith = (reap) => {
331
+ resolve({
332
+ ok: status === 0 && !timedOut,
333
+ record,
334
+ detail: status === 0 && !timedOut
335
+ ? undefined
336
+ : stderr.trim() || undefined,
337
+ timedOut: timedOut ? true : undefined,
338
+ ...(reap ? { reap } : {}),
339
+ // Read the agent's ANSWER from the `.jsonl` at `exit` — the same
340
+ // last-assistant-text read `launch` does at return (task
341
+ // `harness-agent-output`); the process has exited so the log is final.
342
+ output: readLastAssistantText(sessionFile),
343
+ });
344
+ };
345
+ if (!timedOut || pgid === undefined) {
346
+ // Normal exit: we signalled nothing, so there is nothing to prove and
347
+ // nothing to kill. Byte-for-byte the pre-existing behaviour — in
348
+ // particular we do NOT reap a group the agent may have deliberately left
349
+ // running behind a successful run.
350
+ settleWith();
351
+ return;
352
+ }
353
+ // DEADLINE STOP: pi's own exit says NOTHING about its descendants — that
354
+ // assumption is the defect. Before this promise resolves (which is the
355
+ // runner's cue to save WIP, release the lock and dispatch a SUCCESSOR into
356
+ // this same worktree), reap the group and VERIFY it is gone. Bounded by
357
+ // construction, so this cannot reintroduce the resolve-on-`exit` hang the
358
+ // doc-comment above guards against: a tree that will not die resolves with
359
+ // `reaped: false` and the caller refuses to release the lock.
360
+ void reapProcessGroup({ pgid })
361
+ .then((result) => {
362
+ settleWith({
363
+ reaped: result.reaped,
364
+ pgid,
365
+ escalatedToSigkill: result.escalatedToSigkill,
366
+ detail: result.detail,
367
+ });
368
+ })
369
+ .catch((err) => {
370
+ // A throw here means we could not even RUN the verification, which is
371
+ // indistinguishable from "might still be alive" — report it as an
372
+ // unproven reap rather than silently claiming success.
373
+ settleWith({
374
+ reaped: false,
375
+ pgid,
376
+ detail: `could not verify that the agent process group ${pgid} exited ` +
377
+ `(${err instanceof Error ? err.message : String(err)}); treating ` +
378
+ 'the predecessor as possibly still writing to the worktree.',
379
+ });
284
380
  });
285
381
  });
286
382
  // Feed the same prepared prompt on stdin, then close it (pi reads to EOF).
@@ -1 +1 @@
1
- {"version":3,"file":"pi-harness.js","sourceRoot":"","sources":["../src/pi-harness.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pi-harness.js","sourceRoot":"","sources":["../src/pi-harness.ts"],"names":[],"mappings":"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"}
@@ -98,7 +98,16 @@ work/
98
98
 
99
99
  ### The spec lifecycle: `specs/ready/` (pool) → `specs/tasked/` on `main`; the tasking HOLD is a lock ref
100
100
 
101
- A spec rests in `work/specs/ready/` (the auto-task pool) and, when tasked into tasks, moves durably to `work/specs/tasked/` on `main`. The **folder is the source of truth for tasked-ness**, exactly as `work/tasks/done/` is for tasks. Re-tasking a reshaped spec is `work/specs/tasked/ → work/specs/ready/` (reopen-to-ready, mirroring `tasks/done/ → tasks/ready/`).
101
+ A spec rests in `work/specs/ready/` (the auto-task pool) and, when tasked into tasks, moves durably to `work/specs/tasked/` on `main`. The **folder is the source of truth for tasked-ness**, exactly as `work/tasks/done/` is for tasks.
102
+
103
+ **`work/specs/tasked/` and `work/tasks/done/` are TERMINAL positions — they have NO exits.** They record permanent facts ("this spec was decomposed into tasks", "this task landed") that must never be silently rewound: residence IS the signal, and moving an item OUT of its terminal un-records what actually happened and orphans everything downstream (a rewound `specs/tasked/` orphans its emitted tasks, which still carry `spec:`/`covers:` linkage to a spec that would now claim it was never tasked). Correspondingly, the two won't-proceed terminals are reachable only from the NON-terminal positions: `work/specs/dropped/` from `specs/proposed/`/`specs/ready/`, and `work/tasks/cancelled/` from `tasks/backlog/`/`tasks/ready/`. There is no `specs/tasked/ → specs/dropped/` and no `tasks/done/ → tasks/cancelled/`.
104
+
105
+ **Re-tasking a reshaped spec happens IN PLACE from `work/specs/tasked/`.** The tasker's DESTINATION is always `tasked/` regardless of source (see `TASKING-PROTOCOL.md` §6), so a re-task from `tasked/` is a no-op move that already works, and the folder's only operational jobs — gating AUTO-tasking (the auto-tasker draws from `specs/ready/` only) and satisfying `taskedAfter` (which resolves against `specs/tasked/` residence) — are undisturbed. The boundary between re-tasking and minting a NEW spec is checkable, because a spec settles to Problem / Solution / User Stories / Out of Scope after its one-time trim:
106
+
107
+ - **User Stories UNCHANGED** — the decomposition was wrong. RE-TASK IN PLACE, same slug, same `issue:` thread, and supersede the stale emitted tasks into `work/tasks/cancelled/` (reason: superseded by the re-task).
108
+ - **User Stories CHANGED** — the intent moved. Mint a NEW spec (new slug) that begins in `work/specs/proposed/`/`specs/ready/` and flows normally; the OLD spec stays in `work/specs/tasked/` (its terminal), because it WAS tasked and that fact is permanent. Do NOT drop a spec that was really tasked: the honest signal is a new spec superseding it, not a rewind of the old one (`work/specs/dropped/` is for specs that were NEVER tasked).
109
+
110
+ This is the ASYMMETRY between the two regimes: a spec in `specs/tasked/` can still be re-tasked by a human (tasking is not folder-gated on the human path — the tasker accepts a spec regardless of source and always writes to `tasked/`), but a task in `tasks/done/` is genuinely UNCLAIMABLE, because the claimable predicate is "in `tasks/ready/` on `main` AND no lock held" (conflict-safety rule 7). So a done task cannot be driven in place from its terminal; the only honest answer to "redo landed work" is a NEW forward task, never a rewind of the done record. This matches the DIRECTION-and-LIVENESS rule: completed work is a `tasks/done/` record plus its commit, never a back-filled forward artifact.
102
111
 
103
112
  **The tasking HOLD is a per-item lock, NOT a `work/tasking/` folder.** Tasking a spec acquires the unified per-item lock with `action: task` on `refs/dorfl/lock/spec-<slug>` — a create-only ref push that is self-arbitrating (winner creates it; a concurrent tasker loses the same CAS definitively, no retry budget), so a spec is never double-tasked. The spec body STAYS in `work/specs/ready/` while held (it does not move to a `tasking/` folder). On a **successful tasking** the release performs the durable `work/specs/ready/ → work/specs/tasked/` move on `main` in the SAME runner-owned commit that emits the `tasks/` items, then releases the lock. On an **aborted / unclear** tasking the lock is released with no `main` move (the spec already rests in `specs/ready/`), or the lock is marked `stuck` for a human.
104
113
 
@@ -131,7 +140,7 @@ A spec and a task are **launch snapshots** — they capture intent at creation a
131
140
  - **A SPEC that has drifted** (before tasking) → do NOT task it as-is. Set `needsAnswers: true` on the spec with the discrepancy in its body (or, if it is a small factual correction you are certain of, fix the spec first), so the tasker never emits tasks from a stale spec. A human reconciles, clears the flag, then it is tasked.
132
141
  - **A SPEC that has drifted AFTER it was TASKED** (a mechanism it assumed got retired, a sibling decision superseded it) → do **NOT** move it back to `specs/proposed/`. `specs/proposed/` is the untrusted-admission STAGING position; moving an already-tasked spec there falsely un-records a tasking that really happened and ORPHANS the tasks it already emitted (they still sit in `tasks/backlog`/`tasks/ready` carrying `spec:`/`covers:` linkage to a spec that now claims it was never tasked). Tasked-ness is RESIDENCE in `specs/tasked/` and must never be silently rewound. Instead, two honest mechanisms (use the lighter one that fits):
133
142
  - **Annotate in place (drifted, not yet re-decomposed).** Set `needsAnswers: true` on the spec **while it stays in `specs/tasked/`**, with the drift + what must be re-decomposed in its body, AND set `needsAnswers: true` on every emitted task that is now premised on the dead mechanism so no agent builds it. `needsAnswers: true` on a `specs/tasked/` spec is legal and means exactly _"tasked, but the spec has drifted — do not RE-task or rely on it until reconciled."_ The non-drifted emitted tasks are unaffected and stay promotable.
134
- - **Reopen to re-decompose (the sanctioned move).** When you are ready to re-task from the reconciled spec, use the existing reopen path `specs/tasked/ → specs/ready/` (mirroring `tasks/done/ → tasks/ready/`), reconcile the spec, clear `needsAnswers`, then re-task — which emits corrected tasks. Supersede the stale emitted tasks into `tasks/cancelled/` (reason: superseded by the re-task). There is NO `specs/tasked/ → specs/proposed/` transition.
143
+ - **Re-task in place (the sanctioned move).** When you are ready to re-task from the reconciled spec, reconcile the spec IN PLACE (it stays in `work/specs/tasked/`), clear `needsAnswers`, then re-task. The tasker's DESTINATION is always `tasked/` regardless of source (`TASKING-PROTOCOL.md` §6), so a re-task from `tasked/` is a no-op move that leaves residence unchanged — tasked-ness is not silently rewound — and it emits the corrected tasks. Supersede the stale emitted tasks into `tasks/cancelled/` (reason: superseded by the re-task). There is NO `specs/tasked/ → specs/ready/` transition and NO `specs/tasked/ → specs/proposed/` transition: `specs/tasked/` is TERMINAL. If the user stories have CHANGED (not just the decomposition), that is a new intent — mint a NEW spec instead; the old tasked spec stays in `specs/tasked/` because it really was tasked.
135
144
 
136
145
  The rule is symmetric: _a discrepancy between a doc and reality is not something to paper over — it is exactly the "a human must look" signal `needs-attention` (tasks) / `needsAnswers` (specs) exists to carry._ Cheap to honour, and it stops drift from silently propagating into built work.
137
146
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: <Human Readable Title>
2
+ title: '<Human Readable Title>'
3
3
  slug: <url-safe-slug>
4
4
  # issue: 123 # optional: the issue this spec was spawned from (the surviving thread)
5
5
  # humanOnly: true # optional: a HUMAN must drive the tasking of this spec (a decision). OMIT otherwise.
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: <Human Readable Title>
2
+ title: '<Human Readable Title>'
3
3
  slug: <url-safe-slug>
4
4
  spec: <source-spec-slug> # slug of the work/specs/ready/<slug>.md this task derives from. REQUIRED iff `covers` is set; OMIT for a self-contained chore/refactor (covers: []).
5
5
  # humanOnly: true # gate axis 1 (DECIDED, NARROW): NEVER-for-agents BY NATURE (secrets/release/security). Survives even in the pool work/tasks/ready/. OMIT otherwise — "review this before the agent builds" is the POSITION's job (the task is BIRTHED in work/tasks/backlog/), NOT humanOnly's.
@@ -0,0 +1,108 @@
1
+ /**
2
+ * **Reap a stopped agent's whole PROCESS TREE, and VERIFY it is gone** (spec
3
+ * `graceful-pre-timeout-wip-checkpoint`, observation
4
+ * `checkpoint-releases-lock-while-predecessor-agent-still-writes`).
5
+ *
6
+ * ## The gap this closes
7
+ *
8
+ * The deadline checkpoint used to `child.kill('SIGTERM')` the agent process and
9
+ * treat the signal as if it were the outcome: the moment pi's own `exit` fired,
10
+ * the runner saved the WIP, RELEASED the item lock, and let the next tick
11
+ * dispatch a CONTINUATION agent into the SAME worktree. But `child.kill` signals
12
+ * exactly ONE pid, and a modern agent is a TREE (subagent processes, MCP servers,
13
+ * model proxies, tool subshells). Those descendants survive the parent's SIGTERM,
14
+ * and once pi exits they are re-parented to init — so they can no longer even be
15
+ * FOUND by walking `ppid`, while they keep writing into the worktree.
16
+ *
17
+ * Observed on a real run: the checkpoint saved WIP at 02:13, a continuation agent
18
+ * onboarded into the same worktree at ~02:15, and the predecessor's session log
19
+ * kept being written until 02:19:29 — four minutes INTO the successor's run,
20
+ * whose opening `git status` had already read the tree as clean. Nothing was lost
21
+ * only because the two happened to touch different files. The shape is the
22
+ * defect: one lock, one working tree, two live writers. A write landing after the
23
+ * successor's `git status` is invisible to it; a write landing during its edits
24
+ * can be clobbered either way; and the successor can commit the predecessor's
25
+ * half-finished edits as its own, under a message describing something else.
26
+ *
27
+ * ## Why the PROCESS GROUP is the handle
28
+ *
29
+ * A pid-tree walk cannot work here: the descendants we must reap are precisely
30
+ * the ones that OUTLIVE the parent, and an orphan's `ppid` is gone. A process
31
+ * GROUP id, by contrast, is inherited by every descendant and is NOT changed by
32
+ * re-parenting. So if the agent is spawned as a group LEADER (`detached: true`,
33
+ * making its pgid equal its pid), `kill(-pgid, …)` reaches the entire tree,
34
+ * orphans included — which is why {@link reapProcessGroup} takes a pgid and why
35
+ * `pi-harness.ts` spawns the deadline-capable async launch detached.
36
+ *
37
+ * ## Signal, then VERIFY — never assume
38
+ *
39
+ * The point of the whole module is that sending a signal is not evidence that
40
+ * anything died. So: SIGTERM the group, POLL until it is actually gone, escalate
41
+ * to SIGKILL after a grace, keep polling, and if it STILL will not die, say so
42
+ * LOUDLY and let the caller refuse to release the lock. A checkpoint that cannot
43
+ * prove the predecessor is dead must not hand the worktree to a successor.
44
+ */
45
+ /**
46
+ * How long to wait after SIGTERM before escalating to SIGKILL. Matches
47
+ * `pi-harness.ts`'s `DEADLINE_SIGKILL_GRACE_MS` intent: enough for an agent to
48
+ * flush its session log and exit cleanly, short enough not to stall a CI leg.
49
+ */
50
+ export declare const REAP_SIGTERM_GRACE_MS = 10000;
51
+ /** How long to keep waiting after SIGKILL before declaring the reap FAILED. */
52
+ export declare const REAP_SIGKILL_TIMEOUT_MS = 5000;
53
+ /** The outcome of a {@link reapProcessGroup} attempt. */
54
+ export interface ReapResult {
55
+ /**
56
+ * True iff the group is VERIFIED gone (observed non-existent, not merely
57
+ * signalled). Only a `true` here licenses releasing the item lock and
58
+ * dispatching a successor into the same worktree.
59
+ */
60
+ reaped: boolean;
61
+ /** True iff SIGKILL was needed (the tree ignored SIGTERM) — worth reporting. */
62
+ escalatedToSigkill: boolean;
63
+ /** Total wall-clock ms spent waiting for the tree to die. */
64
+ waitedMs: number;
65
+ /** A human-readable account, always populated (the LOUD failure text). */
66
+ detail: string;
67
+ }
68
+ /**
69
+ * Is any process still alive in process group `pgid`?
70
+ *
71
+ * `process.kill(-pgid, 0)` is the liveness probe: signal 0 performs the
72
+ * permission/existence check WITHOUT delivering a signal (the same technique
73
+ * `harness.ts`'s {@link pidAlive} uses for a single pid, widened to the group).
74
+ *
75
+ * - It THROWS `ESRCH` when no process in the group exists ⇒ the group is gone.
76
+ * - It THROWS `EPERM` when the group exists but we may not signal it. That is
77
+ * still "alive", and reporting it as dead would be the very assumption this
78
+ * module exists to remove — so `EPERM` reads as ALIVE.
79
+ * - It succeeds ⇒ alive.
80
+ */
81
+ export declare function processGroupAlive(pgid: number): boolean;
82
+ /**
83
+ * SIGTERM process group `pgid`, wait for it to ACTUALLY exit, escalate to
84
+ * SIGKILL after {@link REAP_SIGTERM_GRACE_MS}, and report whether the tree is
85
+ * VERIFIED gone.
86
+ *
87
+ * Bounded by construction: at worst `sigtermGraceMs + sigkillTimeoutMs` before it
88
+ * returns `reaped: false` with a loud `detail`. It never waits indefinitely, so
89
+ * it cannot reintroduce the runner-hang the async launch's resolve-on-`exit`
90
+ * discipline exists to avoid.
91
+ */
92
+ export declare function reapProcessGroup(params: {
93
+ /** The process GROUP id to reap (the group leader's pid). */
94
+ pgid: number;
95
+ sigtermGraceMs?: number;
96
+ sigkillTimeoutMs?: number;
97
+ /** Injectable sleep so tests need not burn real seconds. */
98
+ wait?: (ms: number) => Promise<void>;
99
+ /**
100
+ * Injectable liveness probe (default {@link processGroupAlive}). Exists so the
101
+ * REFUSAL path — a tree that survives SIGTERM *and* SIGKILL — can be tested
102
+ * deterministically. There is no portable way to create a genuinely unkillable
103
+ * process, and the alternative (a group we truly cannot signal) would risk the
104
+ * test process itself, so the probe is the seam.
105
+ */
106
+ alive?: (pgid: number) => boolean;
107
+ }): Promise<ReapResult>;
108
+ //# sourceMappingURL=reap-agent-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reap-agent-tree.d.ts","sourceRoot":"","sources":["../src/reap-agent-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAKH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAE7C,yDAAyD;AACzD,MAAM,WAAW,UAAU;IAC1B;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,gFAAgF;IAChF,kBAAkB,EAAE,OAAO,CAAC;IAC5B,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;CACf;AAUD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAgBvD;AAWD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC9C,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAClC,GAAG,OAAO,CAAC,UAAU,CAAC,CAyEtB"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * **Reap a stopped agent's whole PROCESS TREE, and VERIFY it is gone** (spec
3
+ * `graceful-pre-timeout-wip-checkpoint`, observation
4
+ * `checkpoint-releases-lock-while-predecessor-agent-still-writes`).
5
+ *
6
+ * ## The gap this closes
7
+ *
8
+ * The deadline checkpoint used to `child.kill('SIGTERM')` the agent process and
9
+ * treat the signal as if it were the outcome: the moment pi's own `exit` fired,
10
+ * the runner saved the WIP, RELEASED the item lock, and let the next tick
11
+ * dispatch a CONTINUATION agent into the SAME worktree. But `child.kill` signals
12
+ * exactly ONE pid, and a modern agent is a TREE (subagent processes, MCP servers,
13
+ * model proxies, tool subshells). Those descendants survive the parent's SIGTERM,
14
+ * and once pi exits they are re-parented to init — so they can no longer even be
15
+ * FOUND by walking `ppid`, while they keep writing into the worktree.
16
+ *
17
+ * Observed on a real run: the checkpoint saved WIP at 02:13, a continuation agent
18
+ * onboarded into the same worktree at ~02:15, and the predecessor's session log
19
+ * kept being written until 02:19:29 — four minutes INTO the successor's run,
20
+ * whose opening `git status` had already read the tree as clean. Nothing was lost
21
+ * only because the two happened to touch different files. The shape is the
22
+ * defect: one lock, one working tree, two live writers. A write landing after the
23
+ * successor's `git status` is invisible to it; a write landing during its edits
24
+ * can be clobbered either way; and the successor can commit the predecessor's
25
+ * half-finished edits as its own, under a message describing something else.
26
+ *
27
+ * ## Why the PROCESS GROUP is the handle
28
+ *
29
+ * A pid-tree walk cannot work here: the descendants we must reap are precisely
30
+ * the ones that OUTLIVE the parent, and an orphan's `ppid` is gone. A process
31
+ * GROUP id, by contrast, is inherited by every descendant and is NOT changed by
32
+ * re-parenting. So if the agent is spawned as a group LEADER (`detached: true`,
33
+ * making its pgid equal its pid), `kill(-pgid, …)` reaches the entire tree,
34
+ * orphans included — which is why {@link reapProcessGroup} takes a pgid and why
35
+ * `pi-harness.ts` spawns the deadline-capable async launch detached.
36
+ *
37
+ * ## Signal, then VERIFY — never assume
38
+ *
39
+ * The point of the whole module is that sending a signal is not evidence that
40
+ * anything died. So: SIGTERM the group, POLL until it is actually gone, escalate
41
+ * to SIGKILL after a grace, keep polling, and if it STILL will not die, say so
42
+ * LOUDLY and let the caller refuse to release the lock. A checkpoint that cannot
43
+ * prove the predecessor is dead must not hand the worktree to a successor.
44
+ */
45
+ /** Poll interval while waiting for a signalled group to actually exit. */
46
+ const REAP_POLL_MS = 50;
47
+ /**
48
+ * How long to wait after SIGTERM before escalating to SIGKILL. Matches
49
+ * `pi-harness.ts`'s `DEADLINE_SIGKILL_GRACE_MS` intent: enough for an agent to
50
+ * flush its session log and exit cleanly, short enough not to stall a CI leg.
51
+ */
52
+ export const REAP_SIGTERM_GRACE_MS = 10_000;
53
+ /** How long to keep waiting after SIGKILL before declaring the reap FAILED. */
54
+ export const REAP_SIGKILL_TIMEOUT_MS = 5_000;
55
+ /** Sleep helper (injectable clock is not needed: callers inject `wait` in tests). */
56
+ function sleep(ms) {
57
+ return new Promise((resolve) => {
58
+ const timer = setTimeout(resolve, ms);
59
+ timer.unref?.();
60
+ });
61
+ }
62
+ /**
63
+ * Is any process still alive in process group `pgid`?
64
+ *
65
+ * `process.kill(-pgid, 0)` is the liveness probe: signal 0 performs the
66
+ * permission/existence check WITHOUT delivering a signal (the same technique
67
+ * `harness.ts`'s {@link pidAlive} uses for a single pid, widened to the group).
68
+ *
69
+ * - It THROWS `ESRCH` when no process in the group exists ⇒ the group is gone.
70
+ * - It THROWS `EPERM` when the group exists but we may not signal it. That is
71
+ * still "alive", and reporting it as dead would be the very assumption this
72
+ * module exists to remove — so `EPERM` reads as ALIVE.
73
+ * - It succeeds ⇒ alive.
74
+ */
75
+ export function processGroupAlive(pgid) {
76
+ if (!Number.isInteger(pgid) || pgid <= 1) {
77
+ // pgid 0/1 (or a bogus value) would mean "our own group" / init — signalling
78
+ // those would be catastrophic, so never claim they are ours to reap.
79
+ return false;
80
+ }
81
+ try {
82
+ process.kill(-pgid, 0);
83
+ return true;
84
+ }
85
+ catch (err) {
86
+ const code = err.code;
87
+ if (code === 'EPERM') {
88
+ return true; // exists, just not signallable by us.
89
+ }
90
+ return false; // ESRCH (or anything else): treat as gone.
91
+ }
92
+ }
93
+ /** Signal a whole process group, tolerating an already-dead group. */
94
+ function signalGroup(pgid, signal) {
95
+ try {
96
+ process.kill(-pgid, signal);
97
+ }
98
+ catch {
99
+ // ESRCH: already gone — the wait loop below observes that and succeeds.
100
+ }
101
+ }
102
+ /**
103
+ * SIGTERM process group `pgid`, wait for it to ACTUALLY exit, escalate to
104
+ * SIGKILL after {@link REAP_SIGTERM_GRACE_MS}, and report whether the tree is
105
+ * VERIFIED gone.
106
+ *
107
+ * Bounded by construction: at worst `sigtermGraceMs + sigkillTimeoutMs` before it
108
+ * returns `reaped: false` with a loud `detail`. It never waits indefinitely, so
109
+ * it cannot reintroduce the runner-hang the async launch's resolve-on-`exit`
110
+ * discipline exists to avoid.
111
+ */
112
+ export async function reapProcessGroup(params) {
113
+ const { pgid, sigtermGraceMs = REAP_SIGTERM_GRACE_MS, sigkillTimeoutMs = REAP_SIGKILL_TIMEOUT_MS, wait = sleep, alive = processGroupAlive, } = params;
114
+ const started = Date.now();
115
+ if (!alive(pgid)) {
116
+ return {
117
+ reaped: true,
118
+ escalatedToSigkill: false,
119
+ waitedMs: 0,
120
+ detail: `agent process group ${pgid} was already gone (nothing to reap).`,
121
+ };
122
+ }
123
+ // 1. SOFT: ask the whole tree to stop, then WAIT for it to be observably gone.
124
+ signalGroup(pgid, 'SIGTERM');
125
+ while (Date.now() - started < sigtermGraceMs) {
126
+ if (!alive(pgid)) {
127
+ const waitedMs = Date.now() - started;
128
+ return {
129
+ reaped: true,
130
+ escalatedToSigkill: false,
131
+ waitedMs,
132
+ detail: `agent process group ${pgid} exited on SIGTERM after ${waitedMs}ms ` +
133
+ '(verified gone).',
134
+ };
135
+ }
136
+ await wait(REAP_POLL_MS);
137
+ }
138
+ // 2. HARD: the tree ignored SIGTERM through the grace. SIGKILL it and keep
139
+ // verifying — a wedged agent still holding the worktree must not survive
140
+ // into the successor's run.
141
+ signalGroup(pgid, 'SIGKILL');
142
+ const killDeadline = Date.now() + sigkillTimeoutMs;
143
+ while (Date.now() < killDeadline) {
144
+ if (!alive(pgid)) {
145
+ const waitedMs = Date.now() - started;
146
+ return {
147
+ reaped: true,
148
+ escalatedToSigkill: true,
149
+ waitedMs,
150
+ detail: `agent process group ${pgid} ignored SIGTERM and was SIGKILLed; ` +
151
+ `exited after ${waitedMs}ms (verified gone).`,
152
+ };
153
+ }
154
+ await wait(REAP_POLL_MS);
155
+ }
156
+ // 3. LOUD FAILURE: we cannot prove the predecessor is dead. Say exactly that,
157
+ // and exactly what the caller must not do as a result.
158
+ const waitedMs = Date.now() - started;
159
+ return {
160
+ reaped: false,
161
+ escalatedToSigkill: true,
162
+ waitedMs,
163
+ detail: `REFUSING TO PROCEED: agent process group ${pgid} is STILL ALIVE ${waitedMs}ms ` +
164
+ 'after SIGTERM and SIGKILL (an unkillable/uninterruptible descendant — e.g. a ' +
165
+ 'process wedged in a kernel call, or one we lack permission to signal). It may ' +
166
+ 'still be WRITING to the worktree, so the item lock must NOT be released and no ' +
167
+ 'successor agent may onboard here: two live writers in one working tree can ' +
168
+ 'silently clobber each other and let a successor commit the predecessor’s ' +
169
+ `half-finished edits. Inspect and kill it by hand (\`ps -g ${pgid}\`, ` +
170
+ `\`kill -9 -${pgid}\`) before re-running this item.`,
171
+ };
172
+ }
173
+ //# sourceMappingURL=reap-agent-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reap-agent-tree.js","sourceRoot":"","sources":["../src/reap-agent-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,0EAA0E;AAC1E,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE5C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAkB7C,qFAAqF;AACrF,SAAS,KAAK,CAAC,EAAU;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACtC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC7C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QAC1C,6EAA6E;QAC7E,qEAAqE;QACrE,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC,CAAC,sCAAsC;QACpD,CAAC;QACD,OAAO,KAAK,CAAC,CAAC,2CAA2C;IAC1D,CAAC;AACF,CAAC;AAED,sEAAsE;AACtE,SAAS,WAAW,CAAC,IAAY,EAAE,MAAsB;IACxD,IAAI,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,wEAAwE;IACzE,CAAC;AACF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAetC;IACA,MAAM,EACL,IAAI,EACJ,cAAc,GAAG,qBAAqB,EACtC,gBAAgB,GAAG,uBAAuB,EAC1C,IAAI,GAAG,KAAK,EACZ,KAAK,GAAG,iBAAiB,GACzB,GAAG,MAAM,CAAC;IACX,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAClB,OAAO;YACN,MAAM,EAAE,IAAI;YACZ,kBAAkB,EAAE,KAAK;YACzB,QAAQ,EAAE,CAAC;YACX,MAAM,EAAE,uBAAuB,IAAI,sCAAsC;SACzE,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,cAAc,EAAE,CAAC;QAC9C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;YACtC,OAAO;gBACN,MAAM,EAAE,IAAI;gBACZ,kBAAkB,EAAE,KAAK;gBACzB,QAAQ;gBACR,MAAM,EACL,uBAAuB,IAAI,4BAA4B,QAAQ,KAAK;oBACpE,kBAAkB;aACnB,CAAC;QACH,CAAC;QACD,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC;IAED,2EAA2E;IAC3E,4EAA4E;IAC5E,+BAA+B;IAC/B,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC7B,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC;IACnD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;YACtC,OAAO;gBACN,MAAM,EAAE,IAAI;gBACZ,kBAAkB,EAAE,IAAI;gBACxB,QAAQ;gBACR,MAAM,EACL,uBAAuB,IAAI,sCAAsC;oBACjE,gBAAgB,QAAQ,qBAAqB;aAC9C,CAAC;QACH,CAAC;QACD,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC;IAED,8EAA8E;IAC9E,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IACtC,OAAO;QACN,MAAM,EAAE,KAAK;QACb,kBAAkB,EAAE,IAAI;QACxB,QAAQ;QACR,MAAM,EACL,4CAA4C,IAAI,mBAAmB,QAAQ,KAAK;YAChF,+EAA+E;YAC/E,gFAAgF;YAChF,iFAAiF;YACjF,6EAA6E;YAC7E,2EAA2E;YAC3E,6DAA6D,IAAI,MAAM;YACvE,cAAc,IAAI,kCAAkC;KACrD,CAAC;AACH,CAAC"}
@@ -46,7 +46,7 @@ export declare const REPO_CONFIG_FILENAME: string;
46
46
  * per-repo `dorfl.json`. Deliberately a subset of {@link Config};
47
47
  * extend this list as more keys become legitimately repo-scoped.
48
48
  */
49
- export declare const REPO_ALLOWED_KEYS: readonly ["integration", "taskingIntegration", "intakeIntegration", "tasksLandIn", "specsLandIn", "untrustedTasksLandIn", "untrustedSpecsLandIn", "noPR", "verify", "prepare", "dorflCmd", "defaultArbiter", "autoBuild", "autoTask", "observationTriage", "mergeQuestions", "surfaceBlockers", "surfaceStaging", "selectionOrder", "model", "harness", "review", "reviewModel", "reviewMaxRounds", "taskerLoop", "taskerLoopMax", "taskerLoopModel", "freshWorktreeGate", "mergeRetries", "strictMergeApproval", "agentDeadlineMinutes", "checkpointHeadroomMinutes", "maxAutoCheckpoints", "promptGuidance"];
49
+ export declare const REPO_ALLOWED_KEYS: readonly ["integration", "taskingIntegration", "intakeIntegration", "tasksLandIn", "specsLandIn", "untrustedTasksLandIn", "untrustedSpecsLandIn", "noPR", "verify", "prepare", "dorflCmd", "defaultArbiter", "autoBuild", "autoTask", "observationTriage", "mergeQuestions", "surfaceBlockers", "surfaceStaging", "selectionOrder", "model", "buildModel", "harness", "review", "reviewModel", "reviewMaxRounds", "taskerLoop", "taskerLoopMax", "taskerLoopModel", "triageModel", "intakeModel", "freshWorktreeGate", "mergeRetries", "strictMergeApproval", "agentDeadlineMinutes", "checkpointHeadroomMinutes", "maxAutoCheckpoints", "promptGuidance"];
50
50
  /** A key honoured in a per-repo file. */
51
51
  export type RepoAllowedKey = (typeof REPO_ALLOWED_KEYS)[number];
52
52
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"repo-config.d.ts","sourceRoot":"","sources":["../src/repo-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAKN,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAe,KAAK,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAG1D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sBAAsB,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAA2B,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,glBAuNgB,CAAC;AAE/C,yCAAyC;AACzC,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,6GAyBrB,CAAC;AAEX,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIlE;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,QAAiC,CAAC;AAE1E;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAChC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAcjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,gBAAgB,CA0ClB;AAMD,2CAA2C;AAC3C,MAAM,WAAW,wBAAwB;IACxC,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAyBD,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IAClC,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,wBAAwB,GAC/B,kBAAkB,CAiBpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAC1C,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE;IACR,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,GACC,kBAAkB,CA2CpB"}
1
+ {"version":3,"file":"repo-config.d.ts","sourceRoot":"","sources":["../src/repo-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAMN,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAe,KAAK,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAG1D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sBAAsB,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,QAA2B,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,4nBAmOgB,CAAC;AAE/C,yCAAyC;AACzC,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,6GAyBrB,CAAC;AAEX,2CAA2C;AAC3C,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIlE;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,QAAiC,CAAC;AAE1E;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAChC,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAcjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACxC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GACjB,gBAAgB,CA0ClB;AAMD,2CAA2C;AAC3C,MAAM,WAAW,wBAAwB;IACxC,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAyBD,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IAClC,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,wBAAwB,GAC/B,kBAAkB,CAiBpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAC1C,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE;IACR,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,GACC,kBAAkB,CAgDpB"}