@ordewell/core 0.10.12 → 0.11.1

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 (50) hide show
  1. package/AUTHORS.md +37 -0
  2. package/NOTICE +71 -0
  3. package/README.md +1 -1
  4. package/dist/{IFileSystem-1PqRCjhu.d.mts → IRunner-B5l9PCTv.d.ts} +926 -102
  5. package/dist/{IFileSystem-lTRL_G9Z.d.ts → IRunner-DBgDx_Mr.d.mts} +926 -102
  6. package/dist/{ModeResolver-CoJAJD-f.d.ts → ModeResolver-BDAgouuE.d.mts} +9 -2
  7. package/dist/{ModeResolver-6TQXVsde.d.mts → ModeResolver-DyXeZ9ib.d.ts} +9 -2
  8. package/dist/{Task-CfBM0wkV.d.mts → Task-BL4Idm9r.d.mts} +656 -26
  9. package/dist/{Task-CfBM0wkV.d.ts → Task-BL4Idm9r.d.ts} +656 -26
  10. package/dist/{chunk-ESKK5I4F.mjs → chunk-4QXOWEGC.mjs} +1888 -334
  11. package/dist/chunk-4QXOWEGC.mjs.map +1 -0
  12. package/dist/{chunk-KWS3VEER.mjs → chunk-MYXMIVWA.mjs} +6 -6
  13. package/dist/chunk-MYXMIVWA.mjs.map +1 -0
  14. package/dist/{chunk-PFH7WFIB.mjs → chunk-QTZ5XTBY.mjs} +228 -15
  15. package/dist/chunk-QTZ5XTBY.mjs.map +1 -0
  16. package/dist/{chunk-24EL4Z2W.mjs → chunk-YAFNYNFX.mjs} +342 -36
  17. package/dist/chunk-YAFNYNFX.mjs.map +1 -0
  18. package/dist/index.d.mts +1488 -769
  19. package/dist/index.d.ts +1488 -769
  20. package/dist/index.js +32003 -20350
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.mjs +29380 -19553
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/parsing.d.mts +2 -2
  25. package/dist/parsing.d.ts +2 -2
  26. package/dist/parsing.js +232 -25
  27. package/dist/parsing.js.map +1 -1
  28. package/dist/parsing.mjs +3 -3
  29. package/dist/plan-utils.d.mts +4 -4
  30. package/dist/plan-utils.d.ts +4 -4
  31. package/dist/plan-utils.js +1156 -96
  32. package/dist/plan-utils.js.map +1 -1
  33. package/dist/plan-utils.mjs +144 -8
  34. package/dist/{runSettingsText-Jb_efnI4.d.mts → runSettingsText-BKwWs052.d.mts} +2882 -621
  35. package/dist/{runSettingsText-C5uv2axI.d.ts → runSettingsText-CAXiTpsj.d.ts} +2882 -621
  36. package/dist/testing.d.mts +10 -4
  37. package/dist/testing.d.ts +10 -4
  38. package/dist/testing.js +30 -7
  39. package/dist/testing.js.map +1 -1
  40. package/dist/testing.mjs +26 -7
  41. package/dist/testing.mjs.map +1 -1
  42. package/package.json +7 -3
  43. package/skills/diagnose/SKILL.md +52 -0
  44. package/skills/explore/SKILL.md +65 -0
  45. package/dist/chunk-24EL4Z2W.mjs.map +0 -1
  46. package/dist/chunk-ESKK5I4F.mjs.map +0 -1
  47. package/dist/chunk-KWS3VEER.mjs.map +0 -1
  48. package/dist/chunk-NJGID6BG.mjs +0 -115
  49. package/dist/chunk-NJGID6BG.mjs.map +0 -1
  50. package/dist/chunk-PFH7WFIB.mjs.map +0 -1
