@cleocode/contracts 2026.9.20 → 2026.9.21

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 (161) hide show
  1. package/README.md +1 -1
  2. package/dist/__tests__/decide.test.d.ts +7 -0
  3. package/dist/__tests__/decide.test.d.ts.map +1 -0
  4. package/dist/__tests__/decide.test.js +91 -0
  5. package/dist/__tests__/decide.test.js.map +1 -0
  6. package/dist/__tests__/evidence-atom-schema.test.js +2 -1
  7. package/dist/__tests__/evidence-atom-schema.test.js.map +1 -1
  8. package/dist/__tests__/memory-wire-shapes.test.js.map +1 -1
  9. package/dist/acceptance-gate-schema.d.ts +5 -0
  10. package/dist/acceptance-gate-schema.d.ts.map +1 -1
  11. package/dist/acceptance-gate-schema.js +26 -0
  12. package/dist/acceptance-gate-schema.js.map +1 -1
  13. package/dist/acceptance-gate.d.ts +70 -3
  14. package/dist/acceptance-gate.d.ts.map +1 -1
  15. package/dist/backup-manifest.d.ts +13 -2
  16. package/dist/backup-manifest.d.ts.map +1 -1
  17. package/dist/branch-lock.d.ts +11 -0
  18. package/dist/branch-lock.d.ts.map +1 -1
  19. package/dist/branch-lock.js +31 -4
  20. package/dist/branch-lock.js.map +1 -1
  21. package/dist/cloud/api.d.ts +607 -0
  22. package/dist/cloud/api.d.ts.map +1 -0
  23. package/dist/cloud/api.js +417 -0
  24. package/dist/cloud/api.js.map +1 -0
  25. package/dist/cloud/envelope.d.ts +84 -0
  26. package/dist/cloud/envelope.d.ts.map +1 -0
  27. package/dist/cloud/envelope.js +54 -0
  28. package/dist/cloud/envelope.js.map +1 -0
  29. package/dist/cloud/ids.d.ts +30 -0
  30. package/dist/cloud/ids.d.ts.map +1 -0
  31. package/dist/cloud/ids.js +32 -0
  32. package/dist/cloud/ids.js.map +1 -0
  33. package/dist/cloud/index.d.ts +17 -0
  34. package/dist/cloud/index.d.ts.map +1 -0
  35. package/dist/cloud/index.js +17 -0
  36. package/dist/cloud/index.js.map +1 -0
  37. package/dist/data-accessor.d.ts +23 -3
  38. package/dist/data-accessor.d.ts.map +1 -1
  39. package/dist/data-accessor.js.map +1 -1
  40. package/dist/decide.d.ts +333 -0
  41. package/dist/decide.d.ts.map +1 -0
  42. package/dist/decide.js +184 -0
  43. package/dist/decide.js.map +1 -0
  44. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  45. package/dist/dispatch/operations-registry.js +55 -0
  46. package/dist/dispatch/operations-registry.js.map +1 -1
  47. package/dist/done-plan.d.ts +311 -0
  48. package/dist/done-plan.d.ts.map +1 -0
  49. package/dist/done-plan.js +17 -0
  50. package/dist/done-plan.js.map +1 -0
  51. package/dist/evidence-atom-schema.d.ts +18 -1
  52. package/dist/evidence-atom-schema.d.ts.map +1 -1
  53. package/dist/evidence-atom-schema.js +27 -2
  54. package/dist/evidence-atom-schema.js.map +1 -1
  55. package/dist/exit-codes.d.ts +40 -0
  56. package/dist/exit-codes.d.ts.map +1 -1
  57. package/dist/exit-codes.js +41 -0
  58. package/dist/exit-codes.js.map +1 -1
  59. package/dist/graph.d.ts +40 -0
  60. package/dist/graph.d.ts.map +1 -1
  61. package/dist/index.d.ts +13 -6
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +4 -0
  64. package/dist/index.js.map +1 -1
  65. package/dist/memory/observe.d.ts +16 -0
  66. package/dist/memory/observe.d.ts.map +1 -1
  67. package/dist/operations/__tests__/release-shared-surface.test.d.ts +10 -0
  68. package/dist/operations/__tests__/release-shared-surface.test.d.ts.map +1 -0
  69. package/dist/operations/__tests__/release-shared-surface.test.js +84 -0
  70. package/dist/operations/__tests__/release-shared-surface.test.js.map +1 -0
  71. package/dist/operations/dialectic.d.ts +16 -0
  72. package/dist/operations/dialectic.d.ts.map +1 -1
  73. package/dist/operations/memory.d.ts +48 -0
  74. package/dist/operations/memory.d.ts.map +1 -1
  75. package/dist/operations/nexus-scope-map.d.ts +8 -0
  76. package/dist/operations/nexus-scope-map.d.ts.map +1 -1
  77. package/dist/operations/nexus-scope-map.js +8 -0
  78. package/dist/operations/nexus-scope-map.js.map +1 -1
  79. package/dist/operations/nexus-user-profile.d.ts +27 -1
  80. package/dist/operations/nexus-user-profile.d.ts.map +1 -1
  81. package/dist/operations/nexus-user-profile.js +11 -1
  82. package/dist/operations/nexus-user-profile.js.map +1 -1
  83. package/dist/operations/nexus.d.ts +394 -3
  84. package/dist/operations/nexus.d.ts.map +1 -1
  85. package/dist/operations/release.d.ts +259 -0
  86. package/dist/operations/release.d.ts.map +1 -1
  87. package/dist/operations/release.js +130 -1
  88. package/dist/operations/release.js.map +1 -1
  89. package/dist/operations/session.d.ts +6 -4
  90. package/dist/operations/session.d.ts.map +1 -1
  91. package/dist/operations/tasks.d.ts +6 -0
  92. package/dist/operations/tasks.d.ts.map +1 -1
  93. package/dist/operations/tasks.js +1 -0
  94. package/dist/operations/tasks.js.map +1 -1
  95. package/dist/operations/validate.d.ts +5 -0
  96. package/dist/operations/validate.d.ts.map +1 -1
  97. package/dist/operations/worktree.d.ts +125 -1
  98. package/dist/operations/worktree.d.ts.map +1 -1
  99. package/dist/process-probe.d.ts +17 -0
  100. package/dist/process-probe.d.ts.map +1 -0
  101. package/dist/process-probe.js +21 -0
  102. package/dist/process-probe.js.map +1 -0
  103. package/dist/project-context.d.ts +44 -2
  104. package/dist/project-context.d.ts.map +1 -1
  105. package/dist/project-lifecycle.d.ts +143 -0
  106. package/dist/project-lifecycle.d.ts.map +1 -0
  107. package/dist/project-lifecycle.js +21 -0
  108. package/dist/project-lifecycle.js.map +1 -0
  109. package/dist/release/plan.d.ts +6 -2
  110. package/dist/release/plan.d.ts.map +1 -1
  111. package/dist/release/plan.js +8 -2
  112. package/dist/release/plan.js.map +1 -1
  113. package/dist/release/plan.test.js +6 -1
  114. package/dist/release/plan.test.js.map +1 -1
  115. package/dist/table-classification.d.ts +208 -0
  116. package/dist/table-classification.d.ts.map +1 -0
  117. package/dist/table-classification.js +23 -0
  118. package/dist/table-classification.js.map +1 -0
  119. package/dist/task.d.ts +79 -0
  120. package/dist/task.d.ts.map +1 -1
  121. package/package.json +6 -2
  122. package/schemas/gate-result.schema.json +43 -0
  123. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  124. package/src/__tests__/decide.test.ts +139 -0
  125. package/src/__tests__/evidence-atom-schema.test.ts +2 -1
  126. package/src/__tests__/memory-wire-shapes.test.ts +3 -0
  127. package/src/acceptance-gate-schema.ts +29 -0
  128. package/src/acceptance-gate.ts +68 -3
  129. package/src/backup-manifest.ts +13 -2
  130. package/src/branch-lock.ts +36 -4
  131. package/src/cloud/api.ts +512 -0
  132. package/src/cloud/envelope.ts +63 -0
  133. package/src/cloud/ids.ts +51 -0
  134. package/src/cloud/index.ts +16 -0
  135. package/src/data-accessor.ts +24 -3
  136. package/src/decide.ts +355 -0
  137. package/src/dispatch/operations-registry.ts +58 -0
  138. package/src/done-plan.ts +350 -0
  139. package/src/evidence-atom-schema.ts +31 -2
  140. package/src/exit-codes.ts +42 -0
  141. package/src/graph.ts +44 -0
  142. package/src/index.ts +117 -0
  143. package/src/memory/observe.ts +16 -0
  144. package/src/operations/__tests__/release-shared-surface.test.ts +112 -0
  145. package/src/operations/dialectic.ts +17 -0
  146. package/src/operations/memory.ts +48 -0
  147. package/src/operations/nexus-scope-map.ts +9 -0
  148. package/src/operations/nexus-user-profile.ts +29 -1
  149. package/src/operations/nexus.ts +416 -3
  150. package/src/operations/release.ts +197 -0
  151. package/src/operations/session.ts +6 -4
  152. package/src/operations/tasks.ts +7 -0
  153. package/src/operations/validate.ts +5 -0
  154. package/src/operations/worktree.ts +133 -1
  155. package/src/process-probe.ts +21 -0
  156. package/src/project-context.ts +42 -2
  157. package/src/project-lifecycle.ts +149 -0
  158. package/src/release/plan.test.ts +6 -1
  159. package/src/release/plan.ts +8 -2
  160. package/src/table-classification.ts +232 -0
  161. package/src/task.ts +84 -1
