@cleocode/contracts 2026.9.21 → 2026.9.22
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.
- package/dist/__tests__/evidence-atom-schema.test.js +17 -0
- package/dist/__tests__/evidence-atom-schema.test.js.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +1 -0
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/done-plan.d.ts +19 -0
- package/dist/done-plan.d.ts.map +1 -1
- package/dist/evidence-atom-schema.d.ts +12 -44
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +29 -14
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/operations/docs.d.ts +5 -2
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js.map +1 -1
- package/dist/task.d.ts +18 -1
- package/dist/task.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/__tests__/evidence-atom-schema.test.ts +22 -0
- package/src/dispatch/operations-registry.ts +1 -0
- package/src/done-plan.ts +20 -0
- package/src/evidence-atom-schema.ts +31 -18
- package/src/index.ts +1 -0
- package/src/operations/docs.ts +5 -2
- package/src/task.ts +18 -1
|
@@ -499,6 +499,28 @@ describe('parseEvidenceString (T10337)', () => {
|
|
|
499
499
|
|
|
500
500
|
// ─── GATE_EVIDENCE_REQUIREMENTS — gate-to-atom mapping ─────────────────────
|
|
501
501
|
|
|
502
|
+
describe('pr:/ci: component references (T12671)', () => {
|
|
503
|
+
it('pr:<component>@<integration> names the integration PR and its component', () => {
|
|
504
|
+
expect(parseEvidenceString('pr:42@41')).toEqual([
|
|
505
|
+
{ kind: 'pr', prNumber: 41, componentPrNumber: 42 },
|
|
506
|
+
]);
|
|
507
|
+
expect(parseEvidenceString('ci:42@41')).toEqual([
|
|
508
|
+
{ kind: 'ci', prNumber: 41, componentPrNumber: 42 },
|
|
509
|
+
]);
|
|
510
|
+
expect(parseEvidenceString('pr:41')).toEqual([{ kind: 'pr', prNumber: 41 }]);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it.each(['pr:42@42', 'pr:42@', 'pr:@41', 'ci:0@41', 'pr:42@4x'])('rejects %s', (raw) => {
|
|
514
|
+
expect(() => parseEvidenceString(raw)).toThrow(EvidenceParseError);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it('the schema accepts the component field', () => {
|
|
518
|
+
expect(
|
|
519
|
+
EvidenceAtomSchema.safeParse({ kind: 'ci', prNumber: 41, componentPrNumber: 42 }).success,
|
|
520
|
+
).toBe(true);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
502
524
|
describe('GATE_EVIDENCE_REQUIREMENTS (T10337)', () => {
|
|
503
525
|
it('covers every VerificationGate', () => {
|
|
504
526
|
const expected: VerificationGate[] = [
|
|
@@ -8456,6 +8456,7 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
8456
8456
|
enum: DOCS_LIFECYCLE_STATUSES,
|
|
8457
8457
|
description:
|
|
8458
8458
|
'Override the new lifecycle status. Defaults to "draft" on every update. ' +
|
|
8459
|
+
'Alone (no file or content) it changes only the lifecycle status. ' +
|
|
8459
8460
|
`Valid: ${DOCS_LIFECYCLE_STATUSES.join('|')}.`,
|
|
8460
8461
|
},
|
|
8461
8462
|
{
|
package/src/done-plan.ts
CHANGED
|
@@ -119,6 +119,11 @@ export interface TaskChangeSet {
|
|
|
119
119
|
executionRoot: string;
|
|
120
120
|
/** Why {@link executionRoot} was chosen. */
|
|
121
121
|
rootSource: ChangeSetRootSource;
|
|
122
|
+
/**
|
|
123
|
+
* Component PR whose change the (integration) `prNumber` landed on the
|
|
124
|
+
* default branch (T12671). Evidence is then `pr:<component>@<prNumber>`.
|
|
125
|
+
*/
|
|
126
|
+
componentPrNumber?: number;
|
|
122
127
|
/** Merged PR number (`source === 'pr'`). */
|
|
123
128
|
prNumber?: number;
|
|
124
129
|
/**
|
|
@@ -168,8 +173,23 @@ export interface TaskChangeSet {
|
|
|
168
173
|
blockers: DonePlanBlocker[];
|
|
169
174
|
/** Non-blocking observations, e.g. PR discovery that could not reach `gh`. */
|
|
170
175
|
warnings: string[];
|
|
176
|
+
/**
|
|
177
|
+
* The merged-PR lookup failed (e.g. `gh` unavailable), so a non-PR change set
|
|
178
|
+
* does not prove the change is unmerged (T12656).
|
|
179
|
+
*/
|
|
180
|
+
prDiscoveryFailed?: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Whether the task's PR has merged to the default branch, as the PR lookup
|
|
183
|
+
* found it — independent of `source` (T12656 review): a merged PR whose
|
|
184
|
+
* `pr:` check was refused still falls back to the task branch, and the
|
|
185
|
+
* branch outlives a squash merge. Absent when no PR was examined.
|
|
186
|
+
*/
|
|
187
|
+
mergeState?: ChangeSetMergeState;
|
|
171
188
|
}
|
|
172
189
|
|
|
190
|
+
/** Whether a task's change has merged to the default branch. */
|
|
191
|
+
export type ChangeSetMergeState = 'merged' | 'unmerged' | 'unknown';
|
|
192
|
+
|
|
173
193
|
/** Planned state of one verification gate. */
|
|
174
194
|
export interface DonePlanGate {
|
|
175
195
|
/** Gate name. */
|
|
@@ -198,7 +198,11 @@ export const decisionAtomSchema = z.object({
|
|
|
198
198
|
* checks task relationship and changed artifacts. Implementation also needs
|
|
199
199
|
* inspected file evidence; testing and review require actual result atoms.
|
|
200
200
|
*
|
|
201
|
-
* Format: `pr:<positive integer>` (e.g. `pr:357`)
|
|
201
|
+
* Format: `pr:<positive integer>` (e.g. `pr:357`), or
|
|
202
|
+
* `pr:<component>@<integration>` (e.g. `pr:356@357`, T12671): component PR
|
|
203
|
+
* #356 merged into an integration branch that PR #357 landed on the default
|
|
204
|
+
* branch. The integration PR supplies the merge commit; the component PR
|
|
205
|
+
* supplies the task linkage and the changed files.
|
|
202
206
|
*
|
|
203
207
|
* @task T9764
|
|
204
208
|
* @task T9838
|
|
@@ -206,6 +210,7 @@ export const decisionAtomSchema = z.object({
|
|
|
206
210
|
export const prAtomSchema = z.object({
|
|
207
211
|
kind: z.literal('pr'),
|
|
208
212
|
prNumber: z.number().int().positive('pr atom requires a positive integer PR number'),
|
|
213
|
+
componentPrNumber: z.number().int().positive().optional(),
|
|
209
214
|
});
|
|
210
215
|
|
|
211
216
|
/**
|
|
@@ -214,13 +219,16 @@ export const prAtomSchema = z.object({
|
|
|
214
219
|
* `qaPassed` only when the project opts in with `evidence.ciSatisfies`
|
|
215
220
|
* (owner decision D11149).
|
|
216
221
|
*
|
|
217
|
-
* Format: `ci:<positive integer>` (e.g. `ci:357`)
|
|
222
|
+
* Format: `ci:<positive integer>` (e.g. `ci:357`), or
|
|
223
|
+
* `ci:<component>@<integration>` (e.g. `ci:356@357`, T12671): the integration
|
|
224
|
+
* PR's merge-commit CI, for a task linked through its component PR.
|
|
218
225
|
*
|
|
219
226
|
* @task T12634
|
|
220
227
|
*/
|
|
221
228
|
export const ciAtomSchema = z.object({
|
|
222
229
|
kind: z.literal('ci'),
|
|
223
230
|
prNumber: z.number().int().positive('ci atom requires a positive integer PR number'),
|
|
231
|
+
componentPrNumber: z.number().int().positive().optional(),
|
|
224
232
|
});
|
|
225
233
|
|
|
226
234
|
/**
|
|
@@ -771,6 +779,21 @@ export class EvidenceParseError extends Error {
|
|
|
771
779
|
* @task T10337
|
|
772
780
|
* @adr ADR-051
|
|
773
781
|
*/
|
|
782
|
+
/**
|
|
783
|
+
* `<n>` or `<component>@<integration>` (T12671), each a positive integer.
|
|
784
|
+
* The integration PR is the atom's `prNumber`.
|
|
785
|
+
*/
|
|
786
|
+
function parsePrReference(
|
|
787
|
+
payload: string,
|
|
788
|
+
): { prNumber: number; componentPrNumber?: number } | null {
|
|
789
|
+
const match = /^(\d+)(?:@(\d+))?$/.exec(payload);
|
|
790
|
+
if (!match) return null;
|
|
791
|
+
const first = Number(match[1]);
|
|
792
|
+
const second = match[2] === undefined ? null : Number(match[2]);
|
|
793
|
+
if (first <= 0 || (second !== null && (second <= 0 || second === first))) return null;
|
|
794
|
+
return second === null ? { prNumber: first } : { prNumber: second, componentPrNumber: first };
|
|
795
|
+
}
|
|
796
|
+
|
|
774
797
|
export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
775
798
|
if (!raw || typeof raw !== 'string') {
|
|
776
799
|
throw new EvidenceParseError(
|
|
@@ -840,26 +863,16 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
|
840
863
|
atoms.push({ kind: 'decision', decisionId: payload });
|
|
841
864
|
break;
|
|
842
865
|
}
|
|
843
|
-
case 'pr':
|
|
844
|
-
const prNumber = Number(payload);
|
|
845
|
-
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
846
|
-
throw new EvidenceParseError(
|
|
847
|
-
`pr atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
|
|
848
|
-
'Use format: pr:<number> e.g. pr:357',
|
|
849
|
-
);
|
|
850
|
-
}
|
|
851
|
-
atoms.push({ kind: 'pr', prNumber });
|
|
852
|
-
break;
|
|
853
|
-
}
|
|
866
|
+
case 'pr':
|
|
854
867
|
case 'ci': {
|
|
855
|
-
const
|
|
856
|
-
if (!
|
|
868
|
+
const ref = parsePrReference(payload);
|
|
869
|
+
if (!ref) {
|
|
857
870
|
throw new EvidenceParseError(
|
|
858
|
-
|
|
859
|
-
|
|
871
|
+
`${kind} atom requires a positive integer PR number, got "${payload}" in "${chunk}"`,
|
|
872
|
+
`Use format: ${kind}:<number> e.g. ${kind}:357, or ${kind}:<component>@<integration> e.g. ${kind}:356@357`,
|
|
860
873
|
);
|
|
861
874
|
}
|
|
862
|
-
atoms.push({ kind: 'ci',
|
|
875
|
+
atoms.push(kind === 'pr' ? { kind: 'pr', ...ref } : { kind: 'ci', ...ref });
|
|
863
876
|
break;
|
|
864
877
|
}
|
|
865
878
|
case 'loc-drop': {
|
package/src/index.ts
CHANGED
package/src/operations/docs.ts
CHANGED
|
@@ -715,7 +715,9 @@ export type DocsLifecycleStatus = (typeof DOCS_LIFECYCLE_STATUSES)[number];
|
|
|
715
715
|
* {@link DocsSupersedeParams}-style flows, NO supersession edge is created
|
|
716
716
|
* (callers wanting an explicit lineage edge should use `cleo docs supersede`).
|
|
717
717
|
*
|
|
718
|
-
*
|
|
718
|
+
* At most one of `file` or `content` may be provided. With neither, `status`
|
|
719
|
+
* is required and the update changes only the lifecycle status, keeping the
|
|
720
|
+
* stored bytes (T12654).
|
|
719
721
|
*
|
|
720
722
|
* @task T10161 (Epic T10157 / Saga T9855 — E12.C4)
|
|
721
723
|
*/
|
|
@@ -738,7 +740,8 @@ export interface DocsUpdateParams {
|
|
|
738
740
|
/**
|
|
739
741
|
* Override the new lifecycle status. Defaults to `'draft'` on every update
|
|
740
742
|
* so an explicit `accepted` doc gets back-pressured to draft on edit. Pass
|
|
741
|
-
* `--status accepted` (or any other valid status) to override.
|
|
743
|
+
* `--status accepted` (or any other valid status) to override. Passed alone
|
|
744
|
+
* (no `file`/`content`), it changes only the lifecycle status.
|
|
742
745
|
*/
|
|
743
746
|
status?: DocsLifecycleStatus;
|
|
744
747
|
/** Validate/preflight only; do not mutate attachment rows, blob storage, or audit logs. */
|
package/src/task.ts
CHANGED
|
@@ -170,6 +170,12 @@ export interface EvidenceValidationContext {
|
|
|
170
170
|
criteria: ReadonlyArray<Pick<AcRow, 'id' | 'text' | 'updatedAt'>>;
|
|
171
171
|
/** Verified PR merge whose immutable artifact bytes must be inspected. */
|
|
172
172
|
artifactCommitSha?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Validate without persisting anything: `pr:`/`ci:` lookups write neither
|
|
175
|
+
* the PR-result cache nor the branch-protection cache (T12671 review — the
|
|
176
|
+
* `cleo done --plan` preview must leave `.cleo/` untouched).
|
|
177
|
+
*/
|
|
178
|
+
readOnly?: boolean;
|
|
173
179
|
}
|
|
174
180
|
|
|
175
181
|
/** Explicit criterion link to inspected artifacts and validated results in one gate receipt. */
|
|
@@ -371,10 +377,19 @@ export type EvidenceAtom =
|
|
|
371
377
|
successCount: number;
|
|
372
378
|
/** Total number of checks evaluated in the rollup. */
|
|
373
379
|
totalChecks: number;
|
|
374
|
-
/**
|
|
380
|
+
/**
|
|
381
|
+
* Changed-file inventory retained for task-scope checks and display. With
|
|
382
|
+
* `componentPrNumber`, the component PR's files that survive in this
|
|
383
|
+
* PR's merge commit — never the whole integration PR.
|
|
384
|
+
*/
|
|
375
385
|
changedPaths?: string[];
|
|
376
386
|
/** Explicit task relationship established when this atom was accepted. */
|
|
377
387
|
taskId?: string;
|
|
388
|
+
/**
|
|
389
|
+
* Component PR this (integration) PR landed on the default branch
|
|
390
|
+
* (T12671): the task is linked through it and its files are the change.
|
|
391
|
+
*/
|
|
392
|
+
componentPrNumber?: number;
|
|
378
393
|
}
|
|
379
394
|
| {
|
|
380
395
|
/**
|
|
@@ -410,6 +425,8 @@ export type EvidenceAtom =
|
|
|
410
425
|
testedTree?: string;
|
|
411
426
|
/** Task the PR was verified to be linked to (T12634). */
|
|
412
427
|
taskId?: string;
|
|
428
|
+
/** Component PR the task is linked through; its CI ran as this integration PR's (T12671). */
|
|
429
|
+
componentPrNumber?: number;
|
|
413
430
|
/** Which configured checks attest each gate (`evidence.ciChecks`). */
|
|
414
431
|
gateChecks?: { testsPassed?: string[]; qaPassed?: string[] };
|
|
415
432
|
/** Where the required-check list came from (env, project context, branch protection). */
|