@cleocode/contracts 2026.10.27 → 2026.11.1-canary.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/.source-stamp.json +4 -0
- package/dist/__tests__/acceptance-gate.test.js +15 -3
- package/dist/__tests__/acceptance-gate.test.js.map +1 -1
- package/dist/__tests__/evidence-atom-schema.test.js +3 -1
- package/dist/__tests__/evidence-atom-schema.test.js.map +1 -1
- package/dist/__tests__/memory-wire-shapes.test.js.map +1 -1
- package/dist/acceptance-gate-schema.d.ts +25 -0
- package/dist/acceptance-gate-schema.d.ts.map +1 -1
- package/dist/acceptance-gate-schema.js +5 -0
- package/dist/acceptance-gate-schema.js.map +1 -1
- package/dist/acceptance-gate.d.ts +8 -0
- package/dist/acceptance-gate.d.ts.map +1 -1
- package/dist/data-accessor.d.ts +7 -0
- package/dist/data-accessor.d.ts.map +1 -1
- package/dist/data-accessor.js.map +1 -1
- package/dist/dependency.d.ts +2 -0
- package/dist/dependency.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +216 -0
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/docs/modernize.d.ts +435 -0
- package/dist/docs/modernize.d.ts.map +1 -0
- package/dist/docs/modernize.js +137 -0
- package/dist/docs/modernize.js.map +1 -0
- package/dist/docs-taxonomy.d.ts +6 -0
- package/dist/docs-taxonomy.d.ts.map +1 -1
- package/dist/docs-taxonomy.js +38 -1
- package/dist/docs-taxonomy.js.map +1 -1
- package/dist/done-plan.d.ts +27 -2
- package/dist/done-plan.d.ts.map +1 -1
- package/dist/evidence-atom-schema.d.ts +78 -4
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +136 -11
- package/dist/evidence-atom-schema.js.map +1 -1
- package/dist/exit-codes.d.ts +41 -0
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +41 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/invariants/sync-write-invariants.d.ts.map +1 -1
- package/dist/invariants/sync-write-invariants.js +15 -0
- package/dist/invariants/sync-write-invariants.js.map +1 -1
- package/dist/memory/search.d.ts +13 -0
- package/dist/memory/search.d.ts.map +1 -1
- package/dist/merge-close.d.ts +167 -0
- package/dist/merge-close.d.ts.map +1 -0
- package/dist/merge-close.js +16 -0
- package/dist/merge-close.js.map +1 -0
- package/dist/operations/admin.d.ts +6 -0
- package/dist/operations/admin.d.ts.map +1 -1
- package/dist/operations/docs.d.ts +192 -3
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js.map +1 -1
- package/dist/operations/worktree.d.ts +63 -0
- package/dist/operations/worktree.d.ts.map +1 -1
- package/dist/project-context.d.ts +31 -0
- package/dist/project-context.d.ts.map +1 -1
- package/dist/release/authorization.d.ts +46 -0
- package/dist/release/authorization.d.ts.map +1 -0
- package/dist/release/authorization.js +10 -0
- package/dist/release/authorization.js.map +1 -0
- package/dist/review-verdict.d.ts +125 -0
- package/dist/review-verdict.d.ts.map +1 -0
- package/dist/review-verdict.js +14 -0
- package/dist/review-verdict.js.map +1 -0
- package/dist/session-journal.d.ts.map +1 -1
- package/dist/session-journal.js +7 -1
- package/dist/session-journal.js.map +1 -1
- package/dist/task-record.d.ts +6 -1
- package/dist/task-record.d.ts.map +1 -1
- package/dist/task.d.ts +130 -3
- package/dist/task.d.ts.map +1 -1
- package/package.json +2 -2
- package/schemas/acceptance-gate.schema.json +24 -0
- package/schemas/task.schema.json +24 -0
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/__tests__/acceptance-gate.test.ts +16 -3
- package/src/__tests__/evidence-atom-schema.test.ts +3 -1
- package/src/__tests__/memory-wire-shapes.test.ts +2 -0
- package/src/acceptance-gate-schema.ts +5 -0
- package/src/acceptance-gate.ts +9 -0
- package/src/data-accessor.ts +8 -0
- package/src/dependency.ts +2 -0
- package/src/dispatch/operations-registry.ts +228 -0
- package/src/docs/modernize.ts +407 -0
- package/src/docs-taxonomy.ts +44 -1
- package/src/done-plan.ts +27 -2
- package/src/evidence-atom-schema.ts +167 -14
- package/src/exit-codes.ts +41 -0
- package/src/index.ts +55 -0
- package/src/invariants/sync-write-invariants.ts +16 -0
- package/src/memory/search.ts +10 -0
- package/src/merge-close.ts +168 -0
- package/src/operations/admin.ts +6 -0
- package/src/operations/docs.ts +199 -4
- package/src/operations/worktree.ts +66 -0
- package/src/project-context.ts +33 -0
- package/src/release/authorization.ts +48 -0
- package/src/review-verdict.ts +122 -0
- package/src/session-journal.ts +7 -1
- package/src/task-record.ts +6 -1
- package/src/task.ts +124 -3
|
@@ -64,6 +64,8 @@ export const EVIDENCE_ATOM_KINDS = [
|
|
|
64
64
|
'decision',
|
|
65
65
|
'pr',
|
|
66
66
|
'ci',
|
|
67
|
+
'ci-run',
|
|
68
|
+
'deploy',
|
|
67
69
|
'loc-drop',
|
|
68
70
|
'callsite-coverage',
|
|
69
71
|
'satisfies',
|
|
@@ -81,6 +83,12 @@ const PARSER_PREFIXES = [...EVIDENCE_ATOM_KINDS, 'state'] as const;
|
|
|
81
83
|
*/
|
|
82
84
|
const KIND_BOUNDARY_REGEX = new RegExp(`;(?=\\s*(?:${PARSER_PREFIXES.join('|')})\\s*:)`, 'g');
|
|
83
85
|
|
|
86
|
+
/** A `deploy:` provider name (T14083): lowercase, e.g. `railway`, `fly`, `cloudflare-pages`. */
|
|
87
|
+
const DEPLOY_PROVIDER_REGEX = /^[a-z0-9][a-z0-9._-]*$/;
|
|
88
|
+
|
|
89
|
+
/** A `deploy:` deployment id (T14083): no whitespace, `@` or `|`. */
|
|
90
|
+
const DEPLOY_ID_REGEX = /^[^\s@|]+$/;
|
|
91
|
+
|
|
84
92
|
// ---------------------------------------------------------------------------
|
|
85
93
|
// Per-atom Zod schemas
|
|
86
94
|
// ---------------------------------------------------------------------------
|
|
@@ -262,6 +270,47 @@ export const ciAtomSchema = z.object({
|
|
|
262
270
|
.optional(),
|
|
263
271
|
});
|
|
264
272
|
|
|
273
|
+
/**
|
|
274
|
+
* `ci-run:<runId>` atom (T14149): a run of the `repeat-run.yml` workflow
|
|
275
|
+
* (dispatched on the default branch) that ran one test file N times, all green.
|
|
276
|
+
* Validated from GitHub's record of the run and its uploaded report, never from
|
|
277
|
+
* a file the agent supplies. It links an acceptance criterion that needs a heavy
|
|
278
|
+
* or repeated run; it does not satisfy `testsPassed` on its own.
|
|
279
|
+
*
|
|
280
|
+
* Format: `ci-run:<positive integer run id>` (e.g. `ci-run:40812345678`).
|
|
281
|
+
*
|
|
282
|
+
* @task T14149
|
|
283
|
+
*/
|
|
284
|
+
export const ciRunAtomSchema = z.object({
|
|
285
|
+
kind: z.literal('ci-run'),
|
|
286
|
+
runId: z.number().int().positive('ci-run atom requires a positive integer run id'),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* `deploy:<provider>/<deploymentId>@<sha>|<probe-url>` atom (T14083): a
|
|
291
|
+
* deployment of commit `<sha>`, proven at verify time by a bounded GET of
|
|
292
|
+
* `<probe-url>` that returns 2xx and names `<sha>` in its body or in an
|
|
293
|
+
* `X-Commit` / `X-Version` header. Satisfies only the gates the project lists
|
|
294
|
+
* in `evidence.deploySatisfies` (no default).
|
|
295
|
+
*
|
|
296
|
+
* Format: `deploy:railway/9f3c2a@1a2b3c4d|https://api.example.com/health`.
|
|
297
|
+
* The provider and deployment id are recorded as given; the probe, not the
|
|
298
|
+
* provider, is the proof.
|
|
299
|
+
*
|
|
300
|
+
* @task T14083
|
|
301
|
+
*/
|
|
302
|
+
export const deployAtomSchema = z.object({
|
|
303
|
+
kind: z.literal('deploy'),
|
|
304
|
+
provider: z
|
|
305
|
+
.string()
|
|
306
|
+
.regex(DEPLOY_PROVIDER_REGEX, 'deploy atom provider must be a lowercase name'),
|
|
307
|
+
deploymentId: z.string().regex(DEPLOY_ID_REGEX, 'deploy atom requires a deployment id'),
|
|
308
|
+
sha: z.string().regex(/^[0-9a-f]{7,40}$/, 'deploy atom sha must be 7-40 lowercase hex chars'),
|
|
309
|
+
probeUrl: z
|
|
310
|
+
.string()
|
|
311
|
+
.regex(/^https?:\/\/\S+$/, 'deploy atom probe url must start with http:// or https://'),
|
|
312
|
+
});
|
|
313
|
+
|
|
265
314
|
/**
|
|
266
315
|
* `loc-drop:<fromLines>:<toLines>` atom — proves that a migrated engine shed
|
|
267
316
|
* lines. Required for the `implemented` gate when the task carries the
|
|
@@ -428,6 +477,8 @@ export const satisfiesAtomSchema = z.object({
|
|
|
428
477
|
* - `note` — free-form note (T832)
|
|
429
478
|
* - `decision` — brain_decisions row reference (T1875)
|
|
430
479
|
* - `pr` — merged-PR retroactive proof (T9764 / T9838)
|
|
480
|
+
* - `ci-run` — a green repeat-run.yml run of one test file (T14149)
|
|
481
|
+
* - `deploy` — probed deployment of a commit (T14083)
|
|
431
482
|
* - `loc-drop` — engine-migration LOC reduction (T1604)
|
|
432
483
|
* - `callsite-coverage` — exported-symbol production callsite (T1605)
|
|
433
484
|
* - `satisfies` — cross-task AC binding (T10506 / ADR-079-r2;
|
|
@@ -447,6 +498,8 @@ export const EvidenceAtomSchema = z.discriminatedUnion('kind', [
|
|
|
447
498
|
decisionAtomSchema,
|
|
448
499
|
prAtomSchema,
|
|
449
500
|
ciAtomSchema,
|
|
501
|
+
ciRunAtomSchema,
|
|
502
|
+
deployAtomSchema,
|
|
450
503
|
locDropAtomSchema,
|
|
451
504
|
callsiteCoverageAtomSchema,
|
|
452
505
|
satisfiesAtomSchema,
|
|
@@ -546,10 +599,59 @@ export const GATE_EVIDENCE_REQUIREMENTS: Readonly<
|
|
|
546
599
|
nexusImpact: { oneOf: [['tool'], ['note']] },
|
|
547
600
|
});
|
|
548
601
|
|
|
602
|
+
/**
|
|
603
|
+
* The gate profiles a task with no code change may declare as a
|
|
604
|
+
* `profile:<name>` label (T13474): operations work, a decision, a census or
|
|
605
|
+
* an audit. The closed set lives here so the gate requirements below and the
|
|
606
|
+
* label rules in core read one list.
|
|
607
|
+
*
|
|
608
|
+
* @task T13474
|
|
609
|
+
* @task T14187
|
|
610
|
+
*/
|
|
611
|
+
export const GATE_PROFILE_NAMES = ['ops', 'decision', 'census', 'audit'] as const;
|
|
612
|
+
|
|
613
|
+
/** One of {@link GATE_PROFILE_NAMES}. */
|
|
614
|
+
export type GateProfileName = (typeof GATE_PROFILE_NAMES)[number];
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Atom combinations a gate accepts IN ADDITION to
|
|
618
|
+
* {@link GATE_EVIDENCE_REQUIREMENTS} for a task that declares a gate profile
|
|
619
|
+
* and whose change touches no code (T14187). An operations, census or audit
|
|
620
|
+
* task (a live-data repair, a measurement) has no commit, PR or decision to
|
|
621
|
+
* cite: its proof is the stored document recording what was done or found,
|
|
622
|
+
* so `implemented` also accepts `files` (that document) with a `note`. A
|
|
623
|
+
* decision task still proves itself with its decision. Core applies this
|
|
624
|
+
* only when every `files:` path is a document, so a code change still owes a
|
|
625
|
+
* commit or PR. `cleo done --plan` and the gates read this one table.
|
|
626
|
+
*
|
|
627
|
+
* @task T14187
|
|
628
|
+
*/
|
|
629
|
+
export const PROFILE_GATE_EVIDENCE: Readonly<
|
|
630
|
+
Record<
|
|
631
|
+
GateProfileName,
|
|
632
|
+
Partial<Record<VerificationGate, ReadonlyArray<ReadonlyArray<EvidenceAtomKind>>>>
|
|
633
|
+
>
|
|
634
|
+
> = Object.freeze({
|
|
635
|
+
ops: { implemented: [['files', 'note']] },
|
|
636
|
+
decision: {},
|
|
637
|
+
census: { implemented: [['files', 'note']] },
|
|
638
|
+
audit: { implemented: [['files', 'note']] },
|
|
639
|
+
});
|
|
640
|
+
|
|
549
641
|
// ---------------------------------------------------------------------------
|
|
550
642
|
// Helpers
|
|
551
643
|
// ---------------------------------------------------------------------------
|
|
552
644
|
|
|
645
|
+
/** The combinations `gate` accepts, with a gate profile's additions. */
|
|
646
|
+
function gateCombinations(
|
|
647
|
+
gate: VerificationGate,
|
|
648
|
+
profile?: GateProfileName | null,
|
|
649
|
+
): ReadonlyArray<ReadonlyArray<EvidenceAtomKind>> {
|
|
650
|
+
const base = GATE_EVIDENCE_REQUIREMENTS[gate]?.oneOf ?? [];
|
|
651
|
+
const extra = profile ? (PROFILE_GATE_EVIDENCE[profile][gate] ?? []) : [];
|
|
652
|
+
return [...base, ...extra];
|
|
653
|
+
}
|
|
654
|
+
|
|
553
655
|
/**
|
|
554
656
|
* Result of {@link validateEvidenceForGate}.
|
|
555
657
|
*
|
|
@@ -582,9 +684,13 @@ export type EvidenceValidationResult = { readonly ok: true } | EvidenceValidatio
|
|
|
582
684
|
*
|
|
583
685
|
* @task T10337
|
|
584
686
|
*/
|
|
585
|
-
export function formatGateRequirement(
|
|
586
|
-
|
|
587
|
-
|
|
687
|
+
export function formatGateRequirement(
|
|
688
|
+
gate: VerificationGate,
|
|
689
|
+
profile?: GateProfileName | null,
|
|
690
|
+
): string {
|
|
691
|
+
return gateCombinations(gate, profile)
|
|
692
|
+
.map((set) => `[${set.join(' AND ')}]`)
|
|
693
|
+
.join(' OR ');
|
|
588
694
|
}
|
|
589
695
|
|
|
590
696
|
/**
|
|
@@ -605,6 +711,8 @@ const ATOM_EXAMPLES: Readonly<Record<EvidenceAtomKind, string>> = Object.freeze(
|
|
|
605
711
|
decision: 'decision:D-arch-001',
|
|
606
712
|
pr: 'pr:357',
|
|
607
713
|
ci: 'ci:357',
|
|
714
|
+
'ci-run': 'ci-run:<runId>',
|
|
715
|
+
deploy: 'deploy:<provider>/<deploymentId>@<sha>|https://<host>/health',
|
|
608
716
|
'loc-drop': 'loc-drop:<fromLines>:<toLines>',
|
|
609
717
|
'callsite-coverage': 'callsite-coverage:<symbolName>:<relativeSourcePath>',
|
|
610
718
|
satisfies: 'satisfies:T1234#AC2',
|
|
@@ -654,16 +762,20 @@ function renderCombinationAsEvidence(combination: ReadonlyArray<EvidenceAtomKind
|
|
|
654
762
|
* @task T9949
|
|
655
763
|
* @adr ADR-051 §2.3
|
|
656
764
|
*/
|
|
657
|
-
export function formatGateRequirementHint(
|
|
765
|
+
export function formatGateRequirementHint(
|
|
766
|
+
gate: VerificationGate,
|
|
767
|
+
profile?: GateProfileName | null,
|
|
768
|
+
): string {
|
|
658
769
|
const requirement = GATE_EVIDENCE_REQUIREMENTS[gate];
|
|
659
770
|
if (!requirement) {
|
|
660
771
|
// Unknown gate — match validateEvidenceForGate's defensive default.
|
|
661
772
|
return `Gate '${gate}' has no documented evidence requirements.`;
|
|
662
773
|
}
|
|
774
|
+
const oneOf = gateCombinations(gate, profile);
|
|
663
775
|
|
|
664
776
|
const lines: string[] = [
|
|
665
777
|
`Gate '${gate}' requires programmatic evidence. Use ONE of:`,
|
|
666
|
-
...
|
|
778
|
+
...oneOf.map(
|
|
667
779
|
(combo) =>
|
|
668
780
|
` - cleo verify T#### --gate ${gate} --evidence '${renderCombinationAsEvidence(combo)}'`,
|
|
669
781
|
),
|
|
@@ -671,16 +783,12 @@ export function formatGateRequirementHint(gate: VerificationGate): string {
|
|
|
671
783
|
|
|
672
784
|
// Note-specific clarification — T9949's headline pain point.
|
|
673
785
|
// The gate accepts note: alone IFF some single-element combination is ['note'].
|
|
674
|
-
const noteAloneAccepted =
|
|
675
|
-
(combo) => combo.length === 1 && combo[0] === 'note',
|
|
676
|
-
);
|
|
786
|
+
const noteAloneAccepted = oneOf.some((combo) => combo.length === 1 && combo[0] === 'note');
|
|
677
787
|
if (noteAloneAccepted) {
|
|
678
788
|
lines.push(`Note: 'note:' alone IS accepted for this gate (waiver-style evidence).`);
|
|
679
789
|
} else {
|
|
680
790
|
// Surface every alternative pairing involving note for at-a-glance routing.
|
|
681
|
-
const notePairings =
|
|
682
|
-
(combo) => combo.length > 1 && combo.includes('note'),
|
|
683
|
-
);
|
|
791
|
+
const notePairings = oneOf.filter((combo) => combo.length > 1 && combo.includes('note'));
|
|
684
792
|
if (notePairings.length > 0) {
|
|
685
793
|
const partners = Array.from(
|
|
686
794
|
new Set(notePairings.flatMap((combo) => combo.filter((k) => k !== 'note'))),
|
|
@@ -715,6 +823,8 @@ export function formatGateRequirementHint(gate: VerificationGate): string {
|
|
|
715
823
|
*
|
|
716
824
|
* @param gate - Verification gate being checked.
|
|
717
825
|
* @param atoms - Already-parsed evidence atoms.
|
|
826
|
+
* @param profile - The task's gate profile when its change touches no code
|
|
827
|
+
* ({@link PROFILE_GATE_EVIDENCE}, T14187); omit otherwise.
|
|
718
828
|
* @returns Pass / fail with a human-readable failure message.
|
|
719
829
|
*
|
|
720
830
|
* @example
|
|
@@ -731,6 +841,7 @@ export function formatGateRequirementHint(gate: VerificationGate): string {
|
|
|
731
841
|
export function validateEvidenceForGate(
|
|
732
842
|
gate: VerificationGate,
|
|
733
843
|
atoms: ReadonlyArray<{ kind: EvidenceAtomKind }>,
|
|
844
|
+
profile?: GateProfileName | null,
|
|
734
845
|
): EvidenceValidationResult {
|
|
735
846
|
const requirement = GATE_EVIDENCE_REQUIREMENTS[gate];
|
|
736
847
|
if (!requirement) {
|
|
@@ -738,18 +849,18 @@ export function validateEvidenceForGate(
|
|
|
738
849
|
// behavior from checkGateEvidenceMinimum when the gate had no entry).
|
|
739
850
|
return { ok: true };
|
|
740
851
|
}
|
|
741
|
-
for (const combination of
|
|
852
|
+
for (const combination of gateCombinations(gate, profile)) {
|
|
742
853
|
const satisfied = combination.every((kind) => atoms.some((a) => a.kind === kind));
|
|
743
854
|
if (satisfied) return { ok: true };
|
|
744
855
|
}
|
|
745
856
|
return {
|
|
746
857
|
ok: false,
|
|
747
|
-
message: `Gate '${gate}' requires evidence: ${formatGateRequirement(gate)}`,
|
|
858
|
+
message: `Gate '${gate}' requires evidence: ${formatGateRequirement(gate, profile)}`,
|
|
748
859
|
// T9949: rich, example-bearing remediation hint suitable for CLI `fix:`.
|
|
749
860
|
// Always populated alongside `message` so consumers can choose between
|
|
750
861
|
// the legacy single-line surface and the multi-line hint without re-
|
|
751
862
|
// computing the spec.
|
|
752
|
-
hint: formatGateRequirementHint(gate),
|
|
863
|
+
hint: formatGateRequirementHint(gate, profile),
|
|
753
864
|
};
|
|
754
865
|
}
|
|
755
866
|
|
|
@@ -835,6 +946,35 @@ function parsePrReference(
|
|
|
835
946
|
: { prNumber: second, componentPrNumber: first, ...repo };
|
|
836
947
|
}
|
|
837
948
|
|
|
949
|
+
/**
|
|
950
|
+
* Parse a `deploy:` payload, `<provider>/<deploymentId>@<sha>|<probe-url>`
|
|
951
|
+
* (T14083). The provider is lowercased, the SHA lowercased hex.
|
|
952
|
+
*/
|
|
953
|
+
function parseDeployPayload(payload: string, chunk: string): z.infer<typeof deployAtomSchema> {
|
|
954
|
+
const fix =
|
|
955
|
+
'Use format: deploy:<provider>/<deploymentId>@<sha>|<probe-url> e.g. ' +
|
|
956
|
+
'deploy:railway/9f3c2a@1a2b3c4d|https://api.example.com/health';
|
|
957
|
+
const bar = payload.indexOf('|');
|
|
958
|
+
const match = /^([^/]+)\/(.+)@([0-9A-Fa-f]{7,40})$/.exec(
|
|
959
|
+
bar < 0 ? '' : payload.slice(0, bar).trim(),
|
|
960
|
+
);
|
|
961
|
+
const probeUrl = bar < 0 ? '' : payload.slice(bar + 1).trim();
|
|
962
|
+
const parsed = deployAtomSchema.safeParse({
|
|
963
|
+
kind: 'deploy',
|
|
964
|
+
provider: match?.[1]?.toLowerCase() ?? '',
|
|
965
|
+
deploymentId: match?.[2] ?? '',
|
|
966
|
+
sha: match?.[3]?.toLowerCase() ?? '',
|
|
967
|
+
probeUrl,
|
|
968
|
+
});
|
|
969
|
+
if (!match || !parsed.success) {
|
|
970
|
+
throw new EvidenceParseError(
|
|
971
|
+
`Malformed deploy atom: "${chunk}" (${!match || parsed.success ? 'expected <provider>/<deploymentId>@<sha>|<probe-url>' : parsed.error.issues[0]?.message})`,
|
|
972
|
+
fix,
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
return parsed.data;
|
|
976
|
+
}
|
|
977
|
+
|
|
838
978
|
export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
839
979
|
if (!raw || typeof raw !== 'string') {
|
|
840
980
|
throw new EvidenceParseError(
|
|
@@ -919,6 +1059,19 @@ export function parseEvidenceString(raw: string): EvidenceAtom[] {
|
|
|
919
1059
|
atoms.push(kind === 'pr' ? { kind: 'pr', ...ref } : { kind: 'ci', ...ref });
|
|
920
1060
|
break;
|
|
921
1061
|
}
|
|
1062
|
+
case 'ci-run': {
|
|
1063
|
+
if (!/^[1-9][0-9]*$/.test(payload)) {
|
|
1064
|
+
throw new EvidenceParseError(
|
|
1065
|
+
`ci-run atom requires a positive integer run id, got "${payload}" in "${chunk}"`,
|
|
1066
|
+
'Use format: ci-run:<runId> e.g. ci-run:40812345678 (a repeat-run.yml run on main)',
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
atoms.push({ kind: 'ci-run', runId: Number(payload) });
|
|
1070
|
+
break;
|
|
1071
|
+
}
|
|
1072
|
+
case 'deploy':
|
|
1073
|
+
atoms.push(parseDeployPayload(payload, chunk));
|
|
1074
|
+
break;
|
|
922
1075
|
case 'loc-drop': {
|
|
923
1076
|
const firstColon = payload.indexOf(':');
|
|
924
1077
|
if (firstColon < 1 || firstColon === payload.length - 1) {
|
package/src/exit-codes.ts
CHANGED
|
@@ -392,6 +392,47 @@ export const E_EVIDENCE_INSUFFICIENT = 'E_EVIDENCE_INSUFFICIENT' as const;
|
|
|
392
392
|
export const E_PLAN_NOT_FOUND = 'E_PLAN_NOT_FOUND' as const;
|
|
393
393
|
/** Generic release-plan validation failure (e.g. schema). Exit code 6. */
|
|
394
394
|
export const E_RELEASE_PLAN_INVALID = 'E_RELEASE_PLAN_INVALID' as const;
|
|
395
|
+
/**
|
|
396
|
+
* `cleo release cut` found no recent default-branch commit whose required `CI`
|
|
397
|
+
* check run concluded `success` (or the pinned `--sha` is not green). Exit code 6.
|
|
398
|
+
* `error.details.candidates[]` lists each commit checked and its CI state.
|
|
399
|
+
*
|
|
400
|
+
* @task T14102
|
|
401
|
+
*/
|
|
402
|
+
export const E_RELEASE_NO_GREEN_COMMIT = 'E_RELEASE_NO_GREEN_COMMIT' as const;
|
|
403
|
+
/**
|
|
404
|
+
* `cleo release cut` found a changeset in range that no merged pull request
|
|
405
|
+
* brought in (pushed straight to the default branch). Exit code 6.
|
|
406
|
+
* `error.details.changesets[]` lists each file and the commit that added it.
|
|
407
|
+
*
|
|
408
|
+
* @task T14102
|
|
409
|
+
*/
|
|
410
|
+
export const E_RELEASE_UNMERGED_CHANGESET = 'E_RELEASE_UNMERGED_CHANGESET' as const;
|
|
411
|
+
/**
|
|
412
|
+
* `cleo release cut` could not create the tag (the tag object or its ref POST
|
|
413
|
+
* failed). Exit code 1. `error.details.tagObject` names a created but
|
|
414
|
+
* unreferenced tag object, which is harmless. The attempt is audited.
|
|
415
|
+
*
|
|
416
|
+
* @task T14102
|
|
417
|
+
*/
|
|
418
|
+
export const E_RELEASE_TAG_FAILED = 'E_RELEASE_TAG_FAILED' as const;
|
|
419
|
+
/**
|
|
420
|
+
* `cleo release cut --yes` from a non-interactive caller (an agent) without a
|
|
421
|
+
* standing release authorization (owner decision D11212; T14141 stores it).
|
|
422
|
+
* Exit code 6. A human at a terminal confirms with `--yes` directly.
|
|
423
|
+
*
|
|
424
|
+
* @task T14102
|
|
425
|
+
*/
|
|
426
|
+
export const E_RELEASE_NOT_AUTHORIZED = 'E_RELEASE_NOT_AUTHORIZED' as const;
|
|
427
|
+
/**
|
|
428
|
+
* `cleo release cut` found docs rot (stale, duplicate or orphaned docs, per
|
|
429
|
+
* `cleo docs rot`) and refused to tag. Exit code 6. `error.details.docsRot`
|
|
430
|
+
* carries the scan.
|
|
431
|
+
*
|
|
432
|
+
* @task T14102
|
|
433
|
+
* @task T14111
|
|
434
|
+
*/
|
|
435
|
+
export const E_DOCS_ROT = 'E_DOCS_ROT' as const;
|
|
395
436
|
/**
|
|
396
437
|
* `cleo release plan` (or the lint script) encountered a `.changeset/*.md`
|
|
397
438
|
* file whose YAML frontmatter cannot be parsed. Exit code 6 (VALIDATION_ERROR).
|
package/src/index.ts
CHANGED
|
@@ -422,6 +422,32 @@ export { CANONICAL_DOMAINS } from './dispatch/identity.js';
|
|
|
422
422
|
export type { OperationDef, Resolution } from './dispatch/operation-def.js';
|
|
423
423
|
// === Dispatch OPERATIONS data + builder helpers (T10061 — T9833b / E-CLI-BOUNDARY / SG-ARCH-SOLID) ===
|
|
424
424
|
export { defineDomain, defineOp, OPERATIONS } from './dispatch/operations-registry.js';
|
|
425
|
+
// === docs modernize plan + receipt (T14113) ===
|
|
426
|
+
export type {
|
|
427
|
+
DocsModernizeDisposition,
|
|
428
|
+
DocsModernizeJudgment,
|
|
429
|
+
DocsModernizeJudgmentQuestion,
|
|
430
|
+
DocsModernizeJudgmentSource,
|
|
431
|
+
DocsModernizeLinkFix,
|
|
432
|
+
DocsModernizeOpKind,
|
|
433
|
+
DocsModernizeOpPrior,
|
|
434
|
+
DocsModernizeOpState,
|
|
435
|
+
DocsModernizeOverride,
|
|
436
|
+
DocsModernizeOverrides,
|
|
437
|
+
DocsModernizePlan,
|
|
438
|
+
DocsModernizePlanRow,
|
|
439
|
+
DocsModernizeReceipt,
|
|
440
|
+
DocsModernizeReceiptOp,
|
|
441
|
+
DocsModernizeStoreRow,
|
|
442
|
+
} from './docs/modernize.js';
|
|
443
|
+
export {
|
|
444
|
+
DOCS_MODERNIZE_DISPOSITIONS,
|
|
445
|
+
DOCS_MODERNIZE_OPS,
|
|
446
|
+
docsModernizeJournalLineSchema,
|
|
447
|
+
docsModernizeOverridesSchema,
|
|
448
|
+
docsModernizePlanSchema,
|
|
449
|
+
docsModernizeReceiptSchema,
|
|
450
|
+
} from './docs/modernize.js';
|
|
425
451
|
// === Docs Provenance Graph (T10166 / Saga T9855 / E12) ===
|
|
426
452
|
export type {
|
|
427
453
|
DocLifecycleStatus,
|
|
@@ -613,6 +639,7 @@ export type {
|
|
|
613
639
|
EvidenceValidationFailure,
|
|
614
640
|
EvidenceValidationResult,
|
|
615
641
|
GateEvidenceRequirement,
|
|
642
|
+
GateProfileName,
|
|
616
643
|
} from './evidence-atom-schema.js';
|
|
617
644
|
export {
|
|
618
645
|
AC_ALIAS_REGEX,
|
|
@@ -627,8 +654,10 @@ export {
|
|
|
627
654
|
formatGateRequirement,
|
|
628
655
|
formatGateRequirementHint,
|
|
629
656
|
GATE_EVIDENCE_REQUIREMENTS,
|
|
657
|
+
GATE_PROFILE_NAMES,
|
|
630
658
|
locDropAtomSchema,
|
|
631
659
|
noteAtomSchema,
|
|
660
|
+
PROFILE_GATE_EVIDENCE,
|
|
632
661
|
parseEvidenceString,
|
|
633
662
|
prAtomSchema,
|
|
634
663
|
qaRunAtomSchema,
|
|
@@ -1222,6 +1251,17 @@ export type {
|
|
|
1222
1251
|
PatternRecord,
|
|
1223
1252
|
SessionSummary,
|
|
1224
1253
|
} from './memory.js';
|
|
1254
|
+
export type {
|
|
1255
|
+
MacosRegressionSync,
|
|
1256
|
+
MergeDirective,
|
|
1257
|
+
MergePrOutcome,
|
|
1258
|
+
MergeReceipt,
|
|
1259
|
+
MergeSweepHooks,
|
|
1260
|
+
MergeSweepOptions,
|
|
1261
|
+
MergeSweepReconcile,
|
|
1262
|
+
MergeSweepResult,
|
|
1263
|
+
MergeTaskOutcome,
|
|
1264
|
+
} from './merge-close.js';
|
|
1225
1265
|
export type {
|
|
1226
1266
|
MigrationEnumDrift,
|
|
1227
1267
|
MigrationForeignKeyViolation,
|
|
@@ -2094,6 +2134,9 @@ export type {
|
|
|
2094
2134
|
ListWorktreesOptions,
|
|
2095
2135
|
ListWorktreesOpts,
|
|
2096
2136
|
ListWorktreesResult,
|
|
2137
|
+
MergedCleanupItem,
|
|
2138
|
+
MergedCleanupOpts,
|
|
2139
|
+
MergedCleanupResult,
|
|
2097
2140
|
PrunedWorktreeOutcome,
|
|
2098
2141
|
PruneOrphanedWorktreesOpts,
|
|
2099
2142
|
PruneOrphanedWorktreesResult,
|
|
@@ -2199,6 +2242,7 @@ export { PS_STABLE_ENV } from './process-probe.js';
|
|
|
2199
2242
|
// === Project Context (ecosystem detection types) ===
|
|
2200
2243
|
export type {
|
|
2201
2244
|
EcosystemHint,
|
|
2245
|
+
EvidenceRepositoryDeclaration,
|
|
2202
2246
|
FileNamingConvention,
|
|
2203
2247
|
ImportStyle,
|
|
2204
2248
|
ProjectContext,
|
|
@@ -2653,6 +2697,7 @@ export type {
|
|
|
2653
2697
|
EvidenceValidationContext,
|
|
2654
2698
|
FileMeta,
|
|
2655
2699
|
GateEvidence,
|
|
2700
|
+
GateStandIn,
|
|
2656
2701
|
Phase,
|
|
2657
2702
|
PhaseStatus,
|
|
2658
2703
|
PhaseTransition,
|
|
@@ -3275,6 +3320,16 @@ export {
|
|
|
3275
3320
|
nexusStreamHeadSchema,
|
|
3276
3321
|
nexusUserKeysSchema,
|
|
3277
3322
|
} from './nexus-vault.js';
|
|
3323
|
+
// === Independent per-AC PR review (T14122) ===
|
|
3324
|
+
export type {
|
|
3325
|
+
ReviewAnswer,
|
|
3326
|
+
ReviewAuthors,
|
|
3327
|
+
ReviewGap,
|
|
3328
|
+
ReviewJudgement,
|
|
3329
|
+
ReviewPacket,
|
|
3330
|
+
ReviewPacketCriterion,
|
|
3331
|
+
ReviewVerdict,
|
|
3332
|
+
} from './review-verdict.js';
|
|
3278
3333
|
// === Sync schema version, shared by the change journal and the cloud vault (T13034) ===
|
|
3279
3334
|
export { SYNC_SCHEMA_VERSION } from './sync-schema.js';
|
|
3280
3335
|
export { isStorableTaskId, isTaskId, TASK_ID_REGEX, type TaskId } from './task-id.js';
|
|
@@ -558,6 +558,22 @@ export const SYNC_WRITE_INVARIANTS: readonly SyncWriteInvariant[] = Object.freez
|
|
|
558
558
|
reason:
|
|
559
559
|
'two devices adopting one legacy doc must mint one uid: the shell takes the legacy birth and sentinel facts, so (uid, birth_fp) converge and receive places the peer copy as a duplicate; a lost local write is refused loudly at the writer',
|
|
560
560
|
},
|
|
561
|
+
// T14118 — a slug is an alias, not an identity: renaming one moves the
|
|
562
|
+
// live slug on the registry row and the conduit identity row together and
|
|
563
|
+
// records the old slug in the append-only alias table.
|
|
564
|
+
{
|
|
565
|
+
id: 'docs.slug.rename-keeps-alias',
|
|
566
|
+
class: 'identity-layer',
|
|
567
|
+
tables: [
|
|
568
|
+
'docs_attachments',
|
|
569
|
+
'conduit_attachments',
|
|
570
|
+
'docs_wikilinks',
|
|
571
|
+
'conduit_doc_slug_aliases',
|
|
572
|
+
],
|
|
573
|
+
sites: [],
|
|
574
|
+
reason:
|
|
575
|
+
'a rename moves the live slug on docs_attachments and conduit_attachments in one transaction and records the old slug as an alias of the immutable doc uid, so every replica resolves the old and the new slug to one doc',
|
|
576
|
+
},
|
|
561
577
|
// T13359 (D11165) — decisions-vs-ADRs link table; the natural composite key
|
|
562
578
|
// makes identical links merge to one row, and the revoke is a one-way latch.
|
|
563
579
|
// T13597: enforced at merge, not just by the local accessor — 'revoked' is an
|
package/src/memory/search.ts
CHANGED
|
@@ -74,6 +74,11 @@ export interface BrainCompactHit {
|
|
|
74
74
|
_next?: Record<string, string>;
|
|
75
75
|
/** Store the hit came from; absent means the project store (T13720). */
|
|
76
76
|
scope?: MemoryScope;
|
|
77
|
+
/**
|
|
78
|
+
* Current docs linked to this decision (`docs link-decision`), each resolved
|
|
79
|
+
* past any supersession; never a superseded doc (T14110).
|
|
80
|
+
*/
|
|
81
|
+
docs?: string[];
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
// ── SearchBrainCompactParams ────────────────────────────────────────
|
|
@@ -158,4 +163,9 @@ export interface SearchBrainCompactResult {
|
|
|
158
163
|
total: number;
|
|
159
164
|
/** Approximate token cost of the returned payload (~chars/4). */
|
|
160
165
|
tokensEstimated: number;
|
|
166
|
+
/**
|
|
167
|
+
* For each task the query names (or a decision hit's task), the task's
|
|
168
|
+
* current docs; superseded docs are replaced by their current doc (T14110).
|
|
169
|
+
*/
|
|
170
|
+
taskDocs?: Array<{ taskId: string; docs: string[] }>;
|
|
161
171
|
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge closes tasks (T14101, owner decision D11209): the contract of the
|
|
3
|
+
* post-merge sweep that records a merged PR's evidence on the tasks its body
|
|
4
|
+
* names and completes them, with no agent step.
|
|
5
|
+
*
|
|
6
|
+
* A PR body names tasks one per line:
|
|
7
|
+
*
|
|
8
|
+
* - `Closes: T#### AC1,AC2` binds those criteria and completes the task once
|
|
9
|
+
* the criteria every merged PR bound cover all of the task's criteria.
|
|
10
|
+
* - `Refs: T#### AC1` binds those criteria from a PR that delivers part of a
|
|
11
|
+
* task, and leaves it open.
|
|
12
|
+
*
|
|
13
|
+
* @task T14101
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { MergedCleanupResult } from './operations/worktree.js';
|
|
17
|
+
|
|
18
|
+
/** One `Closes:` / `Refs:` line of a PR body. */
|
|
19
|
+
export interface MergeDirective {
|
|
20
|
+
/** `closes` completes the task; `refs` only binds criteria. */
|
|
21
|
+
kind: 'closes' | 'refs';
|
|
22
|
+
/** The task the line names. */
|
|
23
|
+
taskId: string;
|
|
24
|
+
/** Criterion aliases (`AC1`, …) the PR delivers, in the order written. */
|
|
25
|
+
acs: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* What a merged PR says, judged without a task store (the CI job's receipt):
|
|
30
|
+
* its lines, whether its required checks were green on its exact head, and
|
|
31
|
+
* why it closes nothing when it does not.
|
|
32
|
+
*/
|
|
33
|
+
export interface MergeReceipt {
|
|
34
|
+
/** PR number. */
|
|
35
|
+
prNumber: number;
|
|
36
|
+
/** The merge commit, or null when GitHub reports none. */
|
|
37
|
+
mergeSha: string | null;
|
|
38
|
+
/** The PR's final head. */
|
|
39
|
+
headSha: string | null;
|
|
40
|
+
/** The parsed lines. */
|
|
41
|
+
directives: MergeDirective[];
|
|
42
|
+
/** Malformed `Closes:` / `Refs:` lines, each with the fix. */
|
|
43
|
+
errors: string[];
|
|
44
|
+
/** Whether every required check passed on the PR's head. */
|
|
45
|
+
ciGreen: boolean;
|
|
46
|
+
/** Why CI does not count as green, when it does not. */
|
|
47
|
+
ciReason?: string;
|
|
48
|
+
/** Why the PR closes nothing at all, when it closes nothing. */
|
|
49
|
+
closesNothing?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** What the sweep did, or would do under `dryRun`, for one task of one PR. */
|
|
53
|
+
export interface MergeTaskOutcome {
|
|
54
|
+
/** The task. */
|
|
55
|
+
taskId: string;
|
|
56
|
+
/**
|
|
57
|
+
* `closed`: evidence recorded and the task completed. `bound`: a `Refs:`
|
|
58
|
+
* line's criteria recorded, the task left open. `already`: an earlier sweep
|
|
59
|
+
* handled this PR for the task, or the task was already done. `refused`:
|
|
60
|
+
* nothing recorded; `reason` says why. `waiting`: the merge commit's own CI
|
|
61
|
+
* run was cancelled, skipped or has not finished and nothing stands in yet;
|
|
62
|
+
* the PR is re-judged every sweep.
|
|
63
|
+
*/
|
|
64
|
+
action: 'closed' | 'bound' | 'already' | 'refused' | 'waiting';
|
|
65
|
+
/** The criteria the line named. */
|
|
66
|
+
acs: string[];
|
|
67
|
+
/** Why, for `refused` and `already`; for a dry run of an open PR, what would happen. */
|
|
68
|
+
reason?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The sweep's result for one merged PR. */
|
|
72
|
+
export interface MergePrOutcome {
|
|
73
|
+
/** PR number. */
|
|
74
|
+
prNumber: number;
|
|
75
|
+
/** Its merge commit. */
|
|
76
|
+
mergeSha: string | null;
|
|
77
|
+
/** One entry per task the PR names. */
|
|
78
|
+
outcomes: MergeTaskOutcome[];
|
|
79
|
+
/** Why nothing was attempted for this PR (no lines, malformed lines, CI not green). */
|
|
80
|
+
closesNothing?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Options of `sweepMergedClosures`. */
|
|
84
|
+
export interface MergeSweepOptions {
|
|
85
|
+
/** Report what would be recorded; write, complete and comment nothing. */
|
|
86
|
+
dryRun?: boolean;
|
|
87
|
+
/** Ignore the throttle (`cleo release` always passes it). */
|
|
88
|
+
force?: boolean;
|
|
89
|
+
/** Only PRs merged at or after this ISO time (default: the last sweep's high-water mark). */
|
|
90
|
+
since?: string;
|
|
91
|
+
/** Only these PRs (implies `force`). */
|
|
92
|
+
prNumbers?: number[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** What the sweep did with open `macos-regression` issues (T14104). */
|
|
96
|
+
export interface MacosRegressionSync {
|
|
97
|
+
/** Tasks created for newly seen issues (under `dryRun`: issue URLs that would get one). */
|
|
98
|
+
created: string[];
|
|
99
|
+
/** Issue URLs closed because their task is done. */
|
|
100
|
+
closedIssues: string[];
|
|
101
|
+
/** Why the issues were not read. */
|
|
102
|
+
skippedReason?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The newest release's provenance reconcile, run by the sweep (T14102). */
|
|
106
|
+
export interface MergeSweepReconcile {
|
|
107
|
+
/** The release version, or null when there is none. */
|
|
108
|
+
version: string | null;
|
|
109
|
+
/** What happened. */
|
|
110
|
+
action: 'reconciled' | 'already' | 'unpublished' | 'none' | 'failed';
|
|
111
|
+
/** Why, when it did not reconcile. */
|
|
112
|
+
reason?: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** The other upkeep one sweep runs after the closures, inside the same claim. */
|
|
116
|
+
export interface MergeSweepHooks {
|
|
117
|
+
/** Task ids flagged `docs-update` for docs a merged change left stale (T14111). */
|
|
118
|
+
docsRot?: string[];
|
|
119
|
+
/** `macos-regression` issues turned into tasks, and closed when their task is done (T14104). */
|
|
120
|
+
macosRegression?: MacosRegressionSync;
|
|
121
|
+
/** The newest release's reconcile (T14102); skipped under `dryRun`. */
|
|
122
|
+
reconcile?: MergeSweepReconcile;
|
|
123
|
+
/**
|
|
124
|
+
* Worktrees, branches and nested copies merged work left behind, removed
|
|
125
|
+
* per applied PR (T14142). Off unless `CLEO_MERGE_SWEEP_CLEANUP=1`.
|
|
126
|
+
*/
|
|
127
|
+
cleanup?: MergedCleanupResult[];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Result of `sweepMergedClosures` / `cleo done --from-merge`. */
|
|
131
|
+
export interface MergeSweepResult {
|
|
132
|
+
/** False when the sweep did not run: throttled, `gh` unavailable, or offline. */
|
|
133
|
+
swept: boolean;
|
|
134
|
+
/** Why it did not run. */
|
|
135
|
+
skippedReason?: string;
|
|
136
|
+
/** Whether this was a dry run. */
|
|
137
|
+
dryRun: boolean;
|
|
138
|
+
/** Each merged PR considered, oldest merge first. */
|
|
139
|
+
prs: MergePrOutcome[];
|
|
140
|
+
/** Tasks completed (under `dryRun`: that would be). */
|
|
141
|
+
closed: string[];
|
|
142
|
+
/** Tasks with criteria bound by `Refs:`, left open. */
|
|
143
|
+
bound: string[];
|
|
144
|
+
/** Tasks nothing was recorded for, with the reason. */
|
|
145
|
+
refused: Array<{ taskId: string; prNumber: number; reason: string }>;
|
|
146
|
+
/** Task outcomes waiting on their merge commit's CI (re-judged every sweep). */
|
|
147
|
+
waiting: number;
|
|
148
|
+
/** Cancelled or skipped merge-commit CI runs this sweep reran (once per merge commit, ever). */
|
|
149
|
+
reran: number;
|
|
150
|
+
/**
|
|
151
|
+
* Set when more PRs merged than one sweep's page budget reads: `read` PRs
|
|
152
|
+
* were processed, and those merged in `[unreadSince, unreadBefore]` are read
|
|
153
|
+
* first by the next sweep (never skipped).
|
|
154
|
+
*/
|
|
155
|
+
truncated?: { read: number; unreadSince: string; unreadBefore: string };
|
|
156
|
+
/**
|
|
157
|
+
* The pre-merge check of open PRs carrying Closes:/Refs: lines (T14122):
|
|
158
|
+
* the `cleo/merge-close` status each head got, and why when it failed.
|
|
159
|
+
*/
|
|
160
|
+
openChecks?: Array<{
|
|
161
|
+
prNumber: number;
|
|
162
|
+
headSha: string;
|
|
163
|
+
state: 'success' | 'failure';
|
|
164
|
+
description: string;
|
|
165
|
+
}>;
|
|
166
|
+
/** The upkeep run in the same sweep, when any ran. */
|
|
167
|
+
hooks?: MergeSweepHooks;
|
|
168
|
+
}
|