dorfl 0.11.1 → 0.11.3

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 (67) 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/do.d.ts +11 -1
  6. package/dist/do.d.ts.map +1 -1
  7. package/dist/do.js +108 -14
  8. package/dist/do.js.map +1 -1
  9. package/dist/harness.d.ts +44 -0
  10. package/dist/harness.d.ts.map +1 -1
  11. package/dist/harness.js.map +1 -1
  12. package/dist/item-lock.d.ts.map +1 -1
  13. package/dist/item-lock.js +89 -15
  14. package/dist/item-lock.js.map +1 -1
  15. package/dist/ledger-write.d.ts.map +1 -1
  16. package/dist/ledger-write.js +38 -3
  17. package/dist/ledger-write.js.map +1 -1
  18. package/dist/needs-attention.d.ts +43 -0
  19. package/dist/needs-attention.d.ts.map +1 -1
  20. package/dist/needs-attention.js +211 -50
  21. package/dist/needs-attention.js.map +1 -1
  22. package/dist/pi-harness.d.ts.map +1 -1
  23. package/dist/pi-harness.js +133 -26
  24. package/dist/pi-harness.js.map +1 -1
  25. package/dist/protocol/REVIEW-PROTOCOL.md +1 -1
  26. package/dist/reap-agent-tree.d.ts +108 -0
  27. package/dist/reap-agent-tree.d.ts.map +1 -0
  28. package/dist/reap-agent-tree.js +173 -0
  29. package/dist/reap-agent-tree.js.map +1 -0
  30. package/dist/repo-mirror.d.ts.map +1 -1
  31. package/dist/repo-mirror.js +17 -0
  32. package/dist/repo-mirror.js.map +1 -1
  33. package/dist/review-verdict.d.ts +46 -2
  34. package/dist/review-verdict.d.ts.map +1 -1
  35. package/dist/review-verdict.js +49 -3
  36. package/dist/review-verdict.js.map +1 -1
  37. package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +1 -1
  38. package/dist/tasker-review-loop.d.ts +13 -0
  39. package/dist/tasker-review-loop.d.ts.map +1 -1
  40. package/dist/tasker-review-loop.js +120 -8
  41. package/dist/tasker-review-loop.js.map +1 -1
  42. package/dist/tasking.d.ts.map +1 -1
  43. package/dist/tasking.js +161 -17
  44. package/dist/tasking.js.map +1 -1
  45. package/dist/watch-session.d.ts +35 -4
  46. package/dist/watch-session.d.ts.map +1 -1
  47. package/dist/watch-session.js +54 -7
  48. package/dist/watch-session.js.map +1 -1
  49. package/dist/worktree-writer-lock.d.ts +99 -0
  50. package/dist/worktree-writer-lock.d.ts.map +1 -0
  51. package/dist/worktree-writer-lock.js +158 -0
  52. package/dist/worktree-writer-lock.js.map +1 -0
  53. package/package.json +1 -1
  54. package/src/arbiter-refs.ts +222 -0
  55. package/src/do.ts +155 -18
  56. package/src/harness.ts +45 -0
  57. package/src/item-lock.ts +93 -19
  58. package/src/ledger-write.ts +41 -5
  59. package/src/needs-attention.ts +282 -59
  60. package/src/pi-harness.ts +136 -28
  61. package/src/reap-agent-tree.ts +221 -0
  62. package/src/repo-mirror.ts +22 -0
  63. package/src/review-verdict.ts +73 -5
  64. package/src/tasker-review-loop.ts +129 -7
  65. package/src/tasking.ts +198 -16
  66. package/src/watch-session.ts +83 -7
  67. package/src/worktree-writer-lock.ts +217 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * **The ONE arbiter-ref refresh + authoritative-read seam** (observation
3
+ * `checkpoint-path-reports-its-own-write-as-absent`).
4
+ *
5
+ * Every "did my own write land?" / "is the branch on the arbiter?" question in
6
+ * the checkpoint + surface paths used to be answered the same wrong way: run a
7
+ * PLAIN `git fetch <arbiter>`, then `git rev-parse <arbiter>/<branch>` — i.e.
8
+ * read a REMOTE-TRACKING ref (`refs/remotes/<arbiter>/…`) and trust it. That is
9
+ * unsound in the configuration dorfl itself creates for `--isolated` runs, and
10
+ * it produced two field defects where dorfl reported its OWN successful write as
11
+ * absent:
12
+ *
13
+ * 1. A job worktree is `git worktree add`ed from the BARE HUB MIRROR
14
+ * (`workspace.ts` `createJob` → `repo-mirror.ts` `ensureMirror`), whose
15
+ * `origin` carries the MIRROR-style refspec `+refs/heads/*:refs/heads/*`.
16
+ * So a plain fetch there writes `refs/heads/main`, and **never populates**
17
+ * `refs/remotes/origin/main` at all. `rev-parse origin/main` then returns
18
+ * whatever a PRIOR explicit-refspec fetch happened to leave behind — a value
19
+ * that PREDATES the write being verified. A push that genuinely landed reads
20
+ * back as "not our commit ⇒ rejected".
21
+ * 2. Worse, in that same worktree a plain `git fetch origin` **fails outright**
22
+ * (`fatal: refusing to fetch into branch 'refs/heads/work/<slug>' checked out
23
+ * at …`), because the mirror refspec's destination IS the branch the worktree
24
+ * has checked out. So it refreshes NOTHING, and a follow-up
25
+ * `rev-parse <arbiter>/work/<slug>` fails against a ref that never existed —
26
+ * reported as "no work branch on <arbiter>" while the branch (and an hour of
27
+ * agent work) sits on the arbiter.
28
+ *
29
+ * Both call sites now route through this module, which fixes the class rather
30
+ * than the two instances:
31
+ *
32
+ * - {@link refreshArbiterRefs} prune-fetches with an EXPLICIT, per-branch
33
+ * refspec into the `refs/remotes/<arbiter>/…` namespace the readers actually
34
+ * read, tolerating the checked-out-branch refusal instead of being silently
35
+ * defeated by it.
36
+ * - {@link resolveArbiterBranch} answers the sha question from the ARBITER
37
+ * ITSELF (`git ls-remote`), so no local ref-namespace/refspec accident can
38
+ * make a landed write look absent. The local tracking ref is only a FALLBACK,
39
+ * used when the arbiter cannot be reached at all.
40
+ *
41
+ * The `ls-remote`-is-authoritative stance is not new — it is the same one
42
+ * `continue-branch.ts` (`branchAheadOfArbiter`), `workspace.ts`, `integrator.ts`
43
+ * and `reap-branches.ts` already take for continue-detection and branch reaping.
44
+ * This module makes it the SHARED default for the post-write verification too,
45
+ * instead of each site re-deciding.
46
+ */
47
+ /** How a {@link ResolvedArbiterBranch} sha was obtained — the read's PROVENANCE. */
48
+ export type ArbiterRefAuthority =
49
+ /** Read from the arbiter itself (`git ls-remote`): AUTHORITATIVE. */
50
+ 'arbiter'
51
+ /**
52
+ * The arbiter could not be reached (offline / broken remote), so the local
53
+ * remote-tracking ref was used. Best-effort: it may be stale, so a caller
54
+ * deciding "did MY write land?" must NOT treat a mismatch here as proof of
55
+ * a loss (see {@link ResolvedArbiterBranch.trustworthy}).
56
+ */
57
+ | 'local-fallback'
58
+ /** Neither the arbiter nor any local ref has this branch. */
59
+ | 'absent';
60
+ /** The resolved state of ONE branch on the arbiter (a single, coherent read). */
61
+ export interface ResolvedArbiterBranch {
62
+ /** The unqualified branch name that was resolved (e.g. `main`, `work/task-x`). */
63
+ branch: string;
64
+ /** Its sha, or `undefined` when the branch exists nowhere we could look. */
65
+ sha?: string;
66
+ /** Where {@link sha} came from. */
67
+ authority: ArbiterRefAuthority;
68
+ /**
69
+ * True iff the arbiter answered (`authority` is `arbiter` or `absent` off a
70
+ * REACHABLE arbiter). When false the read is a stale-capable local fallback,
71
+ * so a mismatch proves nothing and callers must not report a loss from it.
72
+ */
73
+ trustworthy: boolean;
74
+ /** The `ls-remote` stderr when the arbiter could not be reached (diagnostics). */
75
+ unreachableDetail?: string;
76
+ }
77
+ /**
78
+ * PRUNE-FETCH the named arbiter branches into `refs/remotes/<arbiter>/<branch>`
79
+ * — the namespace every reader in this codebase actually reads — using an
80
+ * EXPLICIT per-branch refspec.
81
+ *
82
+ * Three properties matter, and all three are the reason this is not just
83
+ * `git fetch <arbiter>`:
84
+ *
85
+ * - **Explicit refspec.** A bare-hub-mirror worktree's `origin` maps
86
+ * `+refs/heads/*:refs/heads/*`, so a plain fetch never writes
87
+ * `refs/remotes/<arbiter>/*`. Naming the destination makes the refresh work
88
+ * identically in a normal clone AND in a mirror worktree.
89
+ * - **`--prune`.** A branch DELETED on the arbiter (a `requeue --reset`, a
90
+ * merge-reap, a cross-machine `gc`) must disappear from our view too;
91
+ * otherwise a stale tracking ref answers a liveness question with a ghost.
92
+ * - **Per-branch and SOFT.** Fetching branch-at-a-time means the one refspec
93
+ * git refuses (the destination that is checked out in THIS worktree — see the
94
+ * module doc) cannot abort the refresh of the others, which is exactly how the
95
+ * single combined fetch silently refreshed nothing. Every failure is
96
+ * tolerated and reported rather than thrown: this is a REFRESH, and the
97
+ * authoritative answer comes from {@link resolveArbiterBranch} anyway.
98
+ *
99
+ * Returns the branches that could not be refreshed (for diagnostics only — a
100
+ * caller should not gate on it, because the authoritative read does not depend
101
+ * on the refresh succeeding).
102
+ */
103
+ export declare function refreshArbiterRefs(params: {
104
+ cwd: string;
105
+ arbiter: string;
106
+ /** Unqualified branch names to refresh (e.g. `['main', 'work/task-x']`). */
107
+ branches: readonly string[];
108
+ env?: NodeJS.ProcessEnv;
109
+ }): Promise<{
110
+ failed: string[];
111
+ }>;
112
+ /**
113
+ * Resolve ONE branch's sha on the arbiter, ARBITER-AUTHORITATIVELY.
114
+ *
115
+ * `git ls-remote --heads <arbiter> <branch>` asks the arbiter directly, so the
116
+ * answer cannot be defeated by a local refspec/namespace accident — which is the
117
+ * whole point: this is the read a post-write verification uses to decide whether
118
+ * its OWN push landed, and that decision must never be made from a view that
119
+ * predates the push.
120
+ *
121
+ * Decision order:
122
+ * - `ls-remote` exits 0 with a sha ⇒ `{sha, authority: 'arbiter'}` (trustworthy).
123
+ * - `ls-remote` exits 0 with EMPTY output ⇒ the arbiter genuinely does not have
124
+ * the branch ⇒ `{authority: 'absent'}` (trustworthy: a definite "no").
125
+ * - `ls-remote` exits non-zero (unreachable / no such remote) ⇒ fall back to the
126
+ * local `refs/remotes/<arbiter>/<branch>`, flagged `trustworthy: false` so a
127
+ * caller cannot mistake a stale local read for proof of anything.
128
+ *
129
+ * Always call {@link refreshArbiterRefs} first when the caller ALSO needs the
130
+ * objects locally (a CAS base, a `merge-base` / `rev-list` comparison): a sha
131
+ * from `ls-remote` names a commit this repo may not have yet.
132
+ */
133
+ export declare function resolveArbiterBranch(params: {
134
+ cwd: string;
135
+ arbiter: string;
136
+ branch: string;
137
+ env?: NodeJS.ProcessEnv;
138
+ }): Promise<ResolvedArbiterBranch>;
139
+ //# sourceMappingURL=arbiter-refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arbiter-refs.d.ts","sourceRoot":"","sources":["../src/arbiter-refs.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,oFAAoF;AACpF,MAAM,MAAM,mBAAmB;AAC9B,qEAAqE;AACnE,SAAS;AACX;;;;;GAKG;GACD,gBAAgB;AAClB,6DAA6D;GAC3D,QAAQ,CAAC;AAEZ,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACrC,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,SAAS,EAAE,mBAAmB,CAAC;IAC/B;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAOD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CAqB9B;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsDjC"}
@@ -0,0 +1,114 @@
1
+ import { runAsync } from './git.js';
2
+ /** The explicit refspec that maps an arbiter branch into the namespace we READ. */
3
+ function trackingRefspec(arbiter, branch) {
4
+ return `+refs/heads/${branch}:refs/remotes/${arbiter}/${branch}`;
5
+ }
6
+ /**
7
+ * PRUNE-FETCH the named arbiter branches into `refs/remotes/<arbiter>/<branch>`
8
+ * — the namespace every reader in this codebase actually reads — using an
9
+ * EXPLICIT per-branch refspec.
10
+ *
11
+ * Three properties matter, and all three are the reason this is not just
12
+ * `git fetch <arbiter>`:
13
+ *
14
+ * - **Explicit refspec.** A bare-hub-mirror worktree's `origin` maps
15
+ * `+refs/heads/*:refs/heads/*`, so a plain fetch never writes
16
+ * `refs/remotes/<arbiter>/*`. Naming the destination makes the refresh work
17
+ * identically in a normal clone AND in a mirror worktree.
18
+ * - **`--prune`.** A branch DELETED on the arbiter (a `requeue --reset`, a
19
+ * merge-reap, a cross-machine `gc`) must disappear from our view too;
20
+ * otherwise a stale tracking ref answers a liveness question with a ghost.
21
+ * - **Per-branch and SOFT.** Fetching branch-at-a-time means the one refspec
22
+ * git refuses (the destination that is checked out in THIS worktree — see the
23
+ * module doc) cannot abort the refresh of the others, which is exactly how the
24
+ * single combined fetch silently refreshed nothing. Every failure is
25
+ * tolerated and reported rather than thrown: this is a REFRESH, and the
26
+ * authoritative answer comes from {@link resolveArbiterBranch} anyway.
27
+ *
28
+ * Returns the branches that could not be refreshed (for diagnostics only — a
29
+ * caller should not gate on it, because the authoritative read does not depend
30
+ * on the refresh succeeding).
31
+ */
32
+ export async function refreshArbiterRefs(params) {
33
+ const { cwd, arbiter, branches, env } = params;
34
+ const failed = [];
35
+ for (const branch of branches) {
36
+ const fetched = await runAsync('git', [
37
+ 'fetch',
38
+ '--quiet',
39
+ '--prune',
40
+ arbiter,
41
+ trackingRefspec(arbiter, branch),
42
+ ], cwd, { env });
43
+ if (fetched.status !== 0) {
44
+ failed.push(branch);
45
+ }
46
+ }
47
+ return { failed };
48
+ }
49
+ /**
50
+ * Resolve ONE branch's sha on the arbiter, ARBITER-AUTHORITATIVELY.
51
+ *
52
+ * `git ls-remote --heads <arbiter> <branch>` asks the arbiter directly, so the
53
+ * answer cannot be defeated by a local refspec/namespace accident — which is the
54
+ * whole point: this is the read a post-write verification uses to decide whether
55
+ * its OWN push landed, and that decision must never be made from a view that
56
+ * predates the push.
57
+ *
58
+ * Decision order:
59
+ * - `ls-remote` exits 0 with a sha ⇒ `{sha, authority: 'arbiter'}` (trustworthy).
60
+ * - `ls-remote` exits 0 with EMPTY output ⇒ the arbiter genuinely does not have
61
+ * the branch ⇒ `{authority: 'absent'}` (trustworthy: a definite "no").
62
+ * - `ls-remote` exits non-zero (unreachable / no such remote) ⇒ fall back to the
63
+ * local `refs/remotes/<arbiter>/<branch>`, flagged `trustworthy: false` so a
64
+ * caller cannot mistake a stale local read for proof of anything.
65
+ *
66
+ * Always call {@link refreshArbiterRefs} first when the caller ALSO needs the
67
+ * objects locally (a CAS base, a `merge-base` / `rev-list` comparison): a sha
68
+ * from `ls-remote` names a commit this repo may not have yet.
69
+ */
70
+ export async function resolveArbiterBranch(params) {
71
+ const { cwd, arbiter, branch, env } = params;
72
+ const ls = await runAsync('git', ['ls-remote', '--heads', arbiter, branch], cwd, { env });
73
+ if (ls.status === 0) {
74
+ // `<sha>\t<ref>` lines. `--heads <branch>` can match several refs when the
75
+ // name is a glob-ish prefix, so take the line whose ref is EXACTLY ours.
76
+ const sha = ls.stdout
77
+ .split('\n')
78
+ .map((line) => line.trim())
79
+ .filter((line) => line !== '')
80
+ .map((line) => line.split(/\s+/))
81
+ .find(([, ref]) => ref === `refs/heads/${branch}`)?.[0];
82
+ if (sha !== undefined && sha !== '') {
83
+ return { branch, sha, authority: 'arbiter', trustworthy: true };
84
+ }
85
+ // Reachable arbiter that does NOT have the branch: a definite, trustworthy
86
+ // "absent" (a stale local ref must not be able to resurrect it).
87
+ return { branch, authority: 'absent', trustworthy: true };
88
+ }
89
+ // Unreachable arbiter: best-effort local read, explicitly NOT trustworthy.
90
+ const local = await runAsync('git', [
91
+ 'rev-parse',
92
+ '--verify',
93
+ '--quiet',
94
+ `refs/remotes/${arbiter}/${branch}^{commit}`,
95
+ ], cwd, { env });
96
+ const localSha = local.status === 0 ? local.stdout.trim() : '';
97
+ const unreachableDetail = ls.stderr.trim() || `git ls-remote exit ${ls.status}`;
98
+ if (localSha !== '') {
99
+ return {
100
+ branch,
101
+ sha: localSha,
102
+ authority: 'local-fallback',
103
+ trustworthy: false,
104
+ unreachableDetail,
105
+ };
106
+ }
107
+ return {
108
+ branch,
109
+ authority: 'absent',
110
+ trustworthy: false,
111
+ unreachableDetail,
112
+ };
113
+ }
114
+ //# sourceMappingURL=arbiter-refs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arbiter-refs.js","sourceRoot":"","sources":["../src/arbiter-refs.ts"],"names":[],"mappings":"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"}
package/dist/do.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { TaskReviewGate } from './tasker-review-loop.js';
2
- import { type Harness } from './harness.js';
2
+ import { type AgentTreeReap, type Harness } from './harness.js';
3
3
  import { type LedgerReadStrategy } from './ledger-read.js';
