@cleocode/contracts 2026.5.114 → 2026.5.120
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__/meta-tokens.test.d.ts +16 -0
- package/dist/__tests__/meta-tokens.test.d.ts.map +1 -0
- package/dist/__tests__/meta-tokens.test.js +99 -0
- package/dist/__tests__/meta-tokens.test.js.map +1 -0
- package/dist/boundary.js +3 -3
- package/dist/boundary.js.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +119 -0
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/operations/docs.d.ts +166 -8
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js +9 -7
- package/dist/operations/docs.js.map +1 -1
- package/dist/operations/tasks.d.ts +13 -0
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js.map +1 -1
- package/dist/task-record.d.ts +7 -0
- package/dist/task-record.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
- package/src/__tests__/meta-tokens.test.ts +107 -0
- package/src/boundary.ts +3 -3
- package/src/dispatch/operations-registry.ts +124 -0
- package/src/operations/docs.ts +187 -9
- package/src/operations/tasks.ts +13 -0
- package/src/task-record.ts +7 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-shape contract test for the envelope-wide `meta.tokens` field
|
|
3
|
+
* promoted in T9923 (Saga T9855 / E8.4).
|
|
4
|
+
*
|
|
5
|
+
* `CliMeta.tokens` is the first-class structured token-cost annotation
|
|
6
|
+
* attached to every CLI envelope (formerly the underscore-prefixed
|
|
7
|
+
* `meta._tokenEstimate`). The legacy field is retained for ONE release
|
|
8
|
+
* (removeAt v2026.7.0) so existing consumers can migrate without a
|
|
9
|
+
* flag-day.
|
|
10
|
+
*
|
|
11
|
+
* @epic T9919
|
|
12
|
+
* @task T9923
|
|
13
|
+
* @saga T9855
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { CliEnvelope, CliMeta, CliMetaTokens } from '@cleocode/lafs';
|
|
17
|
+
import { describe, expect, expectTypeOf, it } from 'vitest';
|
|
18
|
+
|
|
19
|
+
describe('CliMeta.tokens type shape (T9923)', () => {
|
|
20
|
+
it('accepts the full first-class CliMetaTokens shape', () => {
|
|
21
|
+
const meta: CliMeta = {
|
|
22
|
+
operation: 'tasks.show',
|
|
23
|
+
requestId: '00000000-0000-0000-0000-000000000000',
|
|
24
|
+
duration_ms: 0,
|
|
25
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
26
|
+
tokens: {
|
|
27
|
+
estimate: 1234,
|
|
28
|
+
model: 'cl100k',
|
|
29
|
+
calculatedAt: '2026-05-24T00:00:00.000Z',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
expect(meta.tokens?.estimate).toBe(1234);
|
|
33
|
+
expect(meta.tokens?.model).toBe('cl100k');
|
|
34
|
+
expect(meta.tokens?.calculatedAt).toBe('2026-05-24T00:00:00.000Z');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('requires `estimate` to be a number when `tokens` is present', () => {
|
|
38
|
+
expectTypeOf<NonNullable<CliMeta['tokens']>['estimate']>().toEqualTypeOf<number>();
|
|
39
|
+
expectTypeOf<NonNullable<CliMeta['tokens']>['model']>().toEqualTypeOf<string>();
|
|
40
|
+
expectTypeOf<NonNullable<CliMeta['tokens']>['calculatedAt']>().toEqualTypeOf<string>();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('is optional — omitting `tokens` leaves a valid CliMeta', () => {
|
|
44
|
+
const meta: CliMeta = {
|
|
45
|
+
operation: 'tasks.list',
|
|
46
|
+
requestId: '00000000-0000-0000-0000-000000000001',
|
|
47
|
+
duration_ms: 0,
|
|
48
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
49
|
+
};
|
|
50
|
+
expect(meta.tokens).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('accepts the legacy `_tokenEstimate` field during the deprecation window', () => {
|
|
54
|
+
const meta: CliMeta = {
|
|
55
|
+
operation: 'tasks.show',
|
|
56
|
+
requestId: '00000000-0000-0000-0000-000000000002',
|
|
57
|
+
duration_ms: 0,
|
|
58
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
59
|
+
_tokenEstimate: { estimate: 999 },
|
|
60
|
+
};
|
|
61
|
+
expect(meta._tokenEstimate?.estimate).toBe(999);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('allows BOTH `tokens` and `_tokenEstimate` for backwards-compat dual-write', () => {
|
|
65
|
+
const meta: CliMeta = {
|
|
66
|
+
operation: 'tasks.show',
|
|
67
|
+
requestId: '00000000-0000-0000-0000-000000000003',
|
|
68
|
+
duration_ms: 0,
|
|
69
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
70
|
+
tokens: {
|
|
71
|
+
estimate: 4321,
|
|
72
|
+
model: 'o200k',
|
|
73
|
+
calculatedAt: '2026-05-24T00:00:00.000Z',
|
|
74
|
+
},
|
|
75
|
+
_tokenEstimate: { estimate: 4321 },
|
|
76
|
+
};
|
|
77
|
+
expect(meta.tokens?.estimate).toBe(meta._tokenEstimate?.estimate);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('flows through CliEnvelope<T>.meta verbatim', () => {
|
|
81
|
+
const envelope: CliEnvelope<{ taskId: string }> = {
|
|
82
|
+
success: true,
|
|
83
|
+
data: { taskId: 'T9923' },
|
|
84
|
+
meta: {
|
|
85
|
+
operation: 'tasks.show',
|
|
86
|
+
requestId: '00000000-0000-0000-0000-000000000004',
|
|
87
|
+
duration_ms: 1,
|
|
88
|
+
timestamp: '2026-05-24T00:00:00.000Z',
|
|
89
|
+
tokens: {
|
|
90
|
+
estimate: 512,
|
|
91
|
+
model: 'cl100k',
|
|
92
|
+
calculatedAt: '2026-05-24T00:00:00.000Z',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
expect(envelope.meta.tokens?.estimate).toBe(512);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('CliMetaTokens is structurally compatible with manual construction', () => {
|
|
100
|
+
const t: CliMetaTokens = {
|
|
101
|
+
estimate: 100,
|
|
102
|
+
model: 'approx',
|
|
103
|
+
calculatedAt: '2026-05-24T00:00:00.000Z',
|
|
104
|
+
};
|
|
105
|
+
expect(t.estimate).toBe(100);
|
|
106
|
+
});
|
|
107
|
+
});
|
package/src/boundary.ts
CHANGED
|
@@ -492,7 +492,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
492
492
|
panic_unwind: 'allowed-with-recovery',
|
|
493
493
|
root_escape: 'forbidden',
|
|
494
494
|
},
|
|
495
|
-
amendments: ['adr-
|
|
495
|
+
amendments: ['adr-087-worktree-ffi-topology'],
|
|
496
496
|
rationale:
|
|
497
497
|
'napi binding shim (515 LOC) for worktrunk-core, used by packages/core spawn-pipeline tests, packages/core/src/scaffold/ensure-config.ts, and shipped via packages/worktree-napi-* per-platform npm packages. Internal-only.',
|
|
498
498
|
},
|
|
@@ -510,7 +510,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
510
510
|
panic_unwind: 'forbidden',
|
|
511
511
|
root_escape: 'forbidden',
|
|
512
512
|
},
|
|
513
|
-
amendments: ['adr-077-worktreeinclude', 'adr-078-boundary-registry'],
|
|
513
|
+
amendments: ['adr-077-worktreeinclude-canonical-location', 'adr-078-boundary-registry'],
|
|
514
514
|
rationale:
|
|
515
515
|
'Refactored SoC per ADR-078 amendment 2026-05-23. Worktree primitives core (1,352 LOC) vendored from /mnt/projects/worktrunk per D010, consumed by worktree-napi. Reference implementation of the boundary-registry pattern. Full SDK surface documented in crates/worktrunk-core/README.md (T10223). Internal-only today; publish=true is a future option if external worktree-tooling emerges.',
|
|
516
516
|
},
|
|
@@ -868,7 +868,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
868
868
|
root_escape: 'forbidden',
|
|
869
869
|
fs_writes_outside_root: 'audited',
|
|
870
870
|
},
|
|
871
|
-
amendments: ['adr-
|
|
871
|
+
amendments: ['adr-087-worktree-ffi-topology', 'adr-078-boundary-registry'],
|
|
872
872
|
rationale:
|
|
873
873
|
'Worktree SSoT primitive layer (1,972 LOC) — canonical create/destroy/list/prune/include/copy-on-write bound by @cleocode/paths. Post-PR-#487 the responsibilities are distinct from packages/core/src/worktree/ (SDK-level enrichment). Consumes worktrunk-core via worktree-napi.',
|
|
874
874
|
},
|
|
@@ -2335,6 +2335,41 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
2335
2335
|
},
|
|
2336
2336
|
] satisfies ParamDef[],
|
|
2337
2337
|
},
|
|
2338
|
+
{
|
|
2339
|
+
gateway: 'mutate',
|
|
2340
|
+
domain: 'tasks',
|
|
2341
|
+
operation: 'relates.remove',
|
|
2342
|
+
description:
|
|
2343
|
+
'tasks.relates.remove (mutate) — delete a relation between two tasks. Pass `type` to scope deletion to a single relation kind; omit to remove any type.',
|
|
2344
|
+
tier: 1,
|
|
2345
|
+
idempotent: true,
|
|
2346
|
+
sessionRequired: false,
|
|
2347
|
+
requiredParams: ['taskId', 'relatedId'],
|
|
2348
|
+
params: [
|
|
2349
|
+
{
|
|
2350
|
+
name: 'taskId',
|
|
2351
|
+
type: 'string',
|
|
2352
|
+
required: true,
|
|
2353
|
+
description: 'Source task ID',
|
|
2354
|
+
cli: { positional: true },
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
name: 'relatedId',
|
|
2358
|
+
type: 'string',
|
|
2359
|
+
required: true,
|
|
2360
|
+
description: 'Target task ID to remove the relation to',
|
|
2361
|
+
cli: { positional: true },
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
name: 'type',
|
|
2365
|
+
type: 'string',
|
|
2366
|
+
required: false,
|
|
2367
|
+
description:
|
|
2368
|
+
'Optional relation type (blocks|related|duplicates|absorbs|fixes|extends|supersedes). Omit to remove any type.',
|
|
2369
|
+
cli: { flag: 'type' },
|
|
2370
|
+
},
|
|
2371
|
+
] satisfies ParamDef[],
|
|
2372
|
+
},
|
|
2338
2373
|
// === saga sub-domain (ADR-073 — above-epic grouping tier) ===
|
|
2339
2374
|
{
|
|
2340
2375
|
gateway: 'mutate',
|
|
@@ -6771,6 +6806,40 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
6771
6806
|
},
|
|
6772
6807
|
],
|
|
6773
6808
|
},
|
|
6809
|
+
// ── docs.supersede (T10162) ──────────────────────────────────────────────
|
|
6810
|
+
{
|
|
6811
|
+
gateway: 'mutate',
|
|
6812
|
+
domain: 'docs',
|
|
6813
|
+
operation: 'supersede',
|
|
6814
|
+
description:
|
|
6815
|
+
'docs.supersede (mutate) — atomically flip an older doc to `superseded` and link both rows via the supersedes/superseded_by self-FK pointers (T10162 · Saga T9855)',
|
|
6816
|
+
tier: 1,
|
|
6817
|
+
idempotent: true,
|
|
6818
|
+
sessionRequired: false,
|
|
6819
|
+
requiredParams: ['oldSlug', 'newSlug'],
|
|
6820
|
+
params: [
|
|
6821
|
+
{
|
|
6822
|
+
name: 'oldSlug',
|
|
6823
|
+
type: 'string' as const,
|
|
6824
|
+
required: true,
|
|
6825
|
+
description: 'Slug of the doc being replaced',
|
|
6826
|
+
cli: { positional: true },
|
|
6827
|
+
},
|
|
6828
|
+
{
|
|
6829
|
+
name: 'newSlug',
|
|
6830
|
+
type: 'string' as const,
|
|
6831
|
+
required: true,
|
|
6832
|
+
description: 'Slug of the doc that replaces oldSlug',
|
|
6833
|
+
cli: { positional: true },
|
|
6834
|
+
},
|
|
6835
|
+
{
|
|
6836
|
+
name: 'reason',
|
|
6837
|
+
type: 'string' as const,
|
|
6838
|
+
required: false,
|
|
6839
|
+
description: 'Optional human-readable reason carried back on the response',
|
|
6840
|
+
},
|
|
6841
|
+
],
|
|
6842
|
+
},
|
|
6774
6843
|
// ── docs.generate (T798) ─────────────────────────────────────────────────
|
|
6775
6844
|
{
|
|
6776
6845
|
gateway: 'query',
|
|
@@ -6797,6 +6866,61 @@ export const OPERATIONS: OperationDef[] = [
|
|
|
6797
6866
|
},
|
|
6798
6867
|
],
|
|
6799
6868
|
},
|
|
6869
|
+
// ── docs.update (T10161 — Epic T10157 / Saga T9855) ──────────────────────
|
|
6870
|
+
{
|
|
6871
|
+
gateway: 'mutate',
|
|
6872
|
+
domain: 'docs',
|
|
6873
|
+
operation: 'update',
|
|
6874
|
+
description:
|
|
6875
|
+
'docs.update (mutate) — replace blob content for an existing slug while preserving the slug. ' +
|
|
6876
|
+
'New sha256 + new attachment row; the old row stays reachable by ID for history. ' +
|
|
6877
|
+
'NO supersession edge (use `cleo docs supersede` for that). ' +
|
|
6878
|
+
'Audit log entry appended to .cleo/audit/docs-versioning.jsonl (squashed within a 5-min window).',
|
|
6879
|
+
tier: 1,
|
|
6880
|
+
idempotent: false,
|
|
6881
|
+
sessionRequired: false,
|
|
6882
|
+
requiredParams: ['slug'],
|
|
6883
|
+
params: [
|
|
6884
|
+
{
|
|
6885
|
+
name: 'slug',
|
|
6886
|
+
type: 'string' as const,
|
|
6887
|
+
required: true,
|
|
6888
|
+
description: 'Slug of the existing attachment to update',
|
|
6889
|
+
},
|
|
6890
|
+
{
|
|
6891
|
+
name: 'file',
|
|
6892
|
+
type: 'string' as const,
|
|
6893
|
+
required: false,
|
|
6894
|
+
description: 'Path to a local file containing the new content',
|
|
6895
|
+
},
|
|
6896
|
+
{
|
|
6897
|
+
name: 'content',
|
|
6898
|
+
type: 'string' as const,
|
|
6899
|
+
required: false,
|
|
6900
|
+
description: 'Inline UTF-8 content (mutually exclusive with file)',
|
|
6901
|
+
},
|
|
6902
|
+
{
|
|
6903
|
+
name: 'message',
|
|
6904
|
+
type: 'string' as const,
|
|
6905
|
+
required: false,
|
|
6906
|
+
description: 'One-line summary describing the change (recorded in the audit log)',
|
|
6907
|
+
},
|
|
6908
|
+
{
|
|
6909
|
+
name: 'status',
|
|
6910
|
+
type: 'string' as const,
|
|
6911
|
+
required: false,
|
|
6912
|
+
description:
|
|
6913
|
+
'Override the new lifecycle status. Defaults to "draft" on every update. ' +
|
|
6914
|
+
'Valid: draft|proposed|accepted|superseded|archived|deprecated.',
|
|
6915
|
+
},
|
|
6916
|
+
{
|
|
6917
|
+
name: 'attachedBy',
|
|
6918
|
+
type: 'string' as const,
|
|
6919
|
+
required: false,
|
|
6920
|
+
description: 'Agent identity that performed the update (defaults to "human")',
|
|
6921
|
+
},
|
|
6922
|
+
],
|
|
6923
|
+
},
|
|
6800
6924
|
// ── playbook.* HITL CLI surface (T935) ───────────────────────────────────
|
|
6801
6925
|
{
|
|
6802
6926
|
gateway: 'mutate',
|
package/src/operations/docs.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Docs Domain Operations Contract (
|
|
2
|
+
* Docs Domain Operations Contract (7 operations)
|
|
3
3
|
*
|
|
4
4
|
* Query operations: 3
|
|
5
|
-
* Mutate operations:
|
|
5
|
+
* Mutate operations: 4
|
|
6
6
|
*
|
|
7
7
|
* Docs domain handles attachment management and document generation via `cleo docs`:
|
|
8
|
-
* - add
|
|
9
|
-
* - list
|
|
10
|
-
* - generate
|
|
11
|
-
* - fetch
|
|
12
|
-
* - remove
|
|
8
|
+
* - add — attach a local file or URL to a CLEO owner entity (task, session, observation)
|
|
9
|
+
* - list — list attachments for an owner entity
|
|
10
|
+
* - generate — generate llms.txt summary for a task/epic, optionally attach as blob
|
|
11
|
+
* - fetch — retrieve attachment bytes and metadata by ID or SHA-256
|
|
12
|
+
* - remove — remove an attachment ref; purges blob when refCount hits zero
|
|
13
|
+
* - update — UPDATE-in-place via slug (T10161)
|
|
14
|
+
* - supersede — atomically flip an older doc to `superseded` and link it to its successor (T10162)
|
|
13
15
|
*
|
|
14
16
|
* Owner type is auto-detected from the owner ID prefix:
|
|
15
17
|
* T### → 'task'
|
|
@@ -398,6 +400,15 @@ export interface DocsAddParams {
|
|
|
398
400
|
* @task T9636 (T-DOCS-SLUG-1)
|
|
399
401
|
*/
|
|
400
402
|
slug?: string;
|
|
403
|
+
/**
|
|
404
|
+
* Optional human-readable title — used to derive the kebab-slug tail when
|
|
405
|
+
* auto-allocating an ADR slug. REQUIRED when `type === 'adr'` AND `slug`
|
|
406
|
+
* is omitted; ignored otherwise. The allocator slugifies the title via
|
|
407
|
+
* the shared kebabize helper.
|
|
408
|
+
*
|
|
409
|
+
* @task T10360 (closes T10153)
|
|
410
|
+
*/
|
|
411
|
+
title?: string;
|
|
401
412
|
/**
|
|
402
413
|
* Optional taxonomy classification. Must match one of
|
|
403
414
|
* {@link DOCS_TYPE_VALUES}; an invalid value returns `E_INVALID_TYPE`.
|
|
@@ -447,6 +458,13 @@ export interface DocsAddResult {
|
|
|
447
458
|
slug?: string;
|
|
448
459
|
/** Type classification recorded for this attachment, when provided (T9637). */
|
|
449
460
|
type?: DocsType;
|
|
461
|
+
/**
|
|
462
|
+
* Auto-allocated ADR number, present iff the slug was minted by
|
|
463
|
+
* {@link allocateAdrSlug} (i.e. `--type adr` without `--slug`).
|
|
464
|
+
*
|
|
465
|
+
* @task T10360
|
|
466
|
+
*/
|
|
467
|
+
adrNumber?: number;
|
|
450
468
|
}
|
|
451
469
|
|
|
452
470
|
// --------------------------------------------------------------------------
|
|
@@ -481,6 +499,157 @@ export interface DocsRemoveResult {
|
|
|
481
499
|
attachmentBackend?: AttachmentBackend;
|
|
482
500
|
}
|
|
483
501
|
|
|
502
|
+
// --------------------------------------------------------------------------
|
|
503
|
+
// docs.update — UPDATE-in-place via slug (T10161)
|
|
504
|
+
// --------------------------------------------------------------------------
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Allowed `lifecycle_status` values mirrored from the attachments-table enum
|
|
508
|
+
* (`ATTACHMENT_LIFECYCLE_STATUSES` in
|
|
509
|
+
* `packages/core/src/store/schema/attachments.ts`). Kept inline here so the
|
|
510
|
+
* contract surface stays self-contained — the dispatch handler narrows raw
|
|
511
|
+
* `--status` input against this set before touching the store.
|
|
512
|
+
*
|
|
513
|
+
* @task T10161 (Epic T10157 / Saga T9855)
|
|
514
|
+
*/
|
|
515
|
+
export type DocsLifecycleStatus =
|
|
516
|
+
| 'draft'
|
|
517
|
+
| 'proposed'
|
|
518
|
+
| 'accepted'
|
|
519
|
+
| 'superseded'
|
|
520
|
+
| 'archived'
|
|
521
|
+
| 'deprecated';
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Parameters for `docs.update`.
|
|
525
|
+
*
|
|
526
|
+
* Replaces the blob content for an existing slug — the slug is transferred
|
|
527
|
+
* from the old `attachments` row to a new row carrying the new sha256, and
|
|
528
|
+
* the old row remains reachable by attachment-id for history. Unlike
|
|
529
|
+
* {@link DocsSupersedeParams}-style flows, NO supersession edge is created
|
|
530
|
+
* (callers wanting an explicit lineage edge should use `cleo docs supersede`).
|
|
531
|
+
*
|
|
532
|
+
* Exactly one of `file` or `content` MUST be provided.
|
|
533
|
+
*
|
|
534
|
+
* @task T10161 (Epic T10157 / Saga T9855 — E12.C4)
|
|
535
|
+
*/
|
|
536
|
+
export interface DocsUpdateParams {
|
|
537
|
+
/** Slug of the existing attachment to update (required). */
|
|
538
|
+
slug: string;
|
|
539
|
+
/** Path to a local file containing the new content. */
|
|
540
|
+
file?: string;
|
|
541
|
+
/** Inline UTF-8 content (mutually exclusive with `file`). */
|
|
542
|
+
content?: string;
|
|
543
|
+
/** Optional one-line summary describing the change (recorded in the audit log). */
|
|
544
|
+
message?: string;
|
|
545
|
+
/**
|
|
546
|
+
* Override the new lifecycle status. Defaults to `'draft'` on every update
|
|
547
|
+
* so an explicit `accepted` doc gets back-pressured to draft on edit. Pass
|
|
548
|
+
* `--status accepted` (or any other valid status) to override.
|
|
549
|
+
*/
|
|
550
|
+
status?: DocsLifecycleStatus;
|
|
551
|
+
/** Agent identity that performed the update (default: `'human'`). */
|
|
552
|
+
attachedBy?: string;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Result of `docs.update`.
|
|
557
|
+
*
|
|
558
|
+
* Identifies the new row (active) and the row it replaced. When the supplied
|
|
559
|
+
* content is byte-identical to the current content the operation is a noop —
|
|
560
|
+
* `previousSha256` will equal `sha256` and `changed` will be `false`.
|
|
561
|
+
*
|
|
562
|
+
* @task T10161
|
|
563
|
+
*/
|
|
564
|
+
export interface DocsUpdateResult {
|
|
565
|
+
/** Slug that now points at the new content. */
|
|
566
|
+
slug: string;
|
|
567
|
+
/** Type/kind of the row, preserved from the prior version. */
|
|
568
|
+
type?: DocsType;
|
|
569
|
+
/** New attachment ID now bearing the slug. */
|
|
570
|
+
attachmentId: string;
|
|
571
|
+
/** Previous attachment ID; retains its row + bytes for history. */
|
|
572
|
+
previousAttachmentId: string;
|
|
573
|
+
/** SHA-256 hex of the new content. */
|
|
574
|
+
sha256: string;
|
|
575
|
+
/** SHA-256 hex of the prior content (equals `sha256` on a noop). */
|
|
576
|
+
previousSha256: string;
|
|
577
|
+
/** Whether the bytes actually changed (false ⇒ noop). */
|
|
578
|
+
changed: boolean;
|
|
579
|
+
/** Lifecycle status now stored on the new row. */
|
|
580
|
+
lifecycleStatus: DocsLifecycleStatus;
|
|
581
|
+
/** ISO 8601 timestamp the new row was registered. */
|
|
582
|
+
updatedAt: string;
|
|
583
|
+
/**
|
|
584
|
+
* Best-effort 1-indexed version number for the slug (count of historical
|
|
585
|
+
* rows that ever carried this slug, including the current one). When the
|
|
586
|
+
* underlying store cannot enumerate history this falls back to `2` for the
|
|
587
|
+
* first update and increments by 1 thereafter.
|
|
588
|
+
*/
|
|
589
|
+
version: number;
|
|
590
|
+
/**
|
|
591
|
+
* True when this update was squashed into an existing audit entry within
|
|
592
|
+
* the 5-minute squash window (no new audit line was written).
|
|
593
|
+
*/
|
|
594
|
+
squashed: boolean;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// --------------------------------------------------------------------------
|
|
598
|
+
// docs.supersede — flip an old doc to `superseded` and point at its successor
|
|
599
|
+
// --------------------------------------------------------------------------
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Parameters for `docs.supersede`.
|
|
603
|
+
*
|
|
604
|
+
* Both `oldSlug` and `newSlug` reference rows on `attachments.slug`. The
|
|
605
|
+
* operation is atomic: either both rows update (and the lifecycle flip lands)
|
|
606
|
+
* or neither does.
|
|
607
|
+
*
|
|
608
|
+
* @task T10162 (Saga T9855 · Epic T10157 · ADR-078)
|
|
609
|
+
*/
|
|
610
|
+
export interface DocsSupersedeParams {
|
|
611
|
+
/** Slug of the doc being replaced. */
|
|
612
|
+
oldSlug: string;
|
|
613
|
+
/** Slug of the doc that replaces {@link oldSlug}. */
|
|
614
|
+
newSlug: string;
|
|
615
|
+
/**
|
|
616
|
+
* Optional human-readable reason surfaced on the response envelope and
|
|
617
|
+
* preserved by future provenance-graph reads (T10166 `summary` field on
|
|
618
|
+
* the `supersedes` edge). Not persisted to a dedicated column today.
|
|
619
|
+
*/
|
|
620
|
+
reason?: string;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Result of `docs.supersede`.
|
|
625
|
+
*
|
|
626
|
+
* @task T10162 (Saga T9855 · Epic T10157 · ADR-078)
|
|
627
|
+
*/
|
|
628
|
+
export interface DocsSupersedeResult {
|
|
629
|
+
/** Slug of the doc that was just superseded. */
|
|
630
|
+
oldSlug: string;
|
|
631
|
+
/** Slug of the new doc that supersedes {@link oldSlug}. */
|
|
632
|
+
newSlug: string;
|
|
633
|
+
/** Attachment ID resolved from {@link oldSlug}. */
|
|
634
|
+
oldAttachmentId: string;
|
|
635
|
+
/** Attachment ID resolved from {@link newSlug}. */
|
|
636
|
+
newAttachmentId: string;
|
|
637
|
+
/** ISO-8601 timestamp the supersession was recorded. */
|
|
638
|
+
supersededAt: string;
|
|
639
|
+
/**
|
|
640
|
+
* Stable identifier for the lineage edge minted by this call.
|
|
641
|
+
*
|
|
642
|
+
* The edge itself is not stored in a dedicated table — it is reconstructed
|
|
643
|
+
* at read time from `attachments.supersedes` / `attachments.superseded_by`
|
|
644
|
+
* by `cleo docs provenance` (T10166). The ID is deterministic
|
|
645
|
+
* (`supersedes:<newAttachmentId>->&<oldAttachmentId>`) so concurrent callers
|
|
646
|
+
* that win the transaction race observe a stable value.
|
|
647
|
+
*/
|
|
648
|
+
edgeId: string;
|
|
649
|
+
/** Optional reason carried through from the request. */
|
|
650
|
+
reason?: string;
|
|
651
|
+
}
|
|
652
|
+
|
|
484
653
|
// ============================================================================
|
|
485
654
|
// Discriminated Union (DocsOps)
|
|
486
655
|
// ============================================================================
|
|
@@ -500,7 +669,9 @@ export type DocsOps =
|
|
|
500
669
|
| { op: 'docs.fetch'; params: DocsFetchParams; result: DocsFetchResult }
|
|
501
670
|
| { op: 'docs.generate'; params: DocsGenerateParams; result: DocsGenerateResult }
|
|
502
671
|
| { op: 'docs.add'; params: DocsAddParams; result: DocsAddResult }
|
|
503
|
-
| { op: 'docs.remove'; params: DocsRemoveParams; result: DocsRemoveResult }
|
|
672
|
+
| { op: 'docs.remove'; params: DocsRemoveParams; result: DocsRemoveResult }
|
|
673
|
+
| { op: 'docs.update'; params: DocsUpdateParams; result: DocsUpdateResult }
|
|
674
|
+
| { op: 'docs.supersede'; params: DocsSupersedeParams; result: DocsSupersedeResult };
|
|
504
675
|
|
|
505
676
|
/**
|
|
506
677
|
* Enumeration of all docs domain operation names.
|
|
@@ -509,4 +680,11 @@ export type DocsOps =
|
|
|
509
680
|
* Useful for dynamic operation dispatch, type narrowing, or documentation.
|
|
510
681
|
* Kept in sync with the `DocsOps` discriminated union above.
|
|
511
682
|
*/
|
|
512
|
-
export type DocsOp =
|
|
683
|
+
export type DocsOp =
|
|
684
|
+
| 'docs.list'
|
|
685
|
+
| 'docs.fetch'
|
|
686
|
+
| 'docs.generate'
|
|
687
|
+
| 'docs.add'
|
|
688
|
+
| 'docs.remove'
|
|
689
|
+
| 'docs.update'
|
|
690
|
+
| 'docs.supersede';
|
package/src/operations/tasks.ts
CHANGED
|
@@ -125,6 +125,19 @@ export interface TasksFindParams {
|
|
|
125
125
|
* @task T9072
|
|
126
126
|
*/
|
|
127
127
|
kind?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Unified urgency surface (T9905).
|
|
130
|
+
*
|
|
131
|
+
* When `true`, the predicate is
|
|
132
|
+
*
|
|
133
|
+
* `priority IN ('critical','high') OR severity IN ('P0','P1')`
|
|
134
|
+
*
|
|
135
|
+
* combining the two orthogonal urgency axes (priority + severity) into a
|
|
136
|
+
* single filter. Composes with other filters via AND.
|
|
137
|
+
*
|
|
138
|
+
* @task T9905
|
|
139
|
+
*/
|
|
140
|
+
urgent?: boolean;
|
|
128
141
|
}
|
|
129
142
|
export type TasksFindResult = MinimalTask[];
|
|
130
143
|
|
package/src/task-record.ts
CHANGED
|
@@ -103,4 +103,11 @@ export interface MinimalTaskRecord {
|
|
|
103
103
|
type?: string;
|
|
104
104
|
/** Scope size estimate — helps agents decide if decomposition is needed. @task T091 */
|
|
105
105
|
size?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Bug severity axis — P0|P1|P2|P3 (string-widened). Surfaced so the unified
|
|
108
|
+
* urgency surface (`cleo find --urgent`) can identify P0/P1 rows without a
|
|
109
|
+
* follow-up `cleo show` per row.
|
|
110
|
+
* @task T9905
|
|
111
|
+
*/
|
|
112
|
+
severity?: string | null;
|
|
106
113
|
}
|