@@ -0,0 +1,350 @@
1
+ /**
2
+ * Contracts for the read-only `cleo done --plan` evidence planner.
3
+ *
4
+ * The planner derives, without executing a tool or writing a row, the
5
+ * evidence `cleo done` would record for each required verification gate:
6
+ * the implemented change set and where it came from, the tool runs and
7
+ * whether each already has a fresh cached result, which acceptance criteria
8
+ * map to evidence deterministically, and the blockers in the order an agent
9
+ * must clear them.
10
+ *
11
+ * Spec: `verify-streamlined-design` §3.2 and §7 step 1-2.
12
+ *
13
+ * @task T12623
14
+ * @task T12624
15
+ */
16
+
17
+ import type { VerificationGate } from './task.js';
18
+
19
+ /**
20
+ * Where a task's implemented change set was found, first match wins.
21
+ *
22
+ * - `pr` — a merged PR that cites the task; files read from its merge commit.
23
+ * - `branch` — the unmerged task branch diffed against its merge-base with
24
+ * origin's default branch.
25
+ * - `docs` — attached documents and linked decisions of a research, spike or
26
+ * documentation task.
27
+ * - `none` — nothing references the task yet.
28
+ */
29
+ export type ChangeSetSource = 'pr' | 'branch' | 'docs' | 'none';
30
+
31
+ /**
32
+ * How the repository the change set was read from was chosen.
33
+ *
34
+ * - `declared` — `CLEO_EVIDENCE_GIT_ROOT`, `GIT_WORK_TREE` or
35
+ * `evidence.gitRoot`; outranks every inference.
36
+ * - `invocation-worktree` — the caller's own worktree of this project.
37
+ * - `task-worktree` — the task's registered worktree, used when invoked from
38
+ * the main checkout.
39
+ * - `store` — the CLEO store root or its single child checkout.
40
+ */
41
+ export type ChangeSetRootSource = 'declared' | 'invocation-worktree' | 'task-worktree' | 'store';
42
+
43
+ /** One concrete, runnable next step. */
44
+ export interface DoneNextStep {
45
+ /** Shell command the agent runs next. */
46
+ command: string;
47
+ /** Why this step clears the blocker. */
48
+ why: string;
49
+ }
50
+
51
+ /**
52
+ * Blocker vocabulary, in the order the planner reports them.
53
+ *
54
+ * @see DONE_PLAN_BLOCKER_ORDER in core for the ranking.
55
+ */
56
+ export type DonePlanBlockerCode =
57
+ | 'git-root'
58
+ | 'dirty-tree'
59
+ | 'no-change-set'
60
+ | 'pr-ambiguous'
61
+ | 'pr-unverified'
62
+ | 'pr-stacked'
63
+ | 'pr-reverted'
64
+ | 'merge-commit-missing'
65
+ | 'decision-missing'
66
+ | 'tool-unresolved'
67
+ | 'tool-failed'
68
+ | 'typed-gate-failed'
69
+ | 'ac-mapping-needed'
70
+ | 'manual-gate'
71
+ | 'epic-rollup'
72
+ | 'run-from-worktree'
73
+ | 'checkout-required'
74
+ | 'evidence-refused'
75
+ | 'completion-refused';
76
+
77
+ /** A reason `cleo done` would stop, with exactly one next step. */
78
+ export interface DonePlanBlocker {
79
+ /** Stable machine code. */
80
+ code: DonePlanBlockerCode;
81
+ /** Human-readable statement of what is missing. */
82
+ message: string;
83
+ /** The one next step that clears it. */
84
+ next: DoneNextStep;
85
+ /** Original evidence error code when the blocker wraps a validator refusal. */
86
+ cause?: string;
87
+ }
88
+
89
+ /** A merged PR that cites the task. */
90
+ export interface ChangeSetPrCandidate {
91
+ /** PR number. */
92
+ prNumber: number;
93
+ /** PR title. */
94
+ title: string;
95
+ /** Head branch the PR merged from. */
96
+ headRefName: string;
97
+ }
98
+
99
+ /** A document attached to a research, spike or documentation task. */
100
+ export interface ChangeSetDoc {
101
+ /** Attachment or blob identifier. */
102
+ id: string;
103
+ /** Stable slug, when the doc has one. */
104
+ slug: string | null;
105
+ /** Store-relative path of the bytes the `files:` atom will hash. */
106
+ path: string;
107
+ }
108
+
109
+ /**
110
+ * The implemented change set derived for one task.
111
+ *
112
+ * `implementedEvidence` is the atom string the existing ADR-051 validators
113
+ * receive unchanged; there is no second validation path.
114
+ */
115
+ export interface TaskChangeSet {
116
+ /** Where the change set came from. */
117
+ source: ChangeSetSource;
118
+ /** Absolute path of the repository every git and gh call ran in. */
119
+ executionRoot: string;
120
+ /** Why {@link executionRoot} was chosen. */
121
+ rootSource: ChangeSetRootSource;
122
+ /** Merged PR number (`source === 'pr'`). */
123
+ prNumber?: number;
124
+ /**
125
+ * Verified merge commit the files were read from (`source === 'pr'`). For a
126
+ * stacked PR this is the BASE PR's merge into the default branch.
127
+ */
128
+ mergeCommitSha?: string;
129
+ /**
130
+ * Set when the task's PR merged into another branch (a stacked PR) and the
131
+ * task is attributed to that branch's own PR once it reached the default
132
+ * branch.
133
+ */
134
+ stackedOn?: { baseRef: string; basePrNumber?: number };
135
+ /** Branch tip commit (`source === 'branch'`). */
136
+ commitSha?: string;
137
+ /** Ref that was diffed (`HEAD` or `task/<id>`), for `source === 'branch'`. */
138
+ headRef?: string;
139
+ /** Origin default branch the merge-base was taken against. */
140
+ baseRef?: string;
141
+ /** Merge-base commit between {@link baseRef} and {@link headRef}. */
142
+ mergeBase?: string;
143
+ /** Repo-relative paths that still exist in the change set (the `files:` atom). */
144
+ files: string[];
145
+ /** Repo-relative paths the change set deleted; listed in the receipt, never in `files:`. */
146
+ deletedFiles: string[];
147
+ /** Attached docs (`source === 'docs'`). */
148
+ docs: ChangeSetDoc[];
149
+ /** Decision IDs linked to the task (`source === 'docs'`). */
150
+ decisions: string[];
151
+ /** Every merged PR that cited the task, before disambiguation. */
152
+ candidates: ChangeSetPrCandidate[];
153
+ /**
154
+ * D11151: the task's OTHER own-branch PRs when it shipped across several,
155
+ * oldest first. Each is recorded as its own `implemented` attempt before the
156
+ * primary (latest) PR, which is what the stored gate evidence ends up citing.
157
+ */
158
+ additionalPrs?: Array<{
159
+ prNumber: number;
160
+ mergeCommitSha?: string;
161
+ files: string[];
162
+ deletedFiles: string[];
163
+ implementedEvidence: string | null;
164
+ }>;
165
+ /** Planned `implemented` atoms (without `satisfies:`), or null when none derive. */
166
+ implementedEvidence: string | null;
167
+ /** Blockers found while deriving the change set. */
168
+ blockers: DonePlanBlocker[];
169
+ /** Non-blocking observations, e.g. PR discovery that could not reach `gh`. */
170
+ warnings: string[];
171
+ }
172
+
173
+ /** Planned state of one verification gate. */
174
+ export interface DonePlanGate {
175
+ /** Gate name. */
176
+ gate: VerificationGate;
177
+ /** Whether the project's verification policy requires it. */
178
+ required: boolean;
179
+ /** Whether the task already has the gate recorded as passed. */
180
+ passed: boolean;
181
+ /** Planned `--evidence` string (with `satisfies:`), or null when nothing is planned. */
182
+ evidence: string | null;
183
+ }
184
+
185
+ /**
186
+ * Cache state of a tool run the plan needs.
187
+ *
188
+ * - `fresh-pass` / `fresh-fail` — an ADR-061 cache entry exists for the
189
+ * current HEAD, dirty fingerprint and execution root.
190
+ * - `miss` — `cleo done` would run the tool.
191
+ * - `not-applicable` — the resolver says the tool does not apply here.
192
+ * - `unresolved` — no command resolves for the tool.
193
+ */
194
+ export type DonePlanToolCacheState =
195
+ | 'fresh-pass'
196
+ | 'fresh-fail'
197
+ | 'miss'
198
+ | 'not-applicable'
199
+ | 'unresolved';
200
+
201
+ /** One tool run a gate needs. The planner never executes it. */
202
+ export interface DonePlanToolRun {
203
+ /** Canonical tool name used in the `tool:` atom. */
204
+ tool: string;
205
+ /** Gate the result satisfies. */
206
+ gate: VerificationGate;
207
+ /** Resolved command line, or null when unresolved. */
208
+ command: string | null;
209
+ /** Resolver source (`project-context`, `language-default`, …). */
210
+ source: string | null;
211
+ /** Cache state for the current tree. */
212
+ cache: DonePlanToolCacheState;
213
+ /** Cached exit code, when an entry exists. */
214
+ exitCode?: number;
215
+ /** When the cached result was captured. */
216
+ capturedAt?: string;
217
+ /** Resolver refusal reason, when unresolved or not applicable. */
218
+ reason?: string;
219
+ }
220
+
221
+ /** Stored result of a typed acceptance gate. The planner never runs it. */
222
+ export interface DonePlanTypedGate {
223
+ /** Criterion alias (`AC<n>`). */
224
+ alias: string;
225
+ /** Typed gate kind. */
226
+ kind: string;
227
+ /** Requirement id, when the gate declares one. */
228
+ req?: string;
229
+ /** Latest stored result; `not-run` when none is stored. */
230
+ status: 'pass' | 'fail' | 'not-run';
231
+ }
232
+
233
+ /**
234
+ * Why an acceptance criterion is (or is not) linked automatically.
235
+ *
236
+ * - `typed-gate` — the criterion is a typed gate with a stored passing result.
237
+ * - `files-in-diff` — every repo path the criterion names is in the change set.
238
+ * - `agent` — the agent named it with `--satisfies`.
239
+ * - `recorded` — already linked by gate evidence the task has recorded.
240
+ * - `none` — no deterministic coverage; the agent must answer.
241
+ */
242
+ export type DonePlanAcBasis = 'typed-gate' | 'files-in-diff' | 'agent' | 'recorded' | 'none';
243
+
244
+ /** Mapping of one acceptance criterion to gate evidence. */
245
+ export interface DonePlanAcMapping {
246
+ /** Criterion alias (`AC<n>`). */
247
+ alias: string;
248
+ /** Canonical criterion UUID. */
249
+ id: string;
250
+ /** Criterion text. */
251
+ text: string;
252
+ /** Whether the criterion is linked, by any {@link DonePlanAcBasis} other than `none`. */
253
+ mapped: boolean;
254
+ /** Gates whose planned evidence carries `satisfies:` for this criterion. */
255
+ gates: VerificationGate[];
256
+ /** Why it is or is not linked. */
257
+ basis: DonePlanAcBasis;
258
+ /** Repo paths the criterion names (for `files-in-diff` and its misses). */
259
+ files?: string[];
260
+ }
261
+
262
+ /**
263
+ * Result of `deriveTaskEvidence` / `cleo done <id> --plan`.
264
+ *
265
+ * Read-only by construction: the planner records nothing and executes no tool
266
+ * or typed gate. `commands` are the calls `cleo done` would issue, spelled as
267
+ * the existing verbs so every one is runnable today.
268
+ */
269
+ export interface DonePlan {
270
+ /** Task the plan is for. */
271
+ taskId: string;
272
+ /** Directory the commands must run from (the change set's execution root). */
273
+ runFrom: string;
274
+ /** The derived change set. */
275
+ changeSet: TaskChangeSet;
276
+ /** Every evidence gate the plan covers, required or already passed. */
277
+ gates: DonePlanGate[];
278
+ /** Tool runs the required gates need. */
279
+ toolRuns: DonePlanToolRun[];
280
+ /** Typed acceptance gates and their stored results. */
281
+ typedGates: DonePlanTypedGate[];
282
+ /** Per-criterion mapping. */
283
+ acMapping: DonePlanAcMapping[];
284
+ /** Aliases of criteria that need `--satisfies` from the agent. */
285
+ needsSatisfies: string[];
286
+ /** Blockers, ordered: the first is the one to clear first. */
287
+ blockers: DonePlanBlocker[];
288
+ /**
289
+ * D11151: `implemented` evidence (with its criterion links) for each of the
290
+ * task's earlier own-branch PRs, recorded as separate attempts before the
291
+ * primary gate write.
292
+ */
293
+ additionalImplemented?: string[];
294
+ /** Runnable commands, in order, that record the gates and complete the task. */
295
+ commands: string[];
296
+ /** True when no blocker remains and `commands` would succeed as planned. */
297
+ ready: boolean;
298
+ /** The single next step: the first blocker's, or the first command. */
299
+ next: DoneNextStep | null;
300
+ }
301
+
302
+ /** One tool `cleo done` ran (or reused from the ADR-061 cache) before recording. */
303
+ export interface DoneToolResult {
304
+ /** Canonical tool name. */
305
+ tool: string;
306
+ /** Gate the result satisfies. */
307
+ gate: VerificationGate;
308
+ /** Exit code of the run. */
309
+ exitCode: number | null;
310
+ /** Whether the result came from the ADR-061 cache. */
311
+ cacheHit: boolean;
312
+ /** Wall-clock duration of the run (0 for a cache hit). */
313
+ durationMs: number;
314
+ }
315
+
316
+ /**
317
+ * Successful `cleo done` / `cleo verify --auto` write: every required gate the
318
+ * task lacked, recorded in one transaction through the existing validators.
319
+ * Completion (for `cleo done`) is reported by the caller alongside it.
320
+ */
321
+ export interface DoneRecordResult {
322
+ /** Task the gates were recorded for. */
323
+ taskId: string;
324
+ /** Gates this call recorded, in gate order. */
325
+ recordedGates: VerificationGate[];
326
+ /** Required gates that were already passed and left untouched. */
327
+ alreadyPassed: VerificationGate[];
328
+ /** Tool runs performed or reused before the write. */
329
+ toolResults: DoneToolResult[];
330
+ /** Typed acceptance gates executed before the write (0 when the task has none). */
331
+ typedGateCount: number;
332
+ /** Whether all required gates are now passed. */
333
+ verificationPassed: boolean;
334
+ /** The plan the write was derived from. */
335
+ plan: DonePlan;
336
+ }
337
+
338
+ /** Details of an `E_DONE_BLOCKED` failure: one blocker, one next step. */
339
+ export interface DoneBlockedDetails {
340
+ /** The blocker that stopped `cleo done`. */
341
+ blocker: DonePlanBlockerCode;
342
+ /** Original error code (e.g. `E_EVIDENCE_TOOL_FAILED`), when one exists. */
343
+ cause?: string;
344
+ /** The one next step. */
345
+ next: DoneNextStep;
346
+ /** Gates already recorded before a completion refusal (empty otherwise). */
347
+ recordedGates: VerificationGate[];
348
+ /** The plan, for tooling. */
349
+ plan: DonePlan;
350
+ }
@@ -62,6 +62,7 @@ export const EVIDENCE_ATOM_KINDS = [
62
62
  'note',
63
63
  'decision',
64
64
  'pr',
65
+ 'ci',
65
66
  'loc-drop',
66
67
  'callsite-coverage',
67
68
  'satisfies',
@@ -207,6 +208,21 @@ export const prAtomSchema = z.object({
207
208
  prNumber: z.number().int().positive('pr atom requires a positive integer PR number'),
208
209
  });
209
210
 
211
+ /**
212
+ * `ci:<number>` atom — the required CI checks of a merged PR, judged on the
213
+ * PR's actual MERGE COMMIT (never its head). Satisfies `testsPassed` and
214
+ * `qaPassed` only when the project opts in with `evidence.ciSatisfies`
215
+ * (owner decision D11149).
216
+ *
217
+ * Format: `ci:<positive integer>` (e.g. `ci:357`).
218
+ *
219
+ * @task T12634
220
+ */
221
+ export const ciAtomSchema = z.object({
222
+ kind: z.literal('ci'),
223
+ prNumber: z.number().int().positive('ci atom requires a positive integer PR number'),
224
+ });
225
+
210
226
  /**
211
227
  * `loc-drop:<fromLines>:<toLines>` atom — proves that a migrated engine shed
212
228
  * lines. Required for the `implemented` gate when the task carries the
@@ -389,6 +405,7 @@ export const EvidenceAtomSchema = z.discriminatedUnion('kind', [
389
405
  noteAtomSchema,
390
406
  decisionAtomSchema,
391
407
  prAtomSchema,
408
+ ciAtomSchema,
392
409
  locDropAtomSchema,
393
410
  callsiteCoverageAtomSchema,
394
411
  satisfiesAtomSchema,
@@ -477,8 +494,8 @@ export const GATE_EVIDENCE_REQUIREMENTS: Readonly<
477
494
  ['pr', 'files'],
478
495
  ],
479
496
  },
480
- testsPassed: { oneOf: [['test-run'], ['tool']] },
481
- qaPassed: { oneOf: [['tool']] },
497
+ testsPassed: { oneOf: [['test-run'], ['tool'], ['ci']] },
498
+ qaPassed: { oneOf: [['tool'], ['ci']] },
482
499
  documented: { oneOf: [['files'], ['url']] },
483
500
  securityPassed: { oneOf: [['tool'], ['note']] },
484
501
  cleanupDone: { oneOf: [['note']] },
@@ -542,6 +559,7 @@ const ATOM_EXAMPLES: Readonly<Record<EvidenceAtomKind, string>> = Object.freeze(
542
559
  note: 'note:<short description>',
543
560
  decision: 'decision:D-arch-001',
544
561
  pr: 'pr:357',
562
+ ci: 'ci:357',
545
563
  'loc-drop': 'loc-drop:<fromLines>:<toLines>',
546
564
  'callsite-coverage': 'callsite-coverage:<symbolName>:<relativeSourcePath>',
547
565
  satisfies: 'satisfies:T1234#AC2',
@@ -833,6 +851,17 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
833
851
  atoms.push({ kind: 'pr', prNumber });
834
852
  break;
835
853
  }
854
+ case 'ci': {
855
+ const prNumber = Number(payload);
856
+ if (!Number.isInteger(prNumber) || prNumber <= 0) {
857
+ throw new EvidenceParseError(
858
+ `ci atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
859
+ 'Use format: ci:<number> e.g. ci:357',
860
+ );
861
+ }
862
+ atoms.push({ kind: 'ci', prNumber });
863
+ break;
864
+ }
836
865
  case 'loc-drop': {
837
866
  const firstColon = payload.indexOf(':');
838
867
  if (firstColon < 1 || firstColon === payload.length - 1) {
package/src/exit-codes.ts CHANGED
@@ -21,6 +21,13 @@ export enum ExitCode {
21
21
  VALIDATION_ERROR = 6,
22
22
  LOCK_TIMEOUT = 7,
23
23
  CONFIG_ERROR = 8,
24
+ /**
25
+ * The project this directory resolves to was relocated by
26
+ * `cleo project reroot`; a valid `.cleo-moved.json` tombstone names its new
27
+ * root. Refused instead of silently creating an empty store at the old root.
28
+ * LAFS code `E_PROJECT_MOVED`; details carry `movedTo`. @task T12558
29
+ */
30
+ PROJECT_MOVED = 9,
24
31
 
25
32
  // === HIERARCHY ERRORS (10-19) ===
26
33
  PARENT_NOT_FOUND = 10,
@@ -38,6 +45,29 @@ export enum ExitCode {
38
45
  CHECKSUM_MISMATCH = 20,
39
46
  CONCURRENT_MODIFICATION = 21,
40
47
  ID_COLLISION = 22,
48
+ /**
49
+ * Optimistic-concurrency version mismatch: the caller's expected task
50
+ * version (`updatedAt`) no longer matches the stored row. LAFS code
51
+ * `E_CONFLICT`; the error details carry the current version. @task T12503
52
+ */
53
+ VERSION_CONFLICT = 23,
54
+ /**
55
+ * A session MUTATION (end, decision, suspend-on-switch) was asked to act on
56
+ * "the caller's session" but the caller has no bound identity: no connection
57
+ * handle, no `CLEO_SESSION_ID` naming a real row, and no terminal binding
58
+ * from `cleo session start` / `cleo session resume`. Guessing the newest
59
+ * active row would act on ANOTHER agent's session, so the operation refuses.
60
+ * LAFS code `E_SESSION_UNBOUND`. @task T12500
61
+ */
62
+ SESSION_UNBOUND = 24,
63
+ /**
64
+ * A second spawn asked for a task worktree whose per-task lock is held by a
65
+ * live holder (session, agent, pid + process start, device, heartbeat).
66
+ * Re-provisioning would force-remove the holder's live worktree, so the
67
+ * spawn refuses. LAFS code `E_WORKTREE_LOCKED`; the error details name the
68
+ * holder. @task T12506
69
+ */
70
+ WORKTREE_LOCKED = 25,
41
71
 
42
72
  // === SESSION ERRORS (30-39) ===
43
73
  SESSION_EXISTS = 30,
@@ -97,6 +127,18 @@ export enum ExitCode {
97
127
  CONTEXT_EMERGENCY = 53,
98
128
  CONTEXT_STALE = 54,
99
129
 
130
+ // === STORE MIGRATION (55-59) ===
131
+ /**
132
+ * A twin collapse (a bare legacy table folded into its prefixed twin in the
133
+ * project `cleo.db`) could not complete: the pre-migration snapshot could
134
+ * not be written (backup directory unusable, not enough free space) or the
135
+ * merge failed and was rolled back. Both tables are unchanged. LAFS code
136
+ * `E_TWIN_COLLAPSE_FAILED`; details carry the table, the cause, the
137
+ * snapshot path and the free space needed. `cleo doctor twin-collapse`
138
+ * reports it and `--retry` re-runs it. @task T12535
139
+ */
140
+ TWIN_COLLAPSE_FAILED = 55,
141
+
100
142
  // === ORCHESTRATOR ERRORS (60-68) ===
101
143
  PROTOCOL_MISSING = 60,
102
144
  INVALID_RETURN_MESSAGE = 61,
package/src/graph.ts CHANGED
@@ -683,6 +683,50 @@ export interface GraphIndexAssessment {
683
683
  files: GraphIndexFileReport[];
684
684
  }
685
685
 
686
+ /** Processing outcome of one assessed file. */
687
+ export type GraphIndexFileStatus = GraphIndexFileReport['status'];
688
+
689
+ /** Per-status file counts of an assessment; every status is present, zero included. */
690
+ export type GraphIndexFileStatusCounts = Record<GraphIndexFileStatus, number>;
691
+
692
+ /** One page of an assessment's per-file report, in stored order (T12560). */
693
+ export interface GraphIndexFilePage {
694
+ /** Rows skipped before this page. */
695
+ offset: number;
696
+ /** Requested page size; null when every remaining row was requested. */
697
+ limit: number | null;
698
+ /** Only rows with this status were paged; absent when unfiltered. */
699
+ status?: GraphIndexFileStatus;
700
+ /** Rows matching the filter, before paging. */
701
+ total: number;
702
+ /** Rows in this page. */
703
+ returned: number;
704
+ /** Offset of the next page; null when this page reaches the end. */
705
+ nextOffset: number | null;
706
+ /** The page itself. */
707
+ rows: GraphIndexFileReport[];
708
+ }
709
+
710
+ /**
711
+ * Bounded status projection of a {@link GraphIndexAssessment} (T12560).
712
+ *
713
+ * The per-file list grows with the repository (~635 B per file), so status
714
+ * reports its counts and one page. `files` is present only when every row was
715
+ * requested; otherwise `_withheld.files` carries the full list's UTF-8 JSON size.
716
+ */
717
+ export interface GraphIndexAssessmentProjection extends Omit<GraphIndexAssessment, 'files'> {
718
+ /** Omitted source fields and their UTF-8 serialized sizes; absent when complete. */
719
+ _withheld?: Record<string, number>;
720
+ /** Every file row; present only when the complete list was requested. */
721
+ files?: GraphIndexFileReport[];
722
+ /** Number of assessed files, including excluded ones. */
723
+ fileCount: number;
724
+ /** Assessed files per processing outcome. */
725
+ filesByStatus: GraphIndexFileStatusCounts;
726
+ /** The requested page of file rows; absent when `files` is complete. */
727
+ filesPage?: GraphIndexFilePage;
728
+ }
729
+
686
730
  /** Validated rows staged before an atomic graph publication. */
687
731
  export interface GraphPublicationRows {
688
732
  /** Immutable publication identity shared by anonymous symbols, rows and assessment. */