@@ -1,3 +1,254 @@
1
+ /** Advice to the reader of how much a finding matters; it never reaches the verdict. */
2
+ type FindingSeverity = 'blocker' | 'major' | 'minor' | 'info';
3
+ /** A severity as a surface labels it, so both task views word it the same. */
4
+ declare const SEVERITY_LABEL: Record<FindingSeverity, string>;
5
+ declare function severityLabel(severity: FindingSeverity): string;
6
+ /** The worst severity among `findings`, or null for none: what a compact row says at a glance. */
7
+ declare function worstSeverity(findings: readonly Pick<HandoffFinding, 'severity'>[]): FindingSeverity | null;
8
+ /**
9
+ * A typed reference a reader can check rather than trust. A `task` names the
10
+ * task by id once the call is accepted, and `finding` one of its findings.
11
+ * `resolved` is read-time metadata: the author cannot vouch for resolution,
12
+ * so the completion schema strips it and the saved handoff carries no check.
13
+ */
14
+ type HandoffEvidence = {
15
+ kind: 'file';
16
+ path: string;
17
+ line?: number;
18
+ resolved?: boolean;
19
+ } | {
20
+ kind: 'commit';
21
+ sha: string;
22
+ repo?: string;
23
+ resolved?: boolean;
24
+ } | {
25
+ kind: 'task';
26
+ task: string;
27
+ finding?: string;
28
+ } | {
29
+ kind: 'url';
30
+ url: string;
31
+ };
32
+ /**
33
+ * What a saved patch does not carry as the task's worktree held it
34
+ * (ADR-0050): `excluded`, a symbolic link or a submodule, never carried;
35
+ * `dropped`, a file whose part of the patch was over the size cap;
36
+ * `normalized`, a file git converts on the way in (its text, eol, filter or
37
+ * encoding attributes), whose bytes in the patch differ from the file the
38
+ * task left. `paths` names the first few; `count` is all of them.
39
+ */
40
+ interface PatchFlag {
41
+ kind: 'excluded' | 'dropped' | 'normalized';
42
+ count: number;
43
+ paths: string[];
44
+ }
45
+ /**
46
+ * A read-only task's prototype, saved with `save_patch` (ADR-0050): the patch
47
+ * Ordewell keeps beside the attempt's log, and what is in it. Only Ordewell
48
+ * adds one, never the author: the completion call's artifacts are the other
49
+ * four kinds.
50
+ */
51
+ interface HandoffPatch {
52
+ kind: 'patch';
53
+ /** The patch file, relative to the workspace root. */
54
+ path: string;
55
+ /** The attempt that saved it, beside whose log it is kept. */
56
+ attempt: number;
57
+ /** The commit it was taken against, where it applies as it is. */
58
+ base: string;
59
+ files: number;
60
+ /** Lines added and removed, binary files counting none. */
61
+ added: number;
62
+ removed: number;
63
+ /** Of {@link files}, those whose change is binary. */
64
+ binary: number;
65
+ bytes: number;
66
+ /** The SHA-256 of the patch file's bytes, hex: what `apply_patch` checks the file against before it applies it. */
67
+ sha256: string;
68
+ flags?: PatchFlag[];
69
+ }
70
+ /** What a task produced: the four kinds of evidence, or a patch Ordewell saved for it. */
71
+ type HandoffArtifact = HandoffEvidence | HandoffPatch;
72
+ interface HandoffFinding {
73
+ /** `F1` … `Fn`, assigned by Ordewell in the order the author gave them. */
74
+ id: string;
75
+ severity: FindingSeverity;
76
+ title: string;
77
+ detail?: string;
78
+ evidence?: HandoffEvidence[];
79
+ }
80
+ /**
81
+ * A change task's commit message, as its author wrote it for a human
82
+ * reviewer: what changed and why. The summary is for the next agent.
83
+ */
84
+ interface HandoffCommit {
85
+ subject: string;
86
+ body?: string;
87
+ }
88
+ /** What the plan keeps on a task: enough for a surface or a dependent's prompt without reading a log. */
89
+ interface HandoffDigest {
90
+ commit?: HandoffCommit;
91
+ summary: string;
92
+ /** The first {@link DIGEST_FINDINGS} findings; the rest are in the attempt's log. */
93
+ findings: HandoffFinding[];
94
+ findingCount: number;
95
+ artifacts?: HandoffArtifact[];
96
+ }
97
+ /** The whole handoff, as the attempt's log keeps it. */
98
+ interface StoredHandoff extends HandoffDigest {
99
+ report?: string;
100
+ }
101
+ /** A handoff as its author sends it, before Ordewell numbers the findings. */
102
+ interface Handoff {
103
+ /** How the attempt ended, as the call said: a commit counts only with `done`. */
104
+ status?: 'done' | 'blocked' | 'failed';
105
+ commit?: HandoffCommit;
106
+ summary: string;
107
+ findings?: Omit<HandoffFinding, 'id'>[];
108
+ artifacts?: HandoffArtifact[];
109
+ report?: string;
110
+ }
111
+ /** The most a report keeps: whole findings fit, and a runaway one does not bloat the files that hold it. */
112
+ declare const REPORT_MAX_CHARS = 20000;
113
+ /**
114
+ * What a `task_complete` call may hand over. A call past one is refused, not
115
+ * cut: an author who is told learns, one cut silently does not.
116
+ */
117
+ declare const HANDOFF_LIMITS: {
118
+ readonly summary: 1500;
119
+ readonly commitSubject: 100;
120
+ readonly commitBody: 500;
121
+ readonly findings: 50;
122
+ readonly title: 200;
123
+ readonly detail: 2000;
124
+ readonly reference: 2000;
125
+ readonly evidence: 10;
126
+ readonly artifacts: 20;
127
+ readonly report: 20000;
128
+ };
129
+ /** Findings the plan's digest keeps of a handoff. */
130
+ declare const DIGEST_FINDINGS = 25;
131
+ /** The handoff a call stated, its findings numbered in the author's order; undefined when it stated nothing. */
132
+ declare function storedHandoff(handoff: Handoff): StoredHandoff | undefined;
133
+ /**
134
+ * A handoff that also names the patch its attempt saved (ADR-0050).
135
+ * Ordewell adds it, past the author's own artifacts and their limit, and an
136
+ * attempt that ended without a completion call still hands it on.
137
+ */
138
+ declare function withPatch(handoff: StoredHandoff | undefined, patch: HandoffPatch): StoredHandoff;
139
+ /** The patch a handoff names, if its attempt saved one (ADR-0050). */
140
+ declare function savedPatchOf(handoff: HandoffDigest | undefined): HandoffPatch | undefined;
141
+ /** What the plan keeps of a handoff: no report, and at most {@link DIGEST_FINDINGS} findings. */
142
+ declare function handoffDigest(handoff: StoredHandoff): HandoffDigest;
143
+ /** How a task is named in a handoff's text: the plan's reference for its id, or the id where the plan has none. */
144
+ type HandoffTaskName = (taskId: string) => string | undefined;
145
+ /** A handoff with the tasks it cites named by their references in `plan`, as a reader names them. */
146
+ declare function namedHandoff<H extends HandoffDigest>(plan: readonly Task[], handoff: H): H;
147
+ /** A typed reference as a reader says it: `src/a.ts:42`, `commit abc`, `task #2 F1`, a URL, a saved patch. */
148
+ declare function handoffReferenceText(ref: HandoffArtifact, taskName?: HandoffTaskName): string;
149
+ /** A size as a reader takes it in: bytes, KB or MB. */
150
+ declare function byteSize(bytes: number): string;
151
+ /** One flag of a saved patch as text: what it is, how many, and the paths it names. */
152
+ declare function patchFlagText(flag: PatchFlag): string;
153
+ /** What a saved patch changes, once: files, binary files, lines and size (ADR-0050). */
154
+ declare function patchChangesText(patch: HandoffPatch): string;
155
+ /** The same without the size, for a row that already names the patch and has little room (ADR-0050). */
156
+ declare function patchLinesText(patch: HandoffPatch): string;
157
+ /** A saved patch as one line: where, how big, from which commit, and its flags (ADR-0050). */
158
+ declare function patchText(patch: HandoffPatch): string;
159
+ /**
160
+ * A saved patch as a preview block (ADR-0050): what it changes and the commit
161
+ * it applies to, the file to open, and every path it left out. The compact
162
+ * surfaces say it in one line with `patchText`; this is for a task's handoff,
163
+ * where there is room for the flags.
164
+ */
165
+ declare function patchLines(patch: HandoffPatch): string[];
166
+ /**
167
+ * A saved patch as every surface shows it (ADR-0050): what was saved, the
168
+ * files and lines it changes, where the file is, and what it left out. Each
169
+ * surface paints these words and offers its own way to open the file; none of
170
+ * them works out a count, a size or a flag for itself.
171
+ */
172
+ interface PatchView {
173
+ /** The patch file, relative to the workspace root: what a surface opens. */
174
+ path: string;
175
+ /** "attempt 1". */
176
+ attempt: string;
177
+ /** "3 files, 1 binary, +42 -7, 1.5 KB". */
178
+ changes: string;
179
+ /** "from commit abc123def456". */
180
+ base: string;
181
+ /** "saved as <path> (attempt 1)": the fact it was saved, and the file to open. */
182
+ saved: string;
183
+ /** One line per flag: what it left out, how many and which paths; empty when it left out nothing. */
184
+ flags: string[];
185
+ /** The compact row's part: "patch: 3 files, 1 binary, +42 -7". */
186
+ peek: string;
187
+ }
188
+ /** One saved patch, worded once for every surface. */
189
+ declare function patchView(patch: HandoffPatch): PatchView;
190
+ /** Every patch a handoff names, in the order Ordewell added them. */
191
+ declare function savedPatchViews(handoff: HandoffDigest | undefined): PatchView[];
192
+ /** The patch a handoff names, as a surface shows it; null when it names none. */
193
+ declare function savedPatchView(handoff: HandoffDigest | undefined): PatchView | null;
194
+ /** One finding as text: its id, severity and title, then its detail and evidence under it. */
195
+ declare function findingLines(f: HandoffFinding, taskName?: HandoffTaskName): string[];
196
+ /** A handoff's artifacts as one line, or none when it lists none. */
197
+ declare function artifactLines(handoff: HandoffDigest, taskName?: HandoffTaskName): string[];
198
+ /** A handoff's opening as text: its commit, subject then body, then the summary. */
199
+ declare function openingLines(handoff: HandoffDigest): string[];
200
+ /** Each pushed handoff's text ceiling, excluding artifacts (ADR-0047). */
201
+ declare const HANDOFF_PUSH_CHARS = 4000;
202
+ /** Shared push rendering: reserve the closing pointer before accepting each whole finding. */
203
+ declare function boundedHandoffLines(handoff: HandoffDigest, omission: (more: number) => string, taskName?: HandoffTaskName, footer?: string): string[];
204
+ interface HandoffPreviewOptions {
205
+ /** How many findings to list; the rest are counted. Default: every finding the digest holds. */
206
+ maxFindings?: number;
207
+ /** How to name a task a reference cites; the plan's reference when given. */
208
+ taskName?: HandoffTaskName;
209
+ }
210
+ /**
211
+ * A handoff as a compact surface prints it (ADR-0047): the commit, the summary, each
212
+ * finding's title line (`F1 [blocker] Title`), how many the digest left out,
213
+ * and the artifacts. The detail and evidence stay in the attempt's log.
214
+ */
215
+ declare function handoffPreviewLines(handoff: HandoffDigest, options?: HandoffPreviewOptions): string[];
216
+ /**
217
+ * A handoff as a wake-up carries it (ADR-0029, ADR-0037): the commit and
218
+ * summary, its findings whole within the shared ceiling, how many are left
219
+ * out, and the artifacts. The report is never pushed; it waits in the attempt's log.
220
+ */
221
+ declare function handoffWakeLines(handoff: HandoffDigest, taskName?: HandoffTaskName): string[];
222
+ /**
223
+ * A handoff as text, for a reader with no structure to render it from: the
224
+ * commit and summary, every finding it holds with its id, severity, detail
225
+ * and evidence, how many the digest left out, the artifacts, and the report
226
+ * when it has one.
227
+ */
228
+ declare function handoffLines(handoff: HandoffDigest | StoredHandoff, taskName?: HandoffTaskName): string[];
229
+ /**
230
+ * The tasks whose handoffs `taskId` may cite and read (ADR-0047): itself, its
231
+ * transitive dependencies, and its loop's other end — a check its body tasks,
232
+ * a body task its check (ADR-0045). The plan's own graph scopes the reach.
233
+ */
234
+ declare function readableTaskIds(plan: readonly Task[], taskId: string): Set<string>;
235
+ type HandoffCheck<H> = {
236
+ ok: true;
237
+ handoff: H;
238
+ } | {
239
+ ok: false;
240
+ refusal: string;
241
+ };
242
+ /**
243
+ * Check the references a handoff makes, at the call (ADR-0047): a `task`
244
+ * must be one the producer may read and a finding of it one that exists, and
245
+ * a `url` must be http or https, so no surface renders a `javascript:` link.
246
+ * A `file` or `commit` is a claim about a tree Ordewell may not see, so it is
247
+ * taken as given. An accepted handoff names each task by its id, which a
248
+ * later edit of the plan does not renumber.
249
+ */
250
+ declare function checkHandoff<H extends Handoff>(handoff: H, producerId: string, plan: readonly Task[]): HandoffCheck<H>;
251
+
1
252
  interface TaskDiffRead {
2
253
  repo?: string;
3
254
  path?: string;
@@ -34,6 +285,63 @@ type IsolationAvailability = {
34
285
  reason: IsolationInactiveReason;
35
286
  repos?: string[];
36
287
  };
288
+ /**
289
+ * What a repository's HEAD is. `unborn` is a repository with no commit yet,
290
+ * on `branch`; `unknown` is one git could not be asked about.
291
+ */
292
+ type RepoHead = {
293
+ kind: 'branch';
294
+ branch: string;
295
+ commit: string;
296
+ } | {
297
+ kind: 'detached';
298
+ commit: string;
299
+ } | {
300
+ kind: 'unborn';
301
+ branch?: string;
302
+ } | {
303
+ kind: 'unknown';
304
+ };
305
+ /** One repository of the group, as it is right now. */
306
+ interface PreviewRepo {
307
+ /** Relative to the workspace, as everywhere else: `.` for a workspace that is in one repository. */
308
+ path: string;
309
+ head: RepoHead;
310
+ /** Whether tracked files differ from HEAD; absent when not inspected (isolation is off or git is missing). */
311
+ dirty?: boolean;
312
+ /** Only for the lone repository: the workspace-relative path of its top level when the workspace is below it, such as `..`. */
313
+ top?: string;
314
+ }
315
+ /**
316
+ * What a current look at a workspace says about isolation, for a surface that
317
+ * shows it before any run (ADR-0013, ADR-0014). Every status but `failed`
318
+ * answers as `isActive` would for the same workspace, so the two cannot
319
+ * disagree; only `isActive` at run start is authoritative, and the base
320
+ * branch a run forks from is read again then.
321
+ *
322
+ * - `ready`: code tasks get isolated worktrees from `repos`. A repo with no
323
+ * commit yet is listed with an `unborn` head and shared instead.
324
+ * - `dirty`: as `ready` once the start-time decision (stash, or run without
325
+ * isolation) is made; `repos` marks the ones with `dirty`.
326
+ * - `disabled`, `git-missing`, `no-commits`: code tasks run in the workspace.
327
+ * - `no-repo`: nothing in the workspace is a repository (`repos` is empty).
328
+ * - `failed`: git could not be read; says nothing about whether there is a
329
+ * repository. `error` is the reason.
330
+ *
331
+ * Ops and script tasks use the user's checkout whatever the status.
332
+ */
333
+ interface IsolationPreview {
334
+ status: 'ready' | 'dirty' | 'disabled' | 'git-missing' | 'no-repo' | 'no-commits' | 'failed';
335
+ repos: PreviewRepo[];
336
+ /** What every task would share live, as `IsolationAvailability.shared` names it; empty unless `ready` or `dirty`. */
337
+ shared: string[];
338
+ error?: string;
339
+ }
340
+ /** The read-only look at a workspace's isolation: starts no run and changes nothing in any repository. */
341
+ interface IIsolationPreview {
342
+ /** Never rejects: a failed inspection is a `failed` preview. */
343
+ preview(workspaceRoot: string): Promise<IsolationPreview>;
344
+ }
37
345
  /**
38
346
  * Where a run's tasks work, as the planner is told it: the repos of the group
39
347
  * and the paths shared live between tasks. A lone repository is `['.']` with
@@ -105,6 +413,13 @@ interface IsolationTaskRecord {
105
413
  * so a stale reason cannot outlive the failure it explains.
106
414
  */
107
415
  landingError?: string;
416
+ /**
417
+ * The commit the task's done handoff stated, kept from the landing that
418
+ * first brought it: what Ordewell's own commits for the task are written
419
+ * from (ADR-0013), a repair's merge and every later landing included,
420
+ * though a repair's handoff states none.
421
+ */
422
+ commit?: HandoffCommit;
108
423
  /** Repo-relative paths, in `conflictRepo`, that conflicted; set only while `status` is `conflict` or `repairing`. */
109
424
  conflictFiles?: string[];
110
425
  /**
@@ -123,6 +438,12 @@ interface IsolationTaskRecord {
123
438
  * names it in a group of one, prefixed with its repo's path in a group.
124
439
  */
125
440
  repairedFiles?: string[];
441
+ /**
442
+ * A read-only task's (ADR-0046): never integrated, and removed whole
443
+ * whenever it is released, whatever its worktrees hold, with nothing kept
444
+ * on a branch and no git run inside them.
445
+ */
446
+ readOnly?: true;
126
447
  /**
127
448
  * Set once the task's landed work is in the checked-out HEAD of every repo
128
449
  * it changed — merged into the user's branch, by Merge all or by hand — or
@@ -273,7 +594,7 @@ type IsolationMergeBlockReason = 'merge-in-progress' | 'conflict' | 'uncommitted
273
594
  interface IsolationMergeBlock {
274
595
  repo: string;
275
596
  reason: IsolationMergeBlockReason;
276
- /** The files that would conflict, or the user's uncommitted ones the merge also changes; empty for the other reasons. */
597
+ /** The files that would conflict, or the user's uncommitted ones the merge would change or stand in the way of; empty for the other reasons. */
277
598
  files: string[];
278
599
  /** `wrong-branch` only: the branch the run started on. */
279
600
  expected?: string;
@@ -292,6 +613,9 @@ interface IsolationMergeBlock {
292
613
  * which cannot preflight, or for a reason no preflight could foresee. That
293
614
  * merge was aborted, leaving `repo` as it was; `landed` names the repos
294
615
  * merged before it, which stay merged, and is absent when there are none.
616
+ * A `failed` merge git itself refused carries the first line it said as
617
+ * `error`; one that never started, for a merge of the user's already in
618
+ * progress, carries none.
295
619
  *
296
620
  * A group of one is blocked only by a partial landing or by being on the wrong
297
621
  * branch; otherwise its one merge lands or is aborted whole, so it reports as
@@ -307,6 +631,7 @@ type IsolationMergeResult = {
307
631
  repo: string;
308
632
  files?: string[];
309
633
  landed?: string[];
634
+ error?: string;
310
635
  };
311
636
  /**
312
637
  * What `discard` does with each repo's integration branch: `keep` it for review
@@ -437,8 +762,13 @@ interface IWorktreeIsolation {
437
762
  * the Runner into. Fresh attempts start clean from the tips. A `restore`
438
763
  * puts the previous attempt's preserved work on top of them, falling back
439
764
  * to the tips in every repo when any restoration conflicts (ADR-0018).
765
+ * `readOnly` marks the record as a read-only task's (ADR-0046). A failure
766
+ * after the last attempt was replaced throws `PrepareFailedError`, naming
767
+ * the work that was kept on branches as that attempt's record went.
440
768
  */
441
- prepare(task: Task, run: IsolationRun, intent?: PrepareIntent): Promise<PreparedTask>;
769
+ prepare(task: Task, run: IsolationRun, intent?: PrepareIntent, opts?: {
770
+ readOnly?: boolean;
771
+ }): Promise<PreparedTask>;
442
772
  /**
443
773
  * Hand a conflicted task's kept workspace to a conflict repair (ADR-0015)
444
774
  * as it is — nothing is re-cut — and return the same cwd. Records each
@@ -463,13 +793,19 @@ interface IWorktreeIsolation {
463
793
  * tasks waiting at once the lowest plan order goes first. On anything but
464
794
  * `merged` the worktrees and refs stay, and nothing is resolved here: a
465
795
  * conflict is repaired, if at all, by a new attempt of the task in its own
466
- * worktree (ADR-0015), never inside this queue.
796
+ * worktree (ADR-0015), never inside this queue. A read-only task's record
797
+ * is never merged: it is `failed` (ADR-0046).
467
798
  *
468
799
  * `persist` is called once `run.landing` is set and before the first
469
800
  * merge; the caller saves the run there, synchronously, which is what
470
801
  * lets `pruneOrphans` finish a landing a crash interrupted.
802
+ *
803
+ * `commit`, the one the task's done handoff stated, is kept on the record,
804
+ * and what Ordewell commits and merges for the task is written from it;
805
+ * without one, the record's earlier commit, else the task title. A commit
806
+ * the runner made is never rewritten.
471
807
  */
472
- integrate(task: Task, run: IsolationRun, persist?: () => void): Promise<IsolationOutcome>;
808
+ integrate(task: Task, run: IsolationRun, persist?: () => void, commit?: HandoffCommit): Promise<IsolationOutcome>;
473
809
  /**
474
810
  * `keep: false` removes the task's worktree, branch and record (retry, task
475
811
  * removal), keeping whatever had not landed on a branch first; a record
@@ -477,9 +813,10 @@ interface IWorktreeIsolation {
477
813
  * worktree and branch exactly as they are
478
814
  * for inspection — a failed verdict, a stop, a cancel — and only moves the task off `active`,
479
815
  * so a crash-recovery prune does not sweep it away; a repair it ends leaves
480
- * the task `conflict`, as it was before the repair. Takes the run rather than
481
- * a bare task id: ids are only unique within one plan, and one daemon serves
482
- * many (ADR-0025 D2).
816
+ * the task `conflict`, as it was before the repair. A read-only task's
817
+ * record goes whole either way, with nothing kept (ADR-0046). Takes the run
818
+ * rather than a bare task id: ids are only unique within one plan, and one
819
+ * daemon serves many (ADR-0025 D2).
483
820
  */
484
821
  release(run: IsolationRun, taskId: string, opts: {
485
822
  keep: boolean;
@@ -677,6 +1014,18 @@ declare function isMeasured(totals: UsageTotals): boolean;
677
1014
  interface ContextFill {
678
1015
  usedTokens: number;
679
1016
  windowTokens?: number;
1017
+ /**
1018
+ * Why an API planner's automatic compaction (ADR-0002) is not running for
1019
+ * its model, as it stood when the meter was sent: the settings file turns it
1020
+ * off, or no catalog states the model's window. Absent while it runs, and on
1021
+ * a harness planner, whose agent compacts its own context.
1022
+ */
1023
+ autoCompaction?: {
1024
+ off: 'settings';
1025
+ } | {
1026
+ off: 'unknown_window';
1027
+ model: string;
1028
+ };
680
1029
  }
681
1030
  /** What the usage line draws of a ledger — live from its broadcast, or reloaded from the saved one. */
682
1031
  interface UsageLine {
@@ -768,9 +1117,9 @@ declare function taskUsageSummary(task: Pick<UsageTask, 'attemptUsage'>): TaskUs
768
1117
  declare function formatTokenCount(n: number): string;
769
1118
  /** Two decimals from 1 up and up to four below it: a call can cost a fraction of a cent, a session several dollars. */
770
1119
  declare function formatCost(currency: string, amount: number): string;
771
- /** `ctx 61k (6%)`, `ctx 61k` while the window is unknown, nothing before a call was measured. */
1120
+ /** `ctx 61k (6%)`, `ctx 61k` while the window is unknown, nothing before a call was measured; `· no auto-compact` when automatic compaction is not running. */
772
1121
  declare function contextMeterText(fill: ContextFill | undefined): string | null;
773
- /** Exact counts for the meter's hover. */
1122
+ /** Exact counts for the meter's hover, and why automatic compaction is not running, where it is not. */
774
1123
  declare function contextMeterTitle(fill: ContextFill): string;
775
1124
  /** The cost per currency as reported, joined by ` · `; null when none was reported. */
776
1125
  declare function costText(totals: UsageTotals): string | null;
@@ -818,7 +1167,30 @@ type ExecutionActor = {
818
1167
  | {
819
1168
  kind: 'autorun';
820
1169
  };
821
- type ExecutionCommandName = 'run_plan' | 'run_task' | 'stop_task' | 'stop_run' | 'retry_task' | 'message_task' | 'answer_approval' | 'merge_all' | 'skip_task' | 'force_start' | 'mark_complete' | 'mark_not_done' | 'interrupt_task' | 'force_send_queued' | 'remove_queued' | 'cancel_resume' | 'answer_checkpoint' | 'approve_review';
1170
+ type ExecutionCommandName = 'run_plan' | 'run_task' | 'stop_task' | 'stop_run' | 'retry_task' | 'message_task' | 'answer_approval' | 'merge_all' | 'skip_task' | 'force_start' | 'mark_complete' | 'mark_not_done' | 'interrupt_task' | 'force_send_queued' | 'remove_queued' | 'cancel_resume' | 'answer_checkpoint' | 'approve_review' | 'run_script';
1171
+ /**
1172
+ * How the user consented to the script a `run_script` call ran (ADR-0035):
1173
+ * on the approval card, by an effect allowlist rule (by name), by autorun in
1174
+ * Full, or by choosing Act for the supervisor.
1175
+ */
1176
+ type ScriptConsent = {
1177
+ by: 'card';
1178
+ } | {
1179
+ by: 'allowlist';
1180
+ rule: string;
1181
+ } | {
1182
+ by: 'autorun';
1183
+ } | {
1184
+ by: 'act';
1185
+ };
1186
+ /**
1187
+ * Where a `run_script` call's task stood as the call answered, or as it ended
1188
+ * later: `passed` and `failed` are its verdict, `changed-files` a pass that left
1189
+ * a tracked change waiting on the user, `stopped` an attempt ended with no
1190
+ * verdict, `running` one still under way after a minute, and `gated` one
1191
+ * waiting at its merge gate.
1192
+ */
1193
+ type ScriptResult = 'passed' | 'failed' | 'changed-files' | 'stopped' | 'running' | 'gated';
822
1194
  /**
823
1195
  * One call at the command seam as the timeline keeps it: what was asked, by
824
1196
  * whom, about which task, and how it ended — done, refused by the seam, or
@@ -838,10 +1210,16 @@ interface ExecutionRecord {
838
1210
  outcome: 'done' | 'refused' | 'declined';
839
1211
  /** The refusal, the message sent, or the note a denial carried. */
840
1212
  detail?: string;
1213
+ /** `run_script`: how the script was consented to, once it was. */
1214
+ consent?: ScriptConsent;
1215
+ /** `run_script`: where its task stood; one that answered before its end gets a record of that end too. */
1216
+ result?: ScriptResult;
1217
+ /** `run_script`: the record of that end, kept once the task ended after the call answered. */
1218
+ ended?: true;
841
1219
  at: string;
842
1220
  }
843
1221
 
844
- declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec", "improve-codebase-architecture", "tdd"];
1222
+ declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec", "improve-codebase-architecture", "tdd", "diagnose", "explore"];
845
1223
  interface SkillMetadata {
846
1224
  name: string;
847
1225
  description: string;
@@ -904,6 +1282,12 @@ declare class SkillsService {
904
1282
  /** A seed the user never edited: safe to replace with a newer built-in. */
905
1283
  private isUntouchedSeed;
906
1284
  private seed;
1285
+ /**
1286
+ * A built-in read where it ships, for a home that cannot hold the seed. It is
1287
+ * reported as `global` because that is the precedence a built-in has — it
1288
+ * still shadows a workspace copy of the same name.
1289
+ */
1290
+ private packagedBuiltin;
907
1291
  seedBuiltinSkill(name: string): boolean;
908
1292
  /** Removes a retired built-in's seed from the global dir, but only if it's untouched by the user. */
909
1293
  private isUnmodifiedRetiredSeed;
@@ -919,7 +1303,12 @@ declare class SkillsService {
919
1303
  * workspace folders the first read wins, the same way.
920
1304
  */
921
1305
  private catalog;
922
- /** Inspect installed skills without seeding, refreshing or pruning global files. */
1306
+ /**
1307
+ * Inspect installed skills without seeding, refreshing or pruning global
1308
+ * files. A built-in that is not seeded here still ships with Ordewell, so it
1309
+ * is listed at global precedence: this is the catalog the planner uses even
1310
+ * on a home that cannot hold the seeds.
1311
+ */
923
1312
  readCatalog(): SkillCatalog;
924
1313
  getSkillContent(name: string): string | undefined;
925
1314
  }
@@ -931,6 +1320,50 @@ declare class SkillsService {
931
1320
  */
932
1321
  declare function createSkillsService(workspaceRoot?: string, workspaceRepos?: readonly string[]): SkillsService;
933
1322
 
1323
+ /** How many times a loop's check runs when the loop sets no `maxRounds`. */
1324
+ declare const DEFAULT_LOOP_ROUNDS = 3;
1325
+ /** The most rounds a loop may set: past it, the fix is not converging, and a person should look. */
1326
+ declare const MAX_LOOP_ROUNDS = 10;
1327
+ interface TaskLoop {
1328
+ /** The top-level tasks, by id, that run to fix what the check found. */
1329
+ body: string[];
1330
+ /** The most times the check runs in one set of rounds. */
1331
+ maxRounds: number;
1332
+ }
1333
+ /** What the loop rules read of a task; structural, so a surface's own task shape answers them too. */
1334
+ interface LoopSubject {
1335
+ id: string;
1336
+ status: string;
1337
+ loop?: TaskLoop;
1338
+ /** The round of the check's latest run; absent is round 1. */
1339
+ loopRound?: number;
1340
+ verdict?: {
1341
+ outcome: string;
1342
+ reported?: string;
1343
+ };
1344
+ }
1345
+ /** The round of a check's latest run, or the one it is running. */
1346
+ declare function loopRoundOf(check: Pick<LoopSubject, 'loopRound'>): number;
1347
+ /**
1348
+ * A check between rounds (ADR-0045 L3): it found the work wanting with rounds
1349
+ * left, so its body is fixing what it found and it runs again once the fix
1350
+ * has landed. Its stored status stays `failed`; this is what keeps that from
1351
+ * counting as a failure.
1352
+ */
1353
+ declare function betweenRounds(check: LoopSubject): boolean;
1354
+ /** A check that found the work wanting in its last round: the loop is over, and the check failed. */
1355
+ declare function outOfRounds(check: LoopSubject): boolean;
1356
+ /** The check whose loop has `taskId` in its body, if any. */
1357
+ declare function checkOf<T extends Pick<LoopSubject, 'loop'>>(tasks: readonly T[], taskId: string): T | undefined;
1358
+ /** Whether a task is a loop's check or in a loop's body. */
1359
+ declare function inLoop(tasks: readonly Pick<LoopSubject, 'id' | 'loop'>[], task: Pick<LoopSubject, 'id' | 'loop'>): boolean;
1360
+ /**
1361
+ * Why a task may not be removed while it is a loop's check: its body would
1362
+ * be left as tasks that run whatever happens. Removing a body task takes it
1363
+ * out of its loop instead (ADR-0045 L8). Null when the removal may go ahead.
1364
+ */
1365
+ declare function loopRemovalRefusal(task: Pick<Task, 'title' | 'loop'>): string | null;
1366
+
934
1367
  /**
935
1368
  * The loop guards of an Act supervisor (ADR-0029): pure checks that stop it
936
1369
  * spinning. Each answers with the trip — the note the model is refused with,
@@ -962,6 +1395,16 @@ interface AttemptShape {
962
1395
  runner: string;
963
1396
  model: string | null;
964
1397
  effort: string | null;
1398
+ /**
1399
+ * A script task's body and time limit, read as a prompt is (ADR-0035):
1400
+ * what runs is all a script's retry could change. Absent for any other task,
1401
+ * and from a shape saved before script tasks.
1402
+ */
1403
+ script?: {
1404
+ commands: readonly (readonly string[])[] | null;
1405
+ script: string | null;
1406
+ timeoutMs: number;
1407
+ };
965
1408
  }
966
1409
  /**
967
1410
  * What the loop guards remember as plain data, saved on the plan so a daemon
@@ -998,8 +1441,13 @@ interface EditMemory {
998
1441
  * When the supervisor wakes during a run, and what it is told (#27). Only an
999
1442
  * attention event wakes it; passes ride along as context. It keeps no clock:
1000
1443
  * the host stamps each event and polls on its own tick.
1444
+ *
1445
+ * `task_report` is a task addressed to the planner ending: one whose result a
1446
+ * planner turn asked for and did not get before it answered (ADR-0037, R2).
1447
+ * It is batched as any attention event is, and wakes the planner whatever the
1448
+ * supervisor's level, since the planner asked for it.
1001
1449
  */
1002
- type RunEventKind = 'task_passed' | 'task_failed' | 'waiting_input' | 'waiting_approval' | 'task_idle' | 'landing_conflict' | 'usage_limit' | 'auto_merge_stopped' | 'run_finished';
1450
+ type RunEventKind = 'task_passed' | 'task_failed' | 'waiting_input' | 'waiting_approval' | 'task_idle' | 'landing_conflict' | 'usage_limit' | 'auto_merge_stopped' | 'run_finished' | 'task_report';
1003
1451
  type RunEvent = {
1004
1452
  kind: RunEventKind;
1005
1453
  taskId?: string;
@@ -1012,7 +1460,17 @@ type RunEvent = {
1012
1460
  resumeAt?: string | null;
1013
1461
  /** `usage_limit` in a batch: every task the runner's limit paused in it, in arrival order. */
1014
1462
  taskIds?: string[];
1463
+ /** `task_report` only: the result the planner asked for. */
1464
+ report?: TaskReportEvent;
1015
1465
  };
1466
+ interface TaskReportEvent {
1467
+ /** Whose turn started the task: the turn the report opens acts with its authority (ADR-0037, R3). */
1468
+ authority: TurnAuthority;
1469
+ /** Its verdict and the tail of its output, as the planner reads them: never cut to a digest line. */
1470
+ text: string;
1471
+ /** It passed: a pass, as far as the wake-up rate is concerned. */
1472
+ passed: boolean;
1473
+ }
1016
1474
  /** What the policy holds before its next wake-up, for the plan (ADR-0029). */
1017
1475
  interface WakePolicySnapshot {
1018
1476
  /** Attention events inside the debounce window, in arrival order. */
@@ -1039,9 +1497,12 @@ interface UserStep {
1039
1497
  instruction: string;
1040
1498
  completed: boolean;
1041
1499
  }
1042
- /** One deterministic signal gathered while verifying a completed task. `completion_marker` is only in verdicts saved before completion was reported through `task_complete` alone. */
1500
+ /**
1501
+ * One deterministic signal gathered while verifying a completed task. `completion_marker` is only in verdicts saved before completion was reported through `task_complete` alone.
1502
+ * `script_exit` is a script task's exit status, its one evidence (ADR-0035).
1503
+ */
1043
1504
  interface VerificationCheck {
1044
- name: 'exit_code' | 'completion_marker' | 'task_complete' | 'manual';
1505
+ name: 'exit_code' | 'completion_marker' | 'task_complete' | 'manual' | 'script_exit';
1045
1506
  passed: boolean;
1046
1507
  /** A check that did not apply. Skipped checks don't affect the verdict. */
1047
1508
  skipped: boolean;
@@ -1053,6 +1514,15 @@ interface Verdict {
1053
1514
  reason: string;
1054
1515
  checks: VerificationCheck[];
1055
1516
  decidedAt: string;
1517
+ /**
1518
+ * What the attempt's evidence said of the work: the status the runner's
1519
+ * `task_complete` call reported, or a script's exit read the same way —
1520
+ * `done` on exit 0, `failed` on a non-zero exit, a signal or its time limit,
1521
+ * `blocked` when it could not start. A loop starts a round only on `failed`
1522
+ * (ADR-0045 L2). Absent when nothing reported: a runner that exited without
1523
+ * the call, Mark complete, a stopped script, a verdict saved before it was kept.
1524
+ */
1525
+ reported?: 'done' | 'failed' | 'blocked';
1056
1526
  }
1057
1527
  interface TaskOutputSummary {
1058
1528
  reviewReason: string;
@@ -1062,7 +1532,8 @@ interface TaskOutputSummary {
1062
1532
  logTail: string;
1063
1533
  capturedAt: string;
1064
1534
  }
1065
- type TaskType = 'ai' | 'user';
1535
+ /** `script` is an ops task whose body Ordewell runs with no model (ADR-0035). */
1536
+ type TaskType = 'ai' | 'user' | 'script';
1066
1537
  /**
1067
1538
  * `skipped` is terminal and verdict-less: the task will not be waited for, and
1068
1539
  * nothing it did lands. Its dependents go ahead without it once the work it
@@ -1120,6 +1591,13 @@ interface Task {
1120
1591
  subtasks: Task[];
1121
1592
  verdict?: Verdict;
1122
1593
  outputSummary?: TaskOutputSummary;
1594
+ /**
1595
+ * The latest attempt's handoff, as the plan keeps it (ADR-0047): its
1596
+ * summary, first findings and artifacts. The whole handoff, report and all,
1597
+ * is in that attempt's log. Absent when the attempt handed none over, and
1598
+ * on a plan saved before handoffs, which reads `outputSummary` instead.
1599
+ */
1600
+ handoff?: HandoffDigest;
1123
1601
  assignedModel?: TaskModelAssignment;
1124
1602
  assignedRunner: RunnerId;
1125
1603
  taskMode?: TaskMode;
@@ -1156,9 +1634,14 @@ interface Task {
1156
1634
  * An ops task (ADR-0020): it changes no repository files, runs at the
1157
1635
  * workspace root rather than in a worktree, and waits for the change tasks
1158
1636
  * it depends on to be merged into the user's branch. Top-level AI tasks
1159
- * only; absent means a change task.
1637
+ * only; absent means a change task. A script task is one by nature and
1638
+ * stores none (see {@link runsAsOps}).
1160
1639
  */
1161
1640
  ops?: boolean;
1641
+ /** A top-level read-only AI task whose report returns to the planner (ADR-0037 R1). */
1642
+ research?: boolean;
1643
+ /** A retry still owes research's report under its original authority (ADR-0037 R3). */
1644
+ researchOrigin?: TaskReportTo;
1162
1645
  /**
1163
1646
  * The dependencies, by title, whose work was not merged into the user's
1164
1647
  * branch when the user force-started this task past its merge gate
@@ -1179,8 +1662,56 @@ interface Task {
1179
1662
  * counts as approved to run (ADR-0029). Set by the session, never by an op.
1180
1663
  */
1181
1664
  addedBy?: 'supervisor';
1665
+ /**
1666
+ * An attempt of it has started, so its chat exists from then on: kept after
1667
+ * a failure, a cancel, a skip or a completion, and restored with the plan.
1668
+ * Set by the attempt's own start — never by a status or a manual mark.
1669
+ */
1670
+ attempted?: true;
1182
1671
  attemptUsage?: AttemptUsage[];
1672
+ /**
1673
+ * A script task's body (ADR-0035), exactly one of these two: argument
1674
+ * vectors run in order with no shell, stopping at the first that fails, or
1675
+ * one script run by the research shell's interpreter.
1676
+ */
1677
+ commands?: string[][];
1678
+ script?: string;
1679
+ /** A script task's time limit; absent means ten minutes (`DEFAULT_SCRIPT_TIMEOUT_MS`). */
1680
+ timeoutMs?: number;
1681
+ /** Set on a loop's check (ADR-0045): the tasks that fix what it finds, and how many times it may run. */
1682
+ loop?: TaskLoop;
1683
+ /**
1684
+ * The round of a loop's check's latest run, or the one it is running;
1685
+ * absent is round 1. Execution state, saved so a reload resumes mid-round,
1686
+ * and started over by a retry of the check (ADR-0045 L5, L7).
1687
+ */
1688
+ loopRound?: number;
1689
+ /**
1690
+ * A planner turn waits for this task's result (ADR-0037, R2): set when the
1691
+ * tool that started it answered before it ended, or when research lands.
1692
+ * Cleared once its end is reported, in a turn acting for whoever started it (R3).
1693
+ */
1694
+ reportTo?: TaskReportTo;
1183
1695
  }
1696
+ /**
1697
+ * Who started a task whose result is owed to the planner (ADR-0037, R3): a
1698
+ * planner turn in the user's conversation, or the supervisor, at its level
1699
+ * then, with script consent (ADR-0035) or the research profile (ADR-0037 R4).
1700
+ */
1701
+ type TaskReportTo = ({
1702
+ consent: ScriptConsent;
1703
+ research?: never;
1704
+ } | {
1705
+ research: true;
1706
+ consent?: never;
1707
+ }) & ({
1708
+ actor: 'planner-turn';
1709
+ } | {
1710
+ actor: 'supervisor';
1711
+ level: SupervisorLevel;
1712
+ });
1713
+ /** Whose authority a turn opened without the user acts with (ADR-0037, R3): the user's turn, or the supervisor's. */
1714
+ type TurnAuthority = 'user' | 'supervisor';
1184
1715
  type ResearchToolType = 'read_file' | 'read_files' | 'glob' | 'grep' | 'find_symbol' | 'list_dir' | 'bash' | 'fetch' | 'web_search' | 'spawn_research_agent' | 'task_query' | 'task_output' | 'task_diff'
1185
1716
  /**
1186
1717
  * A tool belonging to a harness planner's own toolbox (ADR-0009) that has no
@@ -1239,7 +1770,7 @@ interface SubagentLogEntry {
1239
1770
  }
1240
1771
  type ResearchLogEntry = ResearchStep | UserPromptEntry | SubagentLogEntry;
1241
1772
  interface ResearchProgress {
1242
- type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness' | 'text_delta' | 'text_retracted' | 'usage' | 'subagent_started' | 'subagent_finished';
1773
+ type: 'thinking' | 'tool_call' | 'tool_result' | 'plan_token' | 'interrupted' | 'liveness' | 'text_delta' | 'text_retracted' | 'usage' | 'subagent_started' | 'subagent_finished' | 'compaction';
1243
1774
  /** Minted by whoever runs the turn and passed through untouched; absent outside a turn. */
1244
1775
  turnId?: string;
1245
1776
  /** One continuous run of model text — text before a tool call is its own segment. */
@@ -1264,6 +1795,7 @@ interface ResearchProgress {
1264
1795
  outcome?: SubagentOutcome;
1265
1796
  digest?: string;
1266
1797
  usage?: UsageTotals;
1798
+ compaction?: PlannerCompaction;
1267
1799
  }
1268
1800
  interface DiscoveredModel {
1269
1801
  modelId: string;
@@ -1293,9 +1825,12 @@ interface DiscoveredModel {
1293
1825
  /**
1294
1826
  * The model's context window when the runner or catalog reports it (#49).
1295
1827
  * Read by the planner-model lookup so context fill can be shown; absent when
1296
- * unknown rather than defaulted to zero.
1828
+ * unknown rather than defaulted to zero. Gemini's is its `inputTokenLimit`:
1829
+ * the prompt alone, the reply limited by `maxOutputTokens` apart from it.
1297
1830
  */
1298
1831
  contextWindow?: number;
1832
+ /** The longest reply the catalog says the model is served with, when it says. */
1833
+ maxOutputTokens?: number;
1299
1834
  }
1300
1835
  type PlanStatus = 'draft' | 'approved' | 'rejected' | 'running' | 'completed';
1301
1836
  /**
@@ -1312,8 +1847,8 @@ interface ConversationMessage {
1312
1847
  * Timeline marker: 'plan_generated' records the point in the dialogue where
1313
1848
  * the plan was committed (the UI anchors the plan card there on restore);
1314
1849
  * 'system' is a host-injected notice; 'compaction' is the summary a
1315
- * user-triggered compaction left in place of the earlier messages — always
1316
- * the transcript's first entry. 'skill_load' is a skill loaded into the
1850
+ * compaction appended, which stands in for the earlier messages in the
1851
+ * planner's context while the transcript keeps them (see `checkpoint`). 'skill_load' is a skill loaded into the
1317
1852
  * conversation, its snapshot in `skill`; one the user invoked follows the
1318
1853
  * message that named it. 'execution' is a call at the command seam
1319
1854
  * (ADR-0029), its record in `execution`. 'wake_up' is the digest a wake-up
@@ -1333,6 +1868,13 @@ interface ConversationMessage {
1333
1868
  execution?: ExecutionRecord;
1334
1869
  /** A user's message that was force sent, stopping the turn before it (ADR-0023). */
1335
1870
  forced?: true;
1871
+ /**
1872
+ * Set on a 'compaction' entry that is a checkpoint of the planner's context
1873
+ * (ADR-0002). A 'compaction' entry without one is a legacy summary when it
1874
+ * stands first — it replaced what it condensed — and a marker drawn but
1875
+ * never replayed anywhere else.
1876
+ */
1877
+ checkpoint?: ContextCheckpoint;
1336
1878
  /**
1337
1879
  * What a reply showed, run by run, where each began (ADR-0017 A1). Beside
1338
1880
  * `content`, which stays the reply as the planner is told it. Absent on
@@ -1340,6 +1882,50 @@ interface ConversationMessage {
1340
1882
  */
1341
1883
  runs?: ReplyRun[];
1342
1884
  }
1885
+ /**
1886
+ * Where a compaction cut the planner's context (ADR-0002): its summary stands
1887
+ * for every transcript entry before `keptFrom`, and the entries from there on
1888
+ * replay verbatim. Entries are only appended, or inserted at a held turn's
1889
+ * position, which no checkpoint's cut passes, so the index never moves.
1890
+ */
1891
+ interface ContextCheckpoint {
1892
+ /** Stable across saves, forks and the wire, so a surface can tell this checkpoint from another with the same text. */
1893
+ id: string;
1894
+ /** An index into the whole transcript, at or before the checkpoint entry itself. */
1895
+ keptFrom: number;
1896
+ trigger: 'manual' | 'auto';
1897
+ }
1898
+ /**
1899
+ * How a planner compaction ended. `condensed`: a summary stands in for the
1900
+ * older context (a checkpoint was written, or a harness planner reported its
1901
+ * own compaction done). `pruned`: older recoverable tool output was dropped
1902
+ * from the live context alone. `unchanged`: nothing needed doing. `paused`:
1903
+ * the context could not be brought to fit, so the planner waits.
1904
+ */
1905
+ type PlannerCompactionOutcome = 'condensed' | 'pruned' | 'unchanged' | 'stopped' | 'failed' | 'paused';
1906
+ /**
1907
+ * One planner compaction's lifecycle as it was observed (ADR-0017): both
1908
+ * phases share `id`, which is the checkpoint's id when one is written. Only
1909
+ * Ordewell's own compaction announces a start; a harness planner's native one
1910
+ * is reported when it is over, and nothing invents the start it never sent.
1911
+ * Sizes are only ever what a backend reported.
1912
+ */
1913
+ type PlannerCompaction = {
1914
+ phase: 'started';
1915
+ id: string;
1916
+ origin: 'ordewell';
1917
+ trigger: ContextCheckpoint['trigger'];
1918
+ at: string;
1919
+ } | {
1920
+ phase: 'ended';
1921
+ id: string;
1922
+ origin: 'ordewell' | 'native';
1923
+ trigger: ContextCheckpoint['trigger'];
1924
+ outcome: PlannerCompactionOutcome;
1925
+ at: string;
1926
+ preTokens?: number;
1927
+ postTokens?: number;
1928
+ };
1343
1929
  /**
1344
1930
  * One run of a planner reply's text: the segment it streamed under, its
1345
1931
  * settled text, and when it began. A run that never streamed (a message the
@@ -1403,6 +1989,12 @@ interface PendingPlanTurn {
1403
1989
  skills?: SkillLoad[];
1404
1990
  /** Force sent: it waits ahead of every input not forced, and its turn is its own (ADR-0023, F2). */
1405
1991
  forced?: true;
1992
+ /**
1993
+ * A supervisor input carrying a report the planner asked for (ADR-0037, R2),
1994
+ * which opens a turn at every supervisor level: `user` for a task started in
1995
+ * the user's turn, which the turn then acts as (R3).
1996
+ */
1997
+ report?: TurnAuthority;
1406
1998
  }
1407
1999
  /**
1408
2000
  * The run's own copy of the user's run settings (ADR-0030): taken when the
@@ -1441,7 +2033,35 @@ interface PlannerResendState {
1441
2033
  runner?: RunnerId;
1442
2034
  at?: string;
1443
2035
  }
2036
+ /**
2037
+ * The planner paused on its context (ADR-0002): an API planner's call could
2038
+ * not be brought inside its model's window, so the turn ended with nothing
2039
+ * landed and the planner waits for the user's Retry compaction. What it
2040
+ * keeps is bounded: the inputs stay in `pendingTurns`, and only the calls
2041
+ * that changed something are listed, never the live context itself.
2042
+ */
2043
+ interface PlannerContextPause {
2044
+ /** The model the call did not fit. */
2045
+ model: string;
2046
+ at: string;
2047
+ /** What the user is told: why it paused, and what Retry compaction does. */
2048
+ reason: string;
2049
+ /** The pending inputs the paused turn took: a retry answers these, and what arrived since waits for the turn after it. */
2050
+ inputs: string[];
2051
+ /**
2052
+ * The calls the paused turn made that changed something or reached out,
2053
+ * each with the head of what it returned (`effectLedger`). It is never
2054
+ * replayed to the planner as a note: it is what lets a retry see that a
2055
+ * turn which ran effects cannot start again from the transcript, and what
2056
+ * the refusal lists for the user.
2057
+ */
2058
+ done?: string[];
2059
+ /** More such calls ran than `done` keeps: the refusal could not list all of them, and a retry without the live context stays refused. */
2060
+ doneTruncated?: true;
2061
+ }
1444
2062
  interface LegacyPlanState {
2063
+ /** Research profiles the planner approved as they landed: lifting one withdraws its approval (ADR-0037 R4). */
2064
+ researchApprovals?: string[];
1445
2065
  runSettings?: RunSettings;
1446
2066
  /** What supervision remembers across a daemon restart (ADR-0029); absent on plans saved before it existed. */
1447
2067
  supervision?: SupervisionSnapshot;
@@ -1466,12 +2086,21 @@ interface LegacyPlanState {
1466
2086
  * must leave it behind rather than share it.
1467
2087
  */
1468
2088
  isolation?: PlanIsolation;
2089
+ /**
2090
+ * The id of the plan's last isolated run, which closed with everything it
2091
+ * landed merged into the user's branch; absent once a later run decides how
2092
+ * it executes. With no run record left, it is what tells a Merge all asked
2093
+ * for after that run from one in a plan that never isolated (ADR-0029).
2094
+ */
2095
+ mergedRun?: string;
1469
2096
  /** Kept so a reopened session shows the same token line (#49). */
1470
2097
  plannerUsage?: PlannerUsage;
1471
2098
  /** The windows reported over the session, so a reopened one knows each model's from its first call. */
1472
2099
  contextWindows?: ReportedWindow[];
1473
2100
  /** The planner hit its usage limit and the inputs its turn took wait to be sent again; absent otherwise. */
1474
2101
  plannerResend?: PlannerResendState;
2102
+ /** The planner's context could not fit its model's window: its turn's inputs wait for Retry compaction; absent otherwise. */
2103
+ plannerContextPause?: PlannerContextPause;
1475
2104
  /**
1476
2105
  * The tasks the user approved to run: every task when they executed the
1477
2106
  * plan, and each one they started or retried themselves since. A command
@@ -1555,11 +2184,12 @@ declare function pausedOnLimit(task: Readonly<Task>): boolean;
1555
2184
  /** The user's approval record, as a plan carries it (ADR-0029). */
1556
2185
  type ApprovalRecord = Pick<LegacyPlanState, 'approved' | 'approvedTasks'>;
1557
2186
  /**
1558
- * A task the scheduler leaves for the user (ADR-0035): an AI task not started
1559
- * yet, in a plan they approved, outside their approval record — the planner
1560
- * added it since. It starts once they start it or execute the plan. In a plan
1561
- * they never approved nothing is theirs yet, so nothing is singled out.
2187
+ * A task the scheduler leaves for the user (ADR-0035): an AI or script task
2188
+ * not started yet, in a plan they approved, outside their approval record —
2189
+ * the planner added it since, or changed a script's body. It starts once they
2190
+ * start it or execute the plan. In a plan they never approved nothing is
2191
+ * theirs yet, so nothing is singled out.
1562
2192
  */
1563
2193
  declare function waitsForApproval(task: Pick<Task, 'id' | 'type' | 'status'>, record: ApprovalRecord): boolean;
1564
2194
 
1565
- export { type RepoGroupLayout as $, type ApprovalRecord as A, BUILTIN_SKILL_NAMES as B, type ContextFill as C, DEFAULT_RUNNERS as D, type ExecutionActor as E, type IsolationTaskRef as F, type GuardTarget as G, type IsolationTaskRepo as H, type IWorktreeIsolation as I, type IsolationTaskStatus as J, type IsolationView as K, type LegacyPlanState as L, type ModelUsage as M, type PlanIsolation as N, type PlanState as O, type PendingPlanTurn as P, type PlanStatus as Q, type PlanUsage as R, type PlannerResendState as S, type PlannerUsage as T, type PrepareIntent as U, type PreparedTask as V, type PreservedWork as W, type PreviousWork as X, type RefusedRemoval as Y, type RepairEvidence as Z, type ReplyRun as _, type AttemptUsage as a, pausedOnLimit as a$, type ReportedWindow as a0, type ResearchLogEntry as a1, type ResearchProgress as a2, type ResearchStep as a3, type ResearchStepOutcome as a4, type ResearchToolType as a5, type RunSettings as a6, type RunnerId as a7, type RunnerSessionCost as a8, type SessionBilling as a9, type UsageTotals as aA, type Verdict as aB, type VerificationCheck as aC, addAttemptUsage as aD, addModelUsage as aE, addPlannerUsage as aF, addUsage as aG, addUsageTotals as aH, contextMeterText as aI, contextMeterTitle as aJ, costText as aK, countedSessionCost as aL, createEmptyPlan as aM, createSkillsService as aN, createTask as aO, endAttemptUsage as aP, flattenTasks as aQ, flattenTasksWithParents as aR, formatCost as aS, formatTokenCount as aT, isAwaitingReason as aU, isMeasured as aV, isSupervisorLevel as aW, isUserMessage as aX, migratePlanState as aY, modelCostTitle as aZ, partedPromptUsage as a_, type SetupFailure as aa, type ShadowedSkill as ab, type SkillAppliesTo as ac, type SkillCatalog as ad, type SkillInfo as ae, type SkillLoad as af, type SkillLoadNotice as ag, type SkillMetadata as ah, type SkillSource as ai, SkillsService as aj, type SubagentOutcome as ak, type SupervisorLevel as al, type Task as am, type TaskDiffRead as an, type TaskDiffResult as ao, type TaskIsolation as ap, type TaskIsolationState as aq, type TaskModelAssignment as ar, type TaskSkillNotice as as, type TaskSkillSnapshot as at, type TaskSnapshot as au, type TaskStatus as av, type TaskUsageSummary as aw, type TreeSnapshot as ax, type UsageLine as ay, type UsageRecord as az, type AwaitingReason as b, planUsage as b0, plannerContextFill as b1, rejoinPlanTree as b2, resumedSessionWindow as b3, startAttemptUsage as b4, subagentCostTitle as b5, taskUsageSummary as b6, tasksTotalText as b7, usageLine as b8, usageText as b9, waitsForApproval as ba, type ConversationMessage as c, type CostProvenance as d, DEFAULT_SUPERVISOR_RETRY_LIMIT as e, type DiscoveredModel as f, type ExecutionCommandName as g, type ExecutionRecord as h, type GuardTrip as i, type IntegrationDisposal as j, type InvalidSkill as k, type IsolationAvailability as l, type IsolationHandoff as m, type IsolationHandoffRepo as n, type IsolationInactiveReason as o, type IsolationLandedTask as p, type IsolationLanding as q, type IsolationMergeBlock as r, type IsolationMergeBlockReason as s, type IsolationMergeResult as t, type IsolationOutcome as u, type IsolationPruneResult as v, type IsolationRemoval as w, type IsolationRepo as x, type IsolationRun as y, type IsolationTaskRecord as z };
2195
+ export { type IsolationTaskStatus as $, type ApprovalRecord as A, BUILTIN_SKILL_NAMES as B, type ContextCheckpoint as C, DEFAULT_LOOP_ROUNDS as D, type ExecutionActor as E, type FindingSeverity as F, type GuardTarget as G, HANDOFF_LIMITS as H, type IIsolationPreview as I, type IsolationAvailability as J, type IsolationHandoff as K, type IsolationHandoffRepo as L, type IsolationInactiveReason as M, type IsolationLandedTask as N, type IsolationLanding as O, type IsolationMergeBlock as P, type IsolationMergeBlockReason as Q, type IsolationMergeResult as R, type IsolationOutcome as S, type IsolationPreview as T, type IsolationPruneResult as U, type IsolationRemoval as V, type IsolationRepo as W, type IsolationRun as X, type IsolationTaskRecord as Y, type IsolationTaskRef as Z, type IsolationTaskRepo as _, type AttemptUsage as a, type TaskSkillSnapshot as a$, type IsolationView as a0, type LegacyPlanState as a1, type LoopSubject as a2, MAX_LOOP_ROUNDS as a3, type ModelUsage as a4, type PatchFlag as a5, type PatchView as a6, type PendingPlanTurn as a7, type PlanIsolation as a8, type PlanState as a9, type RunnerSessionCost as aA, SEVERITY_LABEL as aB, type ScriptConsent as aC, type ScriptResult as aD, type SessionBilling as aE, type SetupFailure as aF, type ShadowedSkill as aG, type SkillAppliesTo as aH, type SkillCatalog as aI, type SkillInfo as aJ, type SkillLoad as aK, type SkillLoadNotice as aL, type SkillMetadata as aM, type SkillSource as aN, SkillsService as aO, type StoredHandoff as aP, type SubagentOutcome as aQ, type SupervisorLevel as aR, type Task as aS, type TaskDiffRead as aT, type TaskDiffResult as aU, type TaskIsolation as aV, type TaskIsolationState as aW, type TaskLoop as aX, type TaskModelAssignment as aY, type TaskReportTo as aZ, type TaskSkillNotice as a_, type PlanStatus as aa, type PlanUsage as ab, type PlannerCompaction as ac, type PlannerCompactionOutcome as ad, type PlannerContextPause as ae, type PlannerResendState as af, type PlannerUsage as ag, type PrepareIntent as ah, type PreparedTask as ai, type PreservedWork as aj, type PreviewRepo as ak, type PreviousWork as al, REPORT_MAX_CHARS as am, type RefusedRemoval as an, type RepairEvidence as ao, type ReplyRun as ap, type RepoGroupLayout as aq, type RepoHead as ar, type ReportedWindow as as, type ResearchLogEntry as at, type ResearchProgress as au, type ResearchStep as av, type ResearchStepOutcome as aw, type ResearchToolType as ax, type RunSettings as ay, type RunnerId as az, type AwaitingReason as b, resumedSessionWindow as b$, type TaskSnapshot as b0, type TaskStatus as b1, type TaskType as b2, type TaskUsageSummary as b3, type TreeSnapshot as b4, type UsageLine as b5, type UsageRecord as b6, type UsageTotals as b7, type Verdict as b8, type VerificationCheck as b9, handoffPreviewLines as bA, handoffReferenceText as bB, handoffWakeLines as bC, inLoop as bD, isAwaitingReason as bE, isMeasured as bF, isSupervisorLevel as bG, isUserMessage as bH, loopRemovalRefusal as bI, loopRoundOf as bJ, migratePlanState as bK, modelCostTitle as bL, namedHandoff as bM, openingLines as bN, outOfRounds as bO, partedPromptUsage as bP, patchChangesText as bQ, patchFlagText as bR, patchLines as bS, patchLinesText as bT, patchText as bU, patchView as bV, pausedOnLimit as bW, planUsage as bX, plannerContextFill as bY, readableTaskIds as bZ, rejoinPlanTree as b_, addAttemptUsage as ba, addModelUsage as bb, addPlannerUsage as bc, addUsage as bd, addUsageTotals as be, artifactLines as bf, betweenRounds as bg, boundedHandoffLines as bh, byteSize as bi, checkHandoff as bj, checkOf as bk, contextMeterText as bl, contextMeterTitle as bm, costText as bn, countedSessionCost as bo, createEmptyPlan as bp, createSkillsService as bq, createTask as br, endAttemptUsage as bs, findingLines as bt, flattenTasks as bu, flattenTasksWithParents as bv, formatCost as bw, formatTokenCount as bx, handoffDigest as by, handoffLines as bz, type ContextFill as c, savedPatchOf as c0, savedPatchView as c1, savedPatchViews as c2, severityLabel as c3, startAttemptUsage as c4, storedHandoff as c5, subagentCostTitle as c6, taskUsageSummary as c7, tasksTotalText as c8, usageLine as c9, usageText as ca, waitsForApproval as cb, withPatch as cc, worstSeverity as cd, type ConversationMessage as d, type CostProvenance as e, DEFAULT_RUNNERS as f, DEFAULT_SUPERVISOR_RETRY_LIMIT as g, DIGEST_FINDINGS as h, type DiscoveredModel as i, type ExecutionCommandName as j, type ExecutionRecord as k, type GuardTrip as l, HANDOFF_PUSH_CHARS as m, type Handoff as n, type HandoffArtifact as o, type HandoffCheck as p, type HandoffCommit as q, type HandoffDigest as r, type HandoffEvidence as s, type HandoffFinding as t, type HandoffPatch as u, type HandoffPreviewOptions as v, type HandoffTaskName as w, type IWorktreeIsolation as x, type IntegrationDisposal as y, type InvalidSkill as z };