@cleocode/contracts 2026.10.27 → 2026.11.1-canary.2
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/.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/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +198 -0
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/docs/modernize.d.ts +409 -0
- package/dist/docs/modernize.d.ts.map +1 -0
- package/dist/docs/modernize.js +129 -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 +47 -1
- package/dist/evidence-atom-schema.d.ts.map +1 -1
- package/dist/evidence-atom-schema.js +83 -0
- 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 +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- 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 +174 -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/dispatch/operations-registry.ts +207 -0
- package/src/docs/modernize.ts +376 -0
- package/src/docs-taxonomy.ts +44 -1
- package/src/done-plan.ts +27 -2
- package/src/evidence-atom-schema.ts +97 -0
- package/src/exit-codes.ts +41 -0
- package/src/index.ts +51 -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 +180 -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
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,31 @@ 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
|
+
DocsModernizeOpKind,
|
|
432
|
+
DocsModernizeOpPrior,
|
|
433
|
+
DocsModernizeOpState,
|
|
434
|
+
DocsModernizeOverride,
|
|
435
|
+
DocsModernizeOverrides,
|
|
436
|
+
DocsModernizePlan,
|
|
437
|
+
DocsModernizePlanRow,
|
|
438
|
+
DocsModernizeReceipt,
|
|
439
|
+
DocsModernizeReceiptOp,
|
|
440
|
+
DocsModernizeStoreRow,
|
|
441
|
+
} from './docs/modernize.js';
|
|
442
|
+
export {
|
|
443
|
+
DOCS_MODERNIZE_DISPOSITIONS,
|
|
444
|
+
DOCS_MODERNIZE_OPS,
|
|
445
|
+
docsModernizeJournalLineSchema,
|
|
446
|
+
docsModernizeOverridesSchema,
|
|
447
|
+
docsModernizePlanSchema,
|
|
448
|
+
docsModernizeReceiptSchema,
|
|
449
|
+
} from './docs/modernize.js';
|
|
425
450
|
// === Docs Provenance Graph (T10166 / Saga T9855 / E12) ===
|
|
426
451
|
export type {
|
|
427
452
|
DocLifecycleStatus,
|
|
@@ -1222,6 +1247,17 @@ export type {
|
|
|
1222
1247
|
PatternRecord,
|
|
1223
1248
|
SessionSummary,
|
|
1224
1249
|
} from './memory.js';
|
|
1250
|
+
export type {
|
|
1251
|
+
MacosRegressionSync,
|
|
1252
|
+
MergeDirective,
|
|
1253
|
+
MergePrOutcome,
|
|
1254
|
+
MergeReceipt,
|
|
1255
|
+
MergeSweepHooks,
|
|
1256
|
+
MergeSweepOptions,
|
|
1257
|
+
MergeSweepReconcile,
|
|
1258
|
+
MergeSweepResult,
|
|
1259
|
+
MergeTaskOutcome,
|
|
1260
|
+
} from './merge-close.js';
|
|
1225
1261
|
export type {
|
|
1226
1262
|
MigrationEnumDrift,
|
|
1227
1263
|
MigrationForeignKeyViolation,
|
|
@@ -2094,6 +2130,9 @@ export type {
|
|
|
2094
2130
|
ListWorktreesOptions,
|
|
2095
2131
|
ListWorktreesOpts,
|
|
2096
2132
|
ListWorktreesResult,
|
|
2133
|
+
MergedCleanupItem,
|
|
2134
|
+
MergedCleanupOpts,
|
|
2135
|
+
MergedCleanupResult,
|
|
2097
2136
|
PrunedWorktreeOutcome,
|
|
2098
2137
|
PruneOrphanedWorktreesOpts,
|
|
2099
2138
|
PruneOrphanedWorktreesResult,
|
|
@@ -2199,6 +2238,7 @@ export { PS_STABLE_ENV } from './process-probe.js';
|
|
|
2199
2238
|
// === Project Context (ecosystem detection types) ===
|
|
2200
2239
|
export type {
|
|
2201
2240
|
EcosystemHint,
|
|
2241
|
+
EvidenceRepositoryDeclaration,
|
|
2202
2242
|
FileNamingConvention,
|
|
2203
2243
|
ImportStyle,
|
|
2204
2244
|
ProjectContext,
|
|
@@ -2653,6 +2693,7 @@ export type {
|
|
|
2653
2693
|
EvidenceValidationContext,
|
|
2654
2694
|
FileMeta,
|
|
2655
2695
|
GateEvidence,
|
|
2696
|
+
GateStandIn,
|
|
2656
2697
|
Phase,
|
|
2657
2698
|
PhaseStatus,
|
|
2658
2699
|
PhaseTransition,
|
|
@@ -3275,6 +3316,16 @@ export {
|
|
|
3275
3316
|
nexusStreamHeadSchema,
|
|
3276
3317
|
nexusUserKeysSchema,
|
|
3277
3318
|
} from './nexus-vault.js';
|
|
3319
|
+
// === Independent per-AC PR review (T14122) ===
|
|
3320
|
+
export type {
|
|
3321
|
+
ReviewAnswer,
|
|
3322
|
+
ReviewAuthors,
|
|
3323
|
+
ReviewGap,
|
|
3324
|
+
ReviewJudgement,
|
|
3325
|
+
ReviewPacket,
|
|
3326
|
+
ReviewPacketCriterion,
|
|
3327
|
+
ReviewVerdict,
|
|
3328
|
+
} from './review-verdict.js';
|
|
3278
3329
|
// === Sync schema version, shared by the change journal and the cloud vault (T13034) ===
|
|
3279
3330
|
export { SYNC_SCHEMA_VERSION } from './sync-schema.js';
|
|
3280
3331
|
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
|
+
}
|
package/src/operations/admin.ts
CHANGED
|
@@ -1662,6 +1662,12 @@ export interface AdminBackupMutateParams {
|
|
|
1662
1662
|
confirmOwnerStore?: boolean;
|
|
1663
1663
|
/** Create: also back up the global store (`<CLEO_HOME>/cleo.db`, T13245). */
|
|
1664
1664
|
includeGlobal?: boolean;
|
|
1665
|
+
/**
|
|
1666
|
+
* Create: write the backup into this directory (the global copy under
|
|
1667
|
+
* `<destination>/global`) instead of the rotating backup dirs; it never
|
|
1668
|
+
* rotates or is rotated (T13209).
|
|
1669
|
+
*/
|
|
1670
|
+
destination?: string;
|
|
1665
1671
|
/** Restore by id: `global` restores a backup of the global store (T13245). */
|
|
1666
1672
|
scope?: 'project' | 'global';
|
|
1667
1673
|
}
|
package/src/operations/docs.ts
CHANGED
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
import { z } from 'zod';
|
|
34
34
|
import type { AttachmentKind } from '../attachment.js';
|
|
35
|
+
import type { TaskPopulation } from '../data-accessor.js';
|
|
36
|
+
import type { DocsModernizePlan, DocsModernizeReceipt } from '../docs/modernize.js';
|
|
35
37
|
import { BUILTIN_DOC_KIND_VALUES, type BuiltinDocKind } from '../docs-taxonomy.js';
|
|
36
38
|
import type {
|
|
37
39
|
BackgroundJobStatus,
|
|
@@ -316,6 +318,12 @@ export interface DocsListResult {
|
|
|
316
318
|
* @task T9792
|
|
317
319
|
*/
|
|
318
320
|
totalCount?: number;
|
|
321
|
+
/**
|
|
322
|
+
* How many attachments matched, how many this response returns, whether it
|
|
323
|
+
* left any out, and the window applied (`null`: every match, `--limit 0`).
|
|
324
|
+
* The same shape `cleo list` reports (T14115).
|
|
325
|
+
*/
|
|
326
|
+
population: Pick<TaskPopulation, 'matched' | 'returned' | 'truncated' | 'limit'>;
|
|
319
327
|
/**
|
|
320
328
|
* Effective limit applied to this response. Mirrored from the request
|
|
321
329
|
* (or {@link DOCS_LIST_DEFAULT_LIMIT} when the request did not set one)
|
|
@@ -458,6 +466,37 @@ export interface DocsFetchResult {
|
|
|
458
466
|
* ratifies.
|
|
459
467
|
*/
|
|
460
468
|
linkedDecisions?: string[];
|
|
469
|
+
/**
|
|
470
|
+
* Where this doc sits in its supersession chain and what it serves
|
|
471
|
+
* (T14110). Present for slugged docs.
|
|
472
|
+
*/
|
|
473
|
+
lineage?: DocsLineage;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* A doc's supersession chain and the work it serves (T14110). Built from the
|
|
478
|
+
* existing `supersedes`/`superseded_by` pointers, task refs and
|
|
479
|
+
* `related_tasks`; nothing here is stored separately.
|
|
480
|
+
*/
|
|
481
|
+
export interface DocsLineage {
|
|
482
|
+
/** The doc's lifecycle status (`superseded` when replaced). */
|
|
483
|
+
readonly lifecycleStatus: string;
|
|
484
|
+
/** Slug of the doc this one replaced, when any. */
|
|
485
|
+
readonly supersedes: string | null;
|
|
486
|
+
/** Slug of the doc that replaced this one, when any. */
|
|
487
|
+
readonly supersededBy: string | null;
|
|
488
|
+
/**
|
|
489
|
+
* Slug of the current doc at the end of the `superseded_by` chain: this
|
|
490
|
+
* doc's own slug when it is current. Read this one instead.
|
|
491
|
+
*/
|
|
492
|
+
readonly currentSlug: string;
|
|
493
|
+
/** Task ids the doc serves: its task owners and the tasks its body names. */
|
|
494
|
+
readonly servesTasks: readonly string[];
|
|
495
|
+
/**
|
|
496
|
+
* Decisions this doc supersedes: the `supersedes` of each decision it links
|
|
497
|
+
* (`docs link-decision`). Absent when it links none that supersede another.
|
|
498
|
+
*/
|
|
499
|
+
readonly supersedesDecisions?: readonly string[];
|
|
461
500
|
}
|
|
462
501
|
|
|
463
502
|
// --------------------------------------------------------------------------
|
|
@@ -570,6 +609,12 @@ export interface DocsAddParams {
|
|
|
570
609
|
* @saga T9855
|
|
571
610
|
*/
|
|
572
611
|
strict?: boolean;
|
|
612
|
+
/**
|
|
613
|
+
* Why this doc is new although it resembles an existing one (T14109). A
|
|
614
|
+
* likely duplicate is refused unless this states the reason (at least 15
|
|
615
|
+
* characters); the reason is recorded in `.cleo/audit/docs-new-because.jsonl`.
|
|
616
|
+
*/
|
|
617
|
+
newBecause?: string;
|
|
573
618
|
}
|
|
574
619
|
|
|
575
620
|
/** Canonical attachment and owner whose optional projections may be prepared. */
|
|
@@ -1121,7 +1166,8 @@ export interface DocsDoctorDiagnostic {
|
|
|
1121
1166
|
| 'legacy-store-present'
|
|
1122
1167
|
| 'legacy-blob-coverage'
|
|
1123
1168
|
| 'stale-drafts'
|
|
1124
|
-
| 'conduit-history-gaps'
|
|
1169
|
+
| 'conduit-history-gaps'
|
|
1170
|
+
| 'publication-ledger-drift';
|
|
1125
1171
|
/** `error` should be fixed promptly; `info` is observational. */
|
|
1126
1172
|
severity: 'info' | 'warn' | 'error';
|
|
1127
1173
|
/** Total offenders found (may exceed `items.length`). */
|
|
@@ -1164,7 +1210,7 @@ export interface DocsDoctorResult {
|
|
|
1164
1210
|
olderThanDays: number;
|
|
1165
1211
|
/** Backup receipt that authorised this run (null on dry runs). */
|
|
1166
1212
|
backupReceiptPath: string | null;
|
|
1167
|
-
/** All
|
|
1213
|
+
/** All nine diagnostics, in declaration order. */
|
|
1168
1214
|
diagnostics: readonly DocsDoctorDiagnostic[];
|
|
1169
1215
|
/** Repair plan (dry run) or executed repairs (apply). */
|
|
1170
1216
|
repairs: readonly DocsDoctorRepairAction[];
|
|
@@ -1242,6 +1288,87 @@ export interface DocsStatsResult {
|
|
|
1242
1288
|
readonly summary: string;
|
|
1243
1289
|
}
|
|
1244
1290
|
|
|
1291
|
+
/**
|
|
1292
|
+
* Parameters for `docs.rot` — the docs-rot report (T14111).
|
|
1293
|
+
*
|
|
1294
|
+
* Read-only and git-only: it reads tracked markdown and git history, never
|
|
1295
|
+
* the docs store, so it runs the same in CI and locally.
|
|
1296
|
+
*
|
|
1297
|
+
* @task T14111 (Epic T14106)
|
|
1298
|
+
*/
|
|
1299
|
+
export interface DocsRotParams {
|
|
1300
|
+
/**
|
|
1301
|
+
* Git revision range `<base>..<head>` of a merged change. When set, the
|
|
1302
|
+
* report also lists the docs whose `covers:` globs match a file the range
|
|
1303
|
+
* changed (and that the range did not update itself).
|
|
1304
|
+
*/
|
|
1305
|
+
readonly changed?: string;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
/**
|
|
1309
|
+
* A doc whose declared `covers:` code changed without the doc changing.
|
|
1310
|
+
*
|
|
1311
|
+
* @task T14111
|
|
1312
|
+
*/
|
|
1313
|
+
export interface DocsRotCoverageHit {
|
|
1314
|
+
/** Repo-relative path of the doc. */
|
|
1315
|
+
readonly doc: string;
|
|
1316
|
+
/** Covered files that changed (sorted, repo-relative). */
|
|
1317
|
+
readonly files: readonly string[];
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* A group of docs that duplicate each other: identical bodies, or titles
|
|
1322
|
+
* close enough that one is a fork of the other.
|
|
1323
|
+
*
|
|
1324
|
+
* @task T14111
|
|
1325
|
+
*/
|
|
1326
|
+
export interface DocsRotDuplicate {
|
|
1327
|
+
/** Why the docs were grouped: `content` (same body) or `title`. */
|
|
1328
|
+
readonly reason: 'content' | 'title';
|
|
1329
|
+
/** Repo-relative paths of the duplicates (sorted). */
|
|
1330
|
+
readonly docs: readonly string[];
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* A doc nothing ties to the repo: a `covers:` glob that matches no tracked
|
|
1335
|
+
* file, or a `docs/` file no other tracked file references.
|
|
1336
|
+
*
|
|
1337
|
+
* @task T14111
|
|
1338
|
+
*/
|
|
1339
|
+
export interface DocsRotOrphan {
|
|
1340
|
+
/** Repo-relative path of the doc. */
|
|
1341
|
+
readonly doc: string;
|
|
1342
|
+
/** `dead-covers` (globs listed in `globs`) or `unreferenced`. */
|
|
1343
|
+
readonly reason: 'dead-covers' | 'unreferenced';
|
|
1344
|
+
/** The covers globs that match nothing (only for `dead-covers`). */
|
|
1345
|
+
readonly globs?: readonly string[];
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* Result of `docs.rot` — stale, duplicate and orphaned docs (T14111).
|
|
1350
|
+
*
|
|
1351
|
+
* @task T14111
|
|
1352
|
+
*/
|
|
1353
|
+
export interface DocsRotResult {
|
|
1354
|
+
/** Tracked markdown docs scanned. */
|
|
1355
|
+
readonly scanned: number;
|
|
1356
|
+
/** Docs that declare `covers:`. */
|
|
1357
|
+
readonly covering: number;
|
|
1358
|
+
/** Docs whose covered code changed after the doc last changed on main. */
|
|
1359
|
+
readonly stale: readonly DocsRotCoverageHit[];
|
|
1360
|
+
/** Duplicate doc groups. */
|
|
1361
|
+
readonly duplicates: readonly DocsRotDuplicate[];
|
|
1362
|
+
/** Orphaned docs. */
|
|
1363
|
+
readonly orphans: readonly DocsRotOrphan[];
|
|
1364
|
+
/** Docs the `changed` range flags for update (empty without `changed`). */
|
|
1365
|
+
readonly flagged: readonly DocsRotCoverageHit[];
|
|
1366
|
+
/** Wall time of the scan in milliseconds. */
|
|
1367
|
+
readonly durationMs: number;
|
|
1368
|
+
/** One-line human summary. */
|
|
1369
|
+
readonly summary: string;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1245
1372
|
// ============================================================================
|
|
1246
1373
|
// Discriminated Union (DocsOps)
|
|
1247
1374
|
// ============================================================================
|
|
@@ -1269,7 +1396,54 @@ export type DocsOps =
|
|
|
1269
1396
|
| { op: 'docs.unlink-decision'; params: DocsDecisionLinkParams; result: DocsDecisionLinkResult }
|
|
1270
1397
|
| { op: 'docs.doctor'; params: DocsDoctorParams; result: DocsDoctorResult }
|
|
1271
1398
|
| { op: 'docs.migrate'; params: DocsMigrateParams; result: DocsMigrateResult }
|
|
1272
|
-
| { op: 'docs.
|
|
1399
|
+
| { op: 'docs.modernize'; params: DocsModernizeParams; result: DocsModernizeResult }
|
|
1400
|
+
| { op: 'docs.stats'; params: DocsStatsParams; result: DocsStatsResult }
|
|
1401
|
+
| { op: 'docs.rot'; params: DocsRotParams; result: DocsRotResult };
|
|
1402
|
+
|
|
1403
|
+
/**
|
|
1404
|
+
* Parameters for `docs.modernize` (T14113) — dry run by default.
|
|
1405
|
+
*/
|
|
1406
|
+
export interface DocsModernizeParams {
|
|
1407
|
+
/** Folder to modernize, relative to the project root or absolute. Required unless `rollback`. */
|
|
1408
|
+
readonly path?: string;
|
|
1409
|
+
/** Execute the plan; without it the run is a dry run that writes nothing. */
|
|
1410
|
+
readonly apply?: boolean;
|
|
1411
|
+
/** Apply this saved plan JSON verbatim instead of re-planning. */
|
|
1412
|
+
readonly planFile?: string;
|
|
1413
|
+
/** Save the plan JSON here. */
|
|
1414
|
+
readonly out?: string;
|
|
1415
|
+
/** Roll back the apply run with this id. */
|
|
1416
|
+
readonly rollback?: string;
|
|
1417
|
+
/** Extra folder names to skip, comma-separated. */
|
|
1418
|
+
readonly exclude?: string;
|
|
1419
|
+
/** Path of a `DocsModernizeOverrides` JSON file keyed by root-relative path. */
|
|
1420
|
+
readonly overrides?: string;
|
|
1421
|
+
/** Use this kind for every file instead of classifying. */
|
|
1422
|
+
readonly kind?: string;
|
|
1423
|
+
/** Ingest every file into the store (task-owned, slugless) and leave the folder untouched. */
|
|
1424
|
+
readonly storeOnly?: boolean;
|
|
1425
|
+
/** Apply even when the root is not a clean git checkout. */
|
|
1426
|
+
readonly allowDirty?: boolean;
|
|
1427
|
+
/** Modernize the docs already in the store (re-kind, re-slug with aliases, ADR numbers). */
|
|
1428
|
+
readonly store?: boolean;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* Result of `docs.modernize`: the plan (dry run, apply) and the receipt
|
|
1433
|
+
* (apply, rollback).
|
|
1434
|
+
*/
|
|
1435
|
+
export interface DocsModernizeResult {
|
|
1436
|
+
/** What ran. */
|
|
1437
|
+
readonly mode: 'dry-run' | 'apply' | 'rollback';
|
|
1438
|
+
/** The plan, or null for a rollback. */
|
|
1439
|
+
readonly plan: DocsModernizePlan | null;
|
|
1440
|
+
/** Where the plan was saved, when `out` was given. */
|
|
1441
|
+
readonly planPath: string | null;
|
|
1442
|
+
/** The receipt, or null for a dry run. */
|
|
1443
|
+
readonly receipt: DocsModernizeReceipt | null;
|
|
1444
|
+
/** One-line human summary. */
|
|
1445
|
+
readonly summary: string;
|
|
1446
|
+
}
|
|
1273
1447
|
|
|
1274
1448
|
/**
|
|
1275
1449
|
* Parameters for `docs.migrate` — the T13360 legacy docs migration (spec §5).
|
|
@@ -1336,4 +1510,6 @@ export type DocsOp =
|
|
|
1336
1510
|
| 'docs.unlink-decision'
|
|
1337
1511
|
| 'docs.doctor'
|
|
1338
1512
|
| 'docs.migrate'
|
|
1339
|
-
| 'docs.
|
|
1513
|
+
| 'docs.modernize'
|
|
1514
|
+
| 'docs.stats'
|
|
1515
|
+
| 'docs.rot';
|
|
@@ -1098,3 +1098,69 @@ export interface AdoptWorktreeResult {
|
|
|
1098
1098
|
*/
|
|
1099
1099
|
projectInfo: 'seeded' | 'present' | 'skipped';
|
|
1100
1100
|
}
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Options for `cleanupMergedWork` (T14142): remove what merged work left
|
|
1104
|
+
* behind, after proving each item redundant.
|
|
1105
|
+
*
|
|
1106
|
+
* @task T14142
|
|
1107
|
+
*/
|
|
1108
|
+
export interface MergedCleanupOpts {
|
|
1109
|
+
/** Project root (the main checkout; its hash names the canonical worktree root). */
|
|
1110
|
+
projectRoot: string;
|
|
1111
|
+
/**
|
|
1112
|
+
* Tasks whose work merged (the sweep's merged PR, or a release's tasks).
|
|
1113
|
+
* Either way a task is in scope only while stored `done` or `cancelled`:
|
|
1114
|
+
* an open task (more PRs to come) keeps its worktree and branches.
|
|
1115
|
+
*/
|
|
1116
|
+
taskIds?: readonly string[];
|
|
1117
|
+
/** List everything without deleting. Default `true`: deleting is opt-in. */
|
|
1118
|
+
dryRun?: boolean;
|
|
1119
|
+
/** Also delete merged task branches on `origin`. Default `true`. */
|
|
1120
|
+
remote?: boolean;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* One item the cleanup considered: removed (or, on a dry run, removable), or
|
|
1125
|
+
* kept with the reason.
|
|
1126
|
+
*
|
|
1127
|
+
* @task T14142
|
|
1128
|
+
*/
|
|
1129
|
+
export interface MergedCleanupItem {
|
|
1130
|
+
/** Worktree or nested-copy path, or branch name. */
|
|
1131
|
+
target: string;
|
|
1132
|
+
/** Owning task, when the target names one. */
|
|
1133
|
+
taskId: string | null;
|
|
1134
|
+
/** Branch tip (branches only): what a restore pushes back. */
|
|
1135
|
+
sha?: string;
|
|
1136
|
+
/** True when removed (dry run: when it would be removed). */
|
|
1137
|
+
removed: boolean;
|
|
1138
|
+
/** Why it was kept, or what proved it redundant. */
|
|
1139
|
+
reason: string;
|
|
1140
|
+
/** Bytes on disk (worktrees and nested copies; 0 for branches). */
|
|
1141
|
+
bytes: number;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Result of `cleanupMergedWork` (T14142).
|
|
1146
|
+
*
|
|
1147
|
+
* @task T14142
|
|
1148
|
+
*/
|
|
1149
|
+
export interface MergedCleanupResult {
|
|
1150
|
+
/** Whether nothing was deleted. */
|
|
1151
|
+
dryRun: boolean;
|
|
1152
|
+
/** The default branch merges were proven against (`origin/<name>`). */
|
|
1153
|
+
base: string;
|
|
1154
|
+
/** Canonical task worktrees. */
|
|
1155
|
+
worktrees: MergedCleanupItem[];
|
|
1156
|
+
/** Local `task/*` branches. */
|
|
1157
|
+
localBranches: MergedCleanupItem[];
|
|
1158
|
+
/** `origin` `task/*` branches. */
|
|
1159
|
+
remoteBranches: MergedCleanupItem[];
|
|
1160
|
+
/** Repository copies nested inside worktrees. */
|
|
1161
|
+
nestedCopies: MergedCleanupItem[];
|
|
1162
|
+
/** Bytes removed (dry run: that would be removed). */
|
|
1163
|
+
bytesReclaimed: number;
|
|
1164
|
+
/** Why nothing was considered (the fetch failed, no base branch); absent when it ran. */
|
|
1165
|
+
skippedReason?: string;
|
|
1166
|
+
}
|