4
4
  import type { ReviewProvider } from './integrator.js';
5
5
  import type { IntegrationMode, PromptGuidance } from './config.js';
@@ -49,6 +49,16 @@ export type DoDorfl = (input: {
49
49
  * injected path — the real deadline race lives in `PiHarness.launchAsync`.
50
50
  */
51
51
  timedOut?: boolean;
52
+ /**
53
+ * **The simulated process-tree REAP verdict** for a {@link timedOut} stop
54
+ * (observation `checkpoint-releases-lock-while-predecessor-agent-still-writes`).
55
+ * Test-only signal, the sibling of `timedOut`, so an injected agent can drive
56
+ * the "predecessor could not be verified dead" routing — where the checkpoint
57
+ * must NOT release the item lock, because the next tick would then onboard a
58
+ * successor into a working tree the predecessor may still be writing to.
59
+ * Absent ⇒ nothing was left running (the pre-existing behaviour).
60
+ */
61
+ reap?: AgentTreeReap;
52
62
  };
53
63
  export interface DoOptions {
54
64
  /** The raw CLI slug argument: bare (= task), `task:<slug>`, or `spec:<slug>`. */
package/dist/do.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"do.d.ts","sourceRoot":"","sources":["../src/do.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"do.d.ts","sourceRoot":"","sources":["../src/do.ts"],"names":[],"mappings":"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"}
package/dist/do.js CHANGED
@@ -7,6 +7,7 @@ import { resolveSlug, SlugResolutionError, workBranchRef, } from './slug-namespa
7
7
  import { performTask } from './tasking.js';
8
8
  import { resolveTask, buildAgentPrompt, resolveContinueContext, resolvePromptGuidanceForItem, PromptError, } from './prompt.js';
9
9
  import { NullHarness } from './harness.js';
10
+ import { acquireWorktreeWriterLock } from './worktree-writer-lock.js';
10
11
  import { PiHarness } from './pi-harness.js';
11
12
  import { launchWithOptionalWatch } from './agent-launch.js';
12
13
  import { ledgerRead } from './ledger-read.js';
@@ -98,6 +99,19 @@ function deadlineAutoContinueReason(params) {
98
99
  `tick continues from the branch tip.`);
99
100
  }
100
101
  function deadlineSurfaceReason(params) {
102
+ if (params.kind === 'unreaped') {
103
+ // The auto-continue path is BLOCKED, not merely skipped: releasing the lock
104
+ // would let a successor onboard into a worktree a live predecessor may still
105
+ // be writing to (observation
106
+ // `checkpoint-releases-lock-while-predecessor-agent-still-writes`). The work
107
+ // is saved and pushed; only the hand-off is withheld.
108
+ return (`deadline checkpoint (agent NOT verifiably stopped): '${params.slug}' hit ` +
109
+ 'the dorfl-internal deadline and its WIP was saved + pushed, but the agent ' +
110
+ 'process tree could not be confirmed dead, so the lock was NOT released and ' +
111
+ 'no successor was dispatched — a second agent in the same working tree can ' +
112
+ 'clobber or silently absorb the first one\u2019s edits. ' +
113
+ `${params.detail ?? ''}`.trim());
114
+ }
101
115
  if (params.kind === 'no-progress') {
102
116
  return (`deadline checkpoint (no progress / ceiling): '${params.slug}' hit ` +
103
117
  `the dorfl-internal deadline WITHOUT any work-branch progress this ` +
@@ -680,6 +694,7 @@ export async function performDo(options) {
680
694
  cwd: tree.dir,
681
695
  arbiter: tree.arbiterRemote,
682
696
  maxAutoCheckpoints: options.maxAutoCheckpoints ?? 5,
697
+ reap: agent.reap,
683
698
  env,
684
699
  note,
685
700
  });
@@ -1236,6 +1251,39 @@ async function runDoAgent(options, cwd, prompt, slug) {
1236
1251
  return options.dorfl({ cwd, prompt, slug, env: options.env });
1237
1252
  }
1238
1253
  const harness = options.harness ?? new NullHarness();
1254
+ // WORKING-TREE SENTINEL (observation
1255
+ // `checkpoint-releases-lock-while-predecessor-agent-still-writes`): the item
1256
+ // lock guards the ITEM; this guards the TREE. A deadline checkpoint releases the
1257
+ // item lock so the next tick can continue the task in the SAME worktree, so the
1258
+ // item lock alone cannot keep a successor out while a predecessor is still
1259
+ // alive. Refuse to launch a second agent into a tree that already has a LIVE
1260
+ // writer, independently of the reap. A dead holder's sentinel is stale and taken
1261
+ // over, so a crashed runner never poisons the worktree.
1262
+ const writer = acquireWorktreeWriterLock({ dir: cwd, slug, env: options.env });
1263
+ if (!writer.acquired) {
1264
+ return { ok: false, detail: writer.reason };
1265
+ }
1266
+ try {
1267
+ return await launchAgentUnderWriterLock({
1268
+ options,
1269
+ cwd,
1270
+ prompt,
1271
+ slug,
1272
+ harness,
1273
+ });
1274
+ }
1275
+ finally {
1276
+ writer.release();
1277
+ }
1278
+ }
1279
+ /**
1280
+ * The actual harness launch, run while this process holds the worktree writer
1281
+ * sentinel (see {@link runDoAgent}). Split out purely so the sentinel's
1282
+ * acquire/release brackets the launch in a `try/finally` without indenting the
1283
+ * whole body.
1284
+ */
1285
+ async function launchAgentUnderWriterLock(params) {
1286
+ const { options, cwd, prompt, slug, harness } = params;
1239
1287
  // Convert the dorfl-internal deadline (minutes) into a wall-clock epoch-ms so
1240
1288
  // the harness (`launchAsync`) can race the child against it (spec
1241
1289
  // `graceful-pre-timeout-wip-checkpoint`). Absent ⇒ no deadline; a run that
@@ -1269,6 +1317,10 @@ async function runDoAgent(options, cwd, prompt, slug) {
1269
1317
  detail: launched.detail,
1270
1318
  output: launched.output,
1271
1319
  timedOut: launched.timedOut,
1320
+ // The harness's PROOF that a deadline-stopped agent's process tree is gone.
1321
+ // Threaded to {@link routeDeadlineCheckpoint}, which refuses to release the
1322
+ // item lock without it.
1323
+ reap: launched.reap,
1272
1324
  };
1273
1325
  }
1274
1326
  /**
@@ -1280,7 +1332,24 @@ async function runDoAgent(options, cwd, prompt, slug) {
1280
1332
  * and remote forms route a deadline stop identically.
1281
1333
  */
1282
1334
  async function routeDeadlineCheckpoint(params) {
1283
- const { slug, branch, cwd, arbiter, maxAutoCheckpoints, env, note } = params;
1335
+ const { slug, branch, cwd, arbiter, maxAutoCheckpoints, reap, env, note } = params;
1336
+ // 0. THE PREDECESSOR MUST BE DEAD BEFORE THE LOCK CAN MOVE.
1337
+ //
1338
+ // The item lock guards the ITEM; nothing guards the WORKING TREE. So if we
1339
+ // released the lock while the checkpointed agent's tree were still alive, the
1340
+ // next tick would onboard a successor into the very worktree the predecessor is
1341
+ // still writing to — one lock, one working tree, two live writers. That was
1342
+ // observed in the field: a predecessor kept writing for four minutes into its
1343
+ // successor's run, and its last write landed on a file the successor had already
1344
+ // read as clean in its opening `git status`.
1345
+ //
1346
+ // A reap we could not VERIFY is therefore a hard stop for the auto-continue
1347
+ // path. We still SAVE the work below (never lose work) and still surface the
1348
+ // item, but we do not hand the tree to anybody else.
1349
+ const predecessorGone = reap === undefined || reap.reaped;
1350
+ if (reap !== undefined && reap.escalatedToSigkill && reap.reaped) {
1351
+ note(`Deadline checkpoint for '${slug}': ${reap.detail}`);
1352
+ }
1284
1353
  // 1. ALWAYS save the WIP first: commit any residue + push the work branch.
1285
1354
  const savedReason = `deadline checkpoint save for '${slug}'`;
1286
1355
  const saved = await saveDeadlineCheckpoint({
@@ -1305,7 +1374,9 @@ async function routeDeadlineCheckpoint(params) {
1305
1374
  branch,
1306
1375
  env,
1307
1376
  });
1308
- if (madeProgressThisSession && checkpointCount <= maxAutoCheckpoints) {
1377
+ if (predecessorGone &&
1378
+ madeProgressThisSession &&
1379
+ checkpointCount <= maxAutoCheckpoints) {
1309
1380
  // AUTO-CONTINUE: release the lock via the SAME default keep+continue path
1310
1381
  // `requeue` uses (no --reset, no --reconcile, no sidecar). The branch is
1311
1382
  // KEPT on the arbiter so the next claim continues from its tip.
@@ -1322,9 +1393,25 @@ async function routeDeadlineCheckpoint(params) {
1322
1393
  note,
1323
1394
  });
1324
1395
  if (returned.moved) {
1396
+ // Derive this line from the SINGLE state `returnToBacklog` resolved, never
1397
+ // from an independent assumption. It used to assert "the next tick continues
1398
+ // from <branch>" unconditionally, which contradicted the requeue's own
1399
+ // "'<slug>' has no work branch on <arbiter> — nothing to continue from" line
1400
+ // emitted two lines earlier. Both cannot be true, and acting on the wrong one
1401
+ // re-drives the task from scratch and discards the saved work (observation
1402
+ // `checkpoint-path-reports-its-own-write-as-absent`). One resolved state, one
1403
+ // story: mutually contradictory lines are worse than emitting nothing.
1404
+ const continueFrom = returned.continueBranch;
1405
+ const continuation = continueFrom === undefined || continueFrom.aheadOfMain
1406
+ ? `lock released so the next tick continues from ${continueFrom?.branch ?? branch}`
1407
+ : continueFrom.trustworthy
1408
+ ? 'lock released; the arbiter has no work to continue from, so the next ' +
1409
+ 'tick starts this item fresh'
1410
+ : `lock released; the arbiter could not be read to confirm ${continueFrom.branch}, ` +
1411
+ 'so do NOT assume the work is gone — check the branch before re-driving';
1325
1412
  const message = `Auto-continued '${slug}' at the dorfl-internal deadline (checkpoint ` +
1326
1413
  `${checkpointCount}/${maxAutoCheckpoints}): WIP saved + branch pushed, ` +
1327
- `lock released so the next tick continues from ${branch}. ${reason}`;
1414
+ `${continuation}. ${reason}`;
1328
1415
  note(message);
1329
1416
  return {
1330
1417
  exitCode: 0,
@@ -1341,14 +1428,16 @@ async function routeDeadlineCheckpoint(params) {
1341
1428
  // SURFACE: mark the lock stuck via the whole applyNeedsAttentionTransition
1342
1429
  // (save + push + stuck). The WIP was already saved above; a second save is
1343
1430
  // idempotent (empty commit is skipped inside routeToNeedsAttention).
1344
- const surfaceReason = madeProgressThisSession
1345
- ? deadlineSurfaceReason({
1346
- slug,
1347
- kind: 'ceiling',
1348
- count: checkpointCount,
1349
- max: maxAutoCheckpoints,
1350
- })
1351
- : deadlineSurfaceReason({ slug, kind: 'no-progress' });
1431
+ const surfaceReason = !predecessorGone
1432
+ ? deadlineSurfaceReason({ slug, kind: 'unreaped', detail: reap?.detail })
1433
+ : madeProgressThisSession
1434
+ ? deadlineSurfaceReason({
1435
+ slug,
1436
+ kind: 'ceiling',
1437
+ count: checkpointCount,
1438
+ max: maxAutoCheckpoints,
1439
+ })
1440
+ : deadlineSurfaceReason({ slug, kind: 'no-progress' });
1352
1441
  const routed = await ledgerWrite.applyNeedsAttentionTransition({
1353
1442
  cwd,
1354
1443
  slug,
@@ -1358,10 +1447,14 @@ async function routeDeadlineCheckpoint(params) {
1358
1447
  note,
1359
1448
  });
1360
1449
  const report = routed.moved ? routeReport(routed, branch) : undefined;
1361
- const message = routed.moved
1362
- ? `Surfaced '${slug}' at the deadline checkpoint (${madeProgressThisSession
1450
+ const why = !predecessorGone
1451
+ ? 'the checkpointed agent could not be verified dead'
1452
+ : madeProgressThisSession
1363
1453
  ? `ceiling ${checkpointCount}/${maxAutoCheckpoints}`
1364
- : 'no progress this session'}); ${report.fragment}. ${surfaceReason}`
1454
+ : 'no progress this session';
1455
+ const message = routed.moved
1456
+ ? `Surfaced '${slug}' at the deadline checkpoint (${why}); ` +
1457
+ `${report.fragment}. ${surfaceReason}`
1365
1458
  : `Could not surface '${slug}' at the deadline checkpoint ` +
1366
1459
  `(${routed.reasonNotMoved ?? 'unknown'}). ${surfaceReason}`;
1367
1460
  note(message);
@@ -1963,6 +2056,7 @@ async function runRemotePipeline(options, tree, slug, note, env) {
1963
2056
  cwd,
1964
2057
  arbiter: arbiterRemote,
1965
2058
  maxAutoCheckpoints: options.maxAutoCheckpoints ?? 5,
2059
+ reap: agent.reap,
1966
2060
  env,
1967
2061
  note,
1968
2062
  });