@cleocode/contracts 2026.5.103 → 2026.5.105
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/boundary.js +5 -5
- package/dist/boundary.js.map +1 -1
- package/dist/errors.d.ts +61 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +46 -0
- package/dist/errors.js.map +1 -1
- package/dist/exit-codes.d.ts +13 -0
- package/dist/exit-codes.d.ts.map +1 -1
- package/dist/exit-codes.js +13 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/release/ship-e2e-smoke.d.ts +97 -0
- package/dist/release/ship-e2e-smoke.d.ts.map +1 -0
- package/dist/release/ship-e2e-smoke.js +20 -0
- package/dist/release/ship-e2e-smoke.js.map +1 -0
- package/dist/render/__tests__/envelope.test.d.ts +14 -0
- package/dist/render/__tests__/envelope.test.d.ts.map +1 -0
- package/dist/render/__tests__/envelope.test.js +178 -0
- package/dist/render/__tests__/envelope.test.js.map +1 -0
- package/dist/render/envelope.d.ts +103 -0
- package/dist/render/envelope.d.ts.map +1 -0
- package/dist/render/envelope.js +53 -0
- package/dist/render/envelope.js.map +1 -0
- package/dist/render/index.d.ts +16 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +12 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/list.d.ts +77 -0
- package/dist/render/list.d.ts.map +1 -0
- package/dist/render/list.js +39 -0
- package/dist/render/list.js.map +1 -0
- package/dist/render/table.d.ts +73 -0
- package/dist/render/table.d.ts.map +1 -0
- package/dist/render/table.js +32 -0
- package/dist/render/table.js.map +1 -0
- package/dist/render/tree.d.ts +94 -0
- package/dist/render/tree.d.ts.map +1 -0
- package/dist/render/tree.js +30 -0
- package/dist/render/tree.js.map +1 -0
- package/package.json +2 -2
- package/src/boundary.ts +5 -5
- package/src/errors.ts +71 -0
- package/src/exit-codes.ts +13 -0
- package/src/index.ts +15 -1
- package/src/release/ship-e2e-smoke.ts +113 -0
- package/src/render/__tests__/envelope.test.ts +194 -0
- package/src/render/envelope.ts +112 -0
- package/src/render/index.ts +40 -0
- package/src/render/list.ts +90 -0
- package/src/render/table.ts +85 -0
- package/src/render/tree.ts +114 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed render contracts — list (flat) and grouped-list.
|
|
3
|
+
*
|
|
4
|
+
* Part of the Human Render Contract (Epic T10114, ADR-077). The list shape is
|
|
5
|
+
* the canonical wire format for ordered or grouped sequential output —
|
|
6
|
+
* presenters render each item according to `renderItem` (or a default).
|
|
7
|
+
*
|
|
8
|
+
* @epic T10114
|
|
9
|
+
* @task T10140
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Runtime type guard for `ListResponse<T>`.
|
|
13
|
+
*
|
|
14
|
+
* Checks the envelope shape only — does not inspect item payloads against `T`.
|
|
15
|
+
*
|
|
16
|
+
* @param value — candidate value to inspect.
|
|
17
|
+
* @returns `true` iff `value` matches the `ListResponse<T>` envelope shape.
|
|
18
|
+
*/
|
|
19
|
+
export function isListResponse(value) {
|
|
20
|
+
if (typeof value !== 'object' || value === null)
|
|
21
|
+
return false;
|
|
22
|
+
const v = value;
|
|
23
|
+
return Array.isArray(v.items) && typeof v.total === 'number';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Runtime type guard for `GroupedListResponse<T>`.
|
|
27
|
+
*
|
|
28
|
+
* Checks the envelope shape only — does not inspect item payloads against `T`.
|
|
29
|
+
*
|
|
30
|
+
* @param value — candidate value to inspect.
|
|
31
|
+
* @returns `true` iff `value` matches the `GroupedListResponse<T>` envelope shape.
|
|
32
|
+
*/
|
|
33
|
+
export function isGroupedListResponse(value) {
|
|
34
|
+
if (typeof value !== 'object' || value === null)
|
|
35
|
+
return false;
|
|
36
|
+
const v = value;
|
|
37
|
+
return Array.isArray(v.groups) && typeof v.total === 'number';
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/render/list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsDH;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAI,KAAc;IAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;AAC/D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAI,KAAc;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed render contracts — table (column-schema-driven rows).
|
|
3
|
+
*
|
|
4
|
+
* Part of the Human Render Contract (Epic T10114, ADR-077). The table shape is
|
|
5
|
+
* the canonical wire format for tabular CLI output — presenters consume the
|
|
6
|
+
* schema to compute widths, headers, and per-cell formatting.
|
|
7
|
+
*
|
|
8
|
+
* @epic T10114
|
|
9
|
+
* @task T10139
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Horizontal alignment hint for a column.
|
|
13
|
+
*/
|
|
14
|
+
export type ColumnAlign = 'left' | 'right' | 'center';
|
|
15
|
+
/**
|
|
16
|
+
* Schema entry for a single table column.
|
|
17
|
+
*
|
|
18
|
+
* `key` MUST be a string-typed property on the row payload `T`. Presenters
|
|
19
|
+
* are expected to extract `row[column.key]` and pipe it through `format` when
|
|
20
|
+
* one is supplied.
|
|
21
|
+
*
|
|
22
|
+
* @typeParam T — caller-defined row payload shape.
|
|
23
|
+
*/
|
|
24
|
+
export interface TableColumn<T> {
|
|
25
|
+
/** Property key on the row payload. */
|
|
26
|
+
readonly key: keyof T & string;
|
|
27
|
+
/** Display header rendered above the column. */
|
|
28
|
+
readonly header: string;
|
|
29
|
+
/** Optional alignment hint. Defaults to `'left'`. */
|
|
30
|
+
readonly align?: ColumnAlign;
|
|
31
|
+
/** Optional fixed width (character count) hint. */
|
|
32
|
+
readonly width?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Optional per-cell formatter. Receives the raw value at `row[key]` and
|
|
35
|
+
* returns the display string. When absent, presenters call `String(value)`.
|
|
36
|
+
*/
|
|
37
|
+
readonly format?: (value: unknown) => string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Column-schema metadata that accompanies every table response.
|
|
41
|
+
*
|
|
42
|
+
* @typeParam T — caller-defined row payload shape.
|
|
43
|
+
*/
|
|
44
|
+
export interface TableSchema<T> {
|
|
45
|
+
/** Ordered list of column descriptors. Rendering order matches this order. */
|
|
46
|
+
readonly columns: ReadonlyArray<TableColumn<T>>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Top-level envelope returned by a table-shaped renderer.
|
|
50
|
+
*
|
|
51
|
+
* @typeParam T — caller-defined row payload shape.
|
|
52
|
+
*/
|
|
53
|
+
export interface TableResponse<T> {
|
|
54
|
+
/** Row payloads in the order they should be rendered. */
|
|
55
|
+
readonly rows: ReadonlyArray<T>;
|
|
56
|
+
/** Column schema — drives presenter formatting. */
|
|
57
|
+
readonly schema: TableSchema<T>;
|
|
58
|
+
/**
|
|
59
|
+
* Total row count. May exceed `rows.length` when the response is paginated
|
|
60
|
+
* or truncated; presenters use this to render "showing N of M" hints.
|
|
61
|
+
*/
|
|
62
|
+
readonly total: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Runtime type guard for `TableResponse<T>`.
|
|
66
|
+
*
|
|
67
|
+
* Checks the envelope shape only — does not inspect row payloads against `T`.
|
|
68
|
+
*
|
|
69
|
+
* @param value — candidate value to inspect.
|
|
70
|
+
* @returns `true` iff `value` matches the `TableResponse<T>` envelope shape.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isTableResponse<T>(value: unknown): value is TableResponse<T>;
|
|
73
|
+
//# sourceMappingURL=table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/render/table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEtD;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAC9C;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACjD;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAChC,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAAC,CAAC,CAAC,CAQ5E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed render contracts — table (column-schema-driven rows).
|
|
3
|
+
*
|
|
4
|
+
* Part of the Human Render Contract (Epic T10114, ADR-077). The table shape is
|
|
5
|
+
* the canonical wire format for tabular CLI output — presenters consume the
|
|
6
|
+
* schema to compute widths, headers, and per-cell formatting.
|
|
7
|
+
*
|
|
8
|
+
* @epic T10114
|
|
9
|
+
* @task T10139
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Runtime type guard for `TableResponse<T>`.
|
|
13
|
+
*
|
|
14
|
+
* Checks the envelope shape only — does not inspect row payloads against `T`.
|
|
15
|
+
*
|
|
16
|
+
* @param value — candidate value to inspect.
|
|
17
|
+
* @returns `true` iff `value` matches the `TableResponse<T>` envelope shape.
|
|
18
|
+
*/
|
|
19
|
+
export function isTableResponse(value) {
|
|
20
|
+
if (typeof value !== 'object' || value === null)
|
|
21
|
+
return false;
|
|
22
|
+
const v = value;
|
|
23
|
+
if (!Array.isArray(v.rows))
|
|
24
|
+
return false;
|
|
25
|
+
if (typeof v.total !== 'number')
|
|
26
|
+
return false;
|
|
27
|
+
const schema = v.schema;
|
|
28
|
+
if (typeof schema !== 'object' || schema === null)
|
|
29
|
+
return false;
|
|
30
|
+
return Array.isArray(schema.columns);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../src/render/table.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA2DH;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAI,KAAc;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,MAAM,GAAG,CAAC,CAAC,MAA6C,CAAC;IAC/D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed render contracts — tree (flattened hierarchy).
|
|
3
|
+
*
|
|
4
|
+
* Part of the Human Render Contract (Epic T10114, ADR-077). The flat-tree shape
|
|
5
|
+
* is the canonical wire format for hierarchical task / saga / epic listings —
|
|
6
|
+
* presenters re-materialise the parent/child structure from `parentId` + `depth`.
|
|
7
|
+
*
|
|
8
|
+
* @epic T10114
|
|
9
|
+
* @task T10138
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Discriminator for a node's place in the task hierarchy.
|
|
13
|
+
*
|
|
14
|
+
* Matches the CLEO 4-tier task model (Saga → Epic → Task → Subtask).
|
|
15
|
+
*/
|
|
16
|
+
export type TreeNodeKind = 'saga' | 'epic' | 'task' | 'subtask';
|
|
17
|
+
/**
|
|
18
|
+
* Lifecycle status of a tree node.
|
|
19
|
+
*
|
|
20
|
+
* Mirrors the canonical task status union used across CLEO.
|
|
21
|
+
*/
|
|
22
|
+
export type TreeNodeStatus = 'pending' | 'in_progress' | 'done' | 'blocked' | 'cancelled' | 'archived';
|
|
23
|
+
/**
|
|
24
|
+
* One row in a flattened tree response.
|
|
25
|
+
*
|
|
26
|
+
* `parentId === null` identifies the root. `depth` is the integer distance
|
|
27
|
+
* from the root (root is `0`). `metadata` is the caller-defined payload —
|
|
28
|
+
* presenters MUST NOT assume any specific shape beyond `T`.
|
|
29
|
+
*
|
|
30
|
+
* @typeParam T — caller-defined metadata payload attached to the node.
|
|
31
|
+
*/
|
|
32
|
+
export interface FlatTreeNode<T> {
|
|
33
|
+
/** Stable, unique identifier (typically a CLEO task ID such as `T1234`). */
|
|
34
|
+
readonly id: string;
|
|
35
|
+
/** Parent identifier, or `null` if this row is the root. */
|
|
36
|
+
readonly parentId: string | null;
|
|
37
|
+
/** Integer distance from the root (root = `0`). */
|
|
38
|
+
readonly depth: number;
|
|
39
|
+
/** Discriminator slot in the CLEO 4-tier model. */
|
|
40
|
+
readonly kind: TreeNodeKind;
|
|
41
|
+
/** Lifecycle status. */
|
|
42
|
+
readonly status: TreeNodeStatus;
|
|
43
|
+
/** Display title — short enough to render on a single line. */
|
|
44
|
+
readonly title: string;
|
|
45
|
+
/** Caller-defined metadata payload. */
|
|
46
|
+
readonly metadata: T;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Top-level envelope returned by a tree-shaped renderer.
|
|
50
|
+
*
|
|
51
|
+
* @typeParam T — caller-defined metadata payload attached to each node.
|
|
52
|
+
*/
|
|
53
|
+
export interface TreeResponse<T> {
|
|
54
|
+
/** Flattened list of nodes in pre-order traversal. */
|
|
55
|
+
readonly tree: ReadonlyArray<FlatTreeNode<T>>;
|
|
56
|
+
/** Identifier of the root node — must match a node in `tree` with `parentId === null`. */
|
|
57
|
+
readonly root: string;
|
|
58
|
+
/** Total number of nodes — equal to `tree.length`. */
|
|
59
|
+
readonly totalNodes: number;
|
|
60
|
+
/** Maximum `depth` seen across `tree`. `0` for a single-node tree. */
|
|
61
|
+
readonly maxDepth: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Caller-supplied options that shape a tree response.
|
|
65
|
+
*
|
|
66
|
+
* All fields are optional. Presenters interpret unset fields as "no constraint"
|
|
67
|
+
* (e.g. unset `depth` means render the full tree).
|
|
68
|
+
*/
|
|
69
|
+
export interface RenderTreeOptions {
|
|
70
|
+
/** Cap the maximum depth rendered. */
|
|
71
|
+
readonly depth?: number;
|
|
72
|
+
/** Restrict the rendered node kinds. */
|
|
73
|
+
readonly kinds?: ReadonlyArray<TreeNodeKind>;
|
|
74
|
+
/** When `true`, presenters may emit unicode icons next to each row. */
|
|
75
|
+
readonly icons?: boolean;
|
|
76
|
+
/** When set, collapse subtrees beyond this child-count threshold. */
|
|
77
|
+
readonly foldAt?: number;
|
|
78
|
+
/** When `true`, presenters MAY annotate inter-task dependencies. */
|
|
79
|
+
readonly withDeps?: boolean;
|
|
80
|
+
/** When `true`, presenters MAY annotate blocker relationships. */
|
|
81
|
+
readonly withBlockers?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Runtime type guard for `TreeResponse<T>`.
|
|
85
|
+
*
|
|
86
|
+
* Verifies the envelope shape without inspecting node payloads — the generic
|
|
87
|
+
* `T` cannot be checked at runtime, so callers must layer per-node validation
|
|
88
|
+
* separately if they need stronger guarantees.
|
|
89
|
+
*
|
|
90
|
+
* @param value — candidate value to inspect.
|
|
91
|
+
* @returns `true` iff `value` matches the `TreeResponse<T>` envelope shape.
|
|
92
|
+
*/
|
|
93
|
+
export declare function isTreeResponse<T>(value: unknown): value is TreeResponse<T>;
|
|
94
|
+
//# sourceMappingURL=tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/render/tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAEhE;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,aAAa,GACb,MAAM,GACN,SAAS,GACT,WAAW,GACX,UAAU,CAAC;AAEf;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAC7C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAAC,CAAC,CAAC,CAS1E"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed render contracts — tree (flattened hierarchy).
|
|
3
|
+
*
|
|
4
|
+
* Part of the Human Render Contract (Epic T10114, ADR-077). The flat-tree shape
|
|
5
|
+
* is the canonical wire format for hierarchical task / saga / epic listings —
|
|
6
|
+
* presenters re-materialise the parent/child structure from `parentId` + `depth`.
|
|
7
|
+
*
|
|
8
|
+
* @epic T10114
|
|
9
|
+
* @task T10138
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Runtime type guard for `TreeResponse<T>`.
|
|
13
|
+
*
|
|
14
|
+
* Verifies the envelope shape without inspecting node payloads — the generic
|
|
15
|
+
* `T` cannot be checked at runtime, so callers must layer per-node validation
|
|
16
|
+
* separately if they need stronger guarantees.
|
|
17
|
+
*
|
|
18
|
+
* @param value — candidate value to inspect.
|
|
19
|
+
* @returns `true` iff `value` matches the `TreeResponse<T>` envelope shape.
|
|
20
|
+
*/
|
|
21
|
+
export function isTreeResponse(value) {
|
|
22
|
+
if (typeof value !== 'object' || value === null)
|
|
23
|
+
return false;
|
|
24
|
+
const v = value;
|
|
25
|
+
return (Array.isArray(v.tree) &&
|
|
26
|
+
typeof v.root === 'string' &&
|
|
27
|
+
typeof v.totalNodes === 'number' &&
|
|
28
|
+
typeof v.maxDepth === 'number');
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../../src/render/tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAqFH;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAI,KAAc;IAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QACrB,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;QAChC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAC/B,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleocode/contracts",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.105",
|
|
4
4
|
"description": "Domain types, interfaces, and contracts for the CLEO ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"dependencies": {
|
|
139
139
|
"zod": "^4.3.6",
|
|
140
140
|
"zod-to-json-schema": "^3.25.2",
|
|
141
|
-
"@cleocode/lafs": "2026.5.
|
|
141
|
+
"@cleocode/lafs": "2026.5.105"
|
|
142
142
|
},
|
|
143
143
|
"scripts": {
|
|
144
144
|
"build": "tsc -b --force && node scripts/emit-schemas.mjs",
|
package/src/boundary.ts
CHANGED
|
@@ -318,9 +318,9 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
318
318
|
'Verified-dead code: 531 LOC Rust crate referenced ONLY by packages/core/src/llm/rust/__tests__/. Production transports (chat-completions/gemini/openai) import StreamingThinkScrubber from the TS think-scrubber.js path. CLEO_USE_RUST gate is never set in any workflow. Scheduled for deletion in T10205; see ADR-078 implementation tasks.',
|
|
319
319
|
},
|
|
320
320
|
{
|
|
321
|
-
module: 'conduit-core',
|
|
321
|
+
module: 'cleo-conduit-core',
|
|
322
322
|
intent: 'data-manifest',
|
|
323
|
-
rustCore: 'crates/conduit-core',
|
|
323
|
+
rustCore: 'crates/cleo-conduit-core',
|
|
324
324
|
canonicalHome: 'cleocode',
|
|
325
325
|
perfBudget: {
|
|
326
326
|
latency_p50_ms: 10,
|
|
@@ -331,7 +331,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
331
331
|
},
|
|
332
332
|
amendments: [],
|
|
333
333
|
rationale:
|
|
334
|
-
'Conduit serde-types crate (681 LOC) —
|
|
334
|
+
'Conduit serde-types crate (681 LOC) — renamed from conduit-core to cleo-conduit-core (T10185, saga T10180) because conduit-core is squatted on crates.io. Stable contract surface consumed by cant-core, signaldock-protocol, signaldock-core, integration-tests, AND external /mnt/projects/signaldock-core + /mnt/projects/signaldock/backend via git deps. Mirrors @cleocode/contracts/conduit.ts. Forced KEEP+publish=true: deleting breaks production, and signaldock-protocol publish requires it.',
|
|
335
335
|
},
|
|
336
336
|
{
|
|
337
337
|
module: 'integration-tests',
|
|
@@ -348,7 +348,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
348
348
|
},
|
|
349
349
|
amendments: [],
|
|
350
350
|
rationale:
|
|
351
|
-
'Internal Rust test harness (241 LOC) over lafs-core/conduit-core/cant-core/cant-router/signaldock-core. publish=false intentional — test code never ships.',
|
|
351
|
+
'Internal Rust test harness (241 LOC) over lafs-core/cleo-conduit-core/cant-core/cant-router/signaldock-core. publish=false intentional — test code never ships.',
|
|
352
352
|
},
|
|
353
353
|
{
|
|
354
354
|
module: 'lafs-core',
|
|
@@ -366,7 +366,7 @@ export const BOUNDARY_REGISTRY: readonly BoundaryEntry[] = [
|
|
|
366
366
|
},
|
|
367
367
|
amendments: [],
|
|
368
368
|
rationale:
|
|
369
|
-
'LAFS envelope serialization core (1,432 LOC) consumed by conduit-core, signaldock-protocol, lafs-napi, integration-tests. Mirrors @cleocode/lafs/src/envelope.ts. Flip publish=true to expose canonical envelope spec as a Rust library.',
|
|
369
|
+
'LAFS envelope serialization core (1,432 LOC) consumed by cleo-conduit-core, signaldock-protocol, lafs-napi, integration-tests. Mirrors @cleocode/lafs/src/envelope.ts. Flip publish=true to expose canonical envelope spec as a Rust library.',
|
|
370
370
|
},
|
|
371
371
|
{
|
|
372
372
|
module: 'lafs-napi',
|
package/src/errors.ts
CHANGED
|
@@ -519,3 +519,74 @@ export function isErrorResult(result: {
|
|
|
519
519
|
}): result is { success: false; error: string } {
|
|
520
520
|
return !result.success;
|
|
521
521
|
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Structured details payload attached to {@link ChangesetYamlInvalidError}.
|
|
525
|
+
*
|
|
526
|
+
* Carries the offending file path, the 1-based line number where the YAML
|
|
527
|
+
* parser bailed (when determinable), and an optional snippet of the parsed
|
|
528
|
+
* region to surface in CLI output and CI logs.
|
|
529
|
+
*
|
|
530
|
+
* @task T10105
|
|
531
|
+
* @epic E-RELEASE-PLAN-CHANGELOG
|
|
532
|
+
*/
|
|
533
|
+
export interface ChangesetYamlInvalidDetails {
|
|
534
|
+
/** Absolute or process-relative path to the offending `.md` file. */
|
|
535
|
+
file: string;
|
|
536
|
+
/** 1-based line number reported by the YAML parser; `null` when unknown. */
|
|
537
|
+
line: number | null;
|
|
538
|
+
/** Optional short excerpt around the failure (≤ 120 chars). */
|
|
539
|
+
snippet?: string;
|
|
540
|
+
/** Underlying parser message, propagated verbatim for diagnostics. */
|
|
541
|
+
parserMessage: string;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Thrown when `cleo release plan` (or the lint script) encounters a `.changeset/*.md`
|
|
546
|
+
* file whose YAML frontmatter cannot be parsed.
|
|
547
|
+
*
|
|
548
|
+
* Replaces the pre-T10105 silent-skip behaviour where a single malformed
|
|
549
|
+
* file (e.g. an unquoted colon in `summary:`) caused the aggregator to
|
|
550
|
+
* silently drop ALL changeset entries for that release. The v2026.5.100
|
|
551
|
+
* ship lost the v5.100/v5.101/v5.103 CHANGELOG entries to exactly this
|
|
552
|
+
* vector — see Saga T10099 SG-RELEASE-AUDIT-V2.
|
|
553
|
+
*
|
|
554
|
+
* @remarks
|
|
555
|
+
* Carries `exitCode` aligned with {@link ExitCode.VALIDATION_ERROR} (6) so
|
|
556
|
+
* the CLI surfaces a deterministic non-zero exit when invoked from CI.
|
|
557
|
+
*
|
|
558
|
+
* @example
|
|
559
|
+
* ```typescript
|
|
560
|
+
* throw new ChangesetYamlInvalidError({
|
|
561
|
+
* file: '.changeset/bad.md',
|
|
562
|
+
* line: 4,
|
|
563
|
+
* parserMessage: 'Nested mappings are not allowed in compact mappings',
|
|
564
|
+
* });
|
|
565
|
+
* ```
|
|
566
|
+
*
|
|
567
|
+
* @task T10105
|
|
568
|
+
* @epic E-RELEASE-PLAN-CHANGELOG
|
|
569
|
+
* @saga T10099
|
|
570
|
+
*/
|
|
571
|
+
export class ChangesetYamlInvalidError extends Error {
|
|
572
|
+
/** Stable LAFS error code string for envelope emission. */
|
|
573
|
+
readonly code = 'E_CHANGESET_YAML_INVALID';
|
|
574
|
+
/** Numeric exit code aligned with {@link ExitCode.VALIDATION_ERROR} (6). */
|
|
575
|
+
readonly exitCode: ExitCode = ExitCode.VALIDATION_ERROR;
|
|
576
|
+
/** Structured payload describing the offending file + location. */
|
|
577
|
+
readonly details: ChangesetYamlInvalidDetails;
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* @param details - Structured failure context including file path and
|
|
581
|
+
* line number. The constructor builds a deterministic human-readable
|
|
582
|
+
* message of the form `<file>:<line> invalid YAML frontmatter: <msg>`.
|
|
583
|
+
*/
|
|
584
|
+
constructor(details: ChangesetYamlInvalidDetails) {
|
|
585
|
+
const linePart = details.line !== null ? `:${details.line}` : '';
|
|
586
|
+
super(
|
|
587
|
+
`E_CHANGESET_YAML_INVALID: ${details.file}${linePart} invalid YAML frontmatter: ${details.parserMessage}`,
|
|
588
|
+
);
|
|
589
|
+
this.name = 'ChangesetYamlInvalidError';
|
|
590
|
+
this.details = details;
|
|
591
|
+
}
|
|
592
|
+
}
|
package/src/exit-codes.ts
CHANGED
|
@@ -266,6 +266,19 @@ export const E_EVIDENCE_INSUFFICIENT = 'E_EVIDENCE_INSUFFICIENT' as const;
|
|
|
266
266
|
export const E_PLAN_NOT_FOUND = 'E_PLAN_NOT_FOUND' as const;
|
|
267
267
|
/** Generic release-plan validation failure (e.g. schema). Exit code 6. */
|
|
268
268
|
export const E_RELEASE_PLAN_INVALID = 'E_RELEASE_PLAN_INVALID' as const;
|
|
269
|
+
/**
|
|
270
|
+
* `cleo release plan` (or the lint script) encountered a `.changeset/*.md`
|
|
271
|
+
* file whose YAML frontmatter cannot be parsed. Exit code 6 (VALIDATION_ERROR).
|
|
272
|
+
*
|
|
273
|
+
* Replaces the pre-T10105 silent-skip that caused the v2026.5.100 ship to
|
|
274
|
+
* lose v5.100/v5.101/v5.103 CHANGELOG entries when a single changeset had
|
|
275
|
+
* an unquoted colon in `summary:`.
|
|
276
|
+
*
|
|
277
|
+
* @task T10105
|
|
278
|
+
* @epic E-RELEASE-PLAN-CHANGELOG
|
|
279
|
+
* @saga T10099
|
|
280
|
+
*/
|
|
281
|
+
export const E_CHANGESET_YAML_INVALID = 'E_CHANGESET_YAML_INVALID' as const;
|
|
269
282
|
/**
|
|
270
283
|
* Release row exists but is not in the status FSM state required by the
|
|
271
284
|
* caller (R-051 — `cleo release open` requires status='planned'). Exit code 6.
|
package/src/index.ts
CHANGED
|
@@ -349,9 +349,10 @@ export {
|
|
|
349
349
|
TASK_SEVERITIES,
|
|
350
350
|
TASK_SIZES,
|
|
351
351
|
} from './enums.js';
|
|
352
|
-
export type { SagaInvariantErrorCode } from './errors.js';
|
|
352
|
+
export type { ChangesetYamlInvalidDetails, SagaInvariantErrorCode } from './errors.js';
|
|
353
353
|
// === Error Utilities ===
|
|
354
354
|
export {
|
|
355
|
+
ChangesetYamlInvalidError,
|
|
355
356
|
ClassifierUnregisteredAgentError,
|
|
356
357
|
createErrorResult,
|
|
357
358
|
createSuccessResult,
|
|
@@ -397,6 +398,8 @@ export {
|
|
|
397
398
|
} from './evidence-record-schema.js';
|
|
398
399
|
// === Exit Codes ===
|
|
399
400
|
export {
|
|
401
|
+
// T10105 / Saga T10099 — fail-loud changeset parse
|
|
402
|
+
E_CHANGESET_YAML_INVALID,
|
|
400
403
|
// SPEC-T9345 release pipeline v2 error code names (T9525)
|
|
401
404
|
E_CHANNEL_MISMATCH,
|
|
402
405
|
E_DIRTY_TREE,
|
|
@@ -1480,6 +1483,15 @@ export {
|
|
|
1480
1483
|
TASK_KIND,
|
|
1481
1484
|
TaskKindSchema,
|
|
1482
1485
|
} from './release/plan.js';
|
|
1486
|
+
// === Release ship-e2e-smoke (T10103) ===
|
|
1487
|
+
export type {
|
|
1488
|
+
ShipE2eSmokeFinalState,
|
|
1489
|
+
ShipE2eSmokeParams,
|
|
1490
|
+
ShipE2eSmokeResult,
|
|
1491
|
+
ShipE2eSmokeStep,
|
|
1492
|
+
ShipE2eSmokeStepName,
|
|
1493
|
+
ShipE2eSmokeStepStatus,
|
|
1494
|
+
} from './release/ship-e2e-smoke.js';
|
|
1483
1495
|
// === Release Version Bump ===
|
|
1484
1496
|
export type {
|
|
1485
1497
|
BumpResult,
|
|
@@ -1490,6 +1502,8 @@ export type {
|
|
|
1490
1502
|
VersionBumpTarget,
|
|
1491
1503
|
VersionBumpTargetSource,
|
|
1492
1504
|
} from './release/version-bump.js';
|
|
1505
|
+
// === Human Render Contract (Epic T10114, ADR-077) ===
|
|
1506
|
+
export * from './render/index.js';
|
|
1493
1507
|
// === Result Types (Dashboard, Stats, Log, Context, Sequence, Analysis, Deps) ===
|
|
1494
1508
|
export type {
|
|
1495
1509
|
BottleneckTask,
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ship E2E Smoke contracts — types for the `cleo release ship-e2e-smoke`
|
|
3
|
+
* one-shot walker that exercises the full release lifecycle end-to-end.
|
|
4
|
+
*
|
|
5
|
+
* The smoke runs `plan` → `open` → wait-for-PR-merge → wait-for-tag →
|
|
6
|
+
* verify-npm-published. Each step records its status, duration, and any
|
|
7
|
+
* side-effect identifier (PR number, tag SHA, npm version) for downstream
|
|
8
|
+
* inspection. The aggregate envelope reports the final lifecycle state
|
|
9
|
+
* reached so failures can be resumed.
|
|
10
|
+
*
|
|
11
|
+
* Dry-run is the default; `--execute` flips the runner to perform real
|
|
12
|
+
* mutations. Both modes return the same envelope shape so consumers can
|
|
13
|
+
* diff a dry-run preview against a real run.
|
|
14
|
+
*
|
|
15
|
+
* @task T10103
|
|
16
|
+
* @epic E-CLEO-RELEASE-VERBS
|
|
17
|
+
* @saga T10099
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The discrete steps the smoke walker executes, in order. The aggregate
|
|
22
|
+
* envelope records one entry per step with status + duration.
|
|
23
|
+
*/
|
|
24
|
+
export type ShipE2eSmokeStepName =
|
|
25
|
+
| 'plan'
|
|
26
|
+
| 'open'
|
|
27
|
+
| 'wait-for-pr'
|
|
28
|
+
| 'wait-for-tag'
|
|
29
|
+
| 'verify-npm-published';
|
|
30
|
+
|
|
31
|
+
/** Outcome of a single smoke step. */
|
|
32
|
+
export type ShipE2eSmokeStepStatus =
|
|
33
|
+
/** Step completed successfully (or, in dry-run, would have run). */
|
|
34
|
+
| 'ok'
|
|
35
|
+
/** Step was skipped — e.g. dry-run mode shortcuts post-plan. */
|
|
36
|
+
| 'skipped'
|
|
37
|
+
/** Step failed; the smoke envelope's success flag will be false. */
|
|
38
|
+
| 'failed';
|
|
39
|
+
|
|
40
|
+
/** Per-step result attached to the smoke envelope. */
|
|
41
|
+
export interface ShipE2eSmokeStep {
|
|
42
|
+
/** The step name. */
|
|
43
|
+
name: ShipE2eSmokeStepName;
|
|
44
|
+
/** Outcome. */
|
|
45
|
+
status: ShipE2eSmokeStepStatus;
|
|
46
|
+
/** Wall-clock duration in milliseconds. */
|
|
47
|
+
durationMs: number;
|
|
48
|
+
/**
|
|
49
|
+
* Optional human-readable detail string captured at step completion
|
|
50
|
+
* (e.g. "plan written to .cleo/release/v2026.5.104.plan.json",
|
|
51
|
+
* "PR #524 merged", "tag v2026.5.104 found", "npm @cleocode/cleo@…
|
|
52
|
+
* published").
|
|
53
|
+
*/
|
|
54
|
+
detail?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Optional error message when `status === 'failed'`. Single-line so
|
|
57
|
+
* the envelope stays compact for CLI consumers.
|
|
58
|
+
*/
|
|
59
|
+
error?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Final lifecycle state the smoke reached. Mirrors the `releases.status`
|
|
64
|
+
* column values plus a synthetic `npm-published` terminal state captured
|
|
65
|
+
* by the verify step.
|
|
66
|
+
*/
|
|
67
|
+
export type ShipE2eSmokeFinalState =
|
|
68
|
+
| 'not-started'
|
|
69
|
+
| 'planned'
|
|
70
|
+
| 'pr-opened'
|
|
71
|
+
| 'pr-merged'
|
|
72
|
+
| 'tag-pushed'
|
|
73
|
+
| 'npm-published';
|
|
74
|
+
|
|
75
|
+
/** Parameters accepted by the smoke walker. */
|
|
76
|
+
export interface ShipE2eSmokeParams {
|
|
77
|
+
/** Candidate release version (e.g. "v2026.6.0" or "2026.6.0"). */
|
|
78
|
+
version: string;
|
|
79
|
+
/** Epic task ID that scopes the release (forwarded to `release plan`). */
|
|
80
|
+
epicId: string;
|
|
81
|
+
/**
|
|
82
|
+
* When false (default), no side effects — every step reports what it
|
|
83
|
+
* WOULD do. When true, each step performs its real mutation.
|
|
84
|
+
*/
|
|
85
|
+
execute: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Total wall-clock budget across all polling waits, in milliseconds.
|
|
88
|
+
* Default 30 minutes when omitted. Per-step waits MUST respect this
|
|
89
|
+
* shared budget so a stuck PR doesn't hang the whole smoke.
|
|
90
|
+
*/
|
|
91
|
+
totalTimeoutMs?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Poll interval for wait-* steps, in milliseconds. Default 5_000.
|
|
94
|
+
* Lowered in tests via DI to keep the suite fast.
|
|
95
|
+
*/
|
|
96
|
+
pollIntervalMs?: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Top-level envelope returned by the smoke walker. */
|
|
100
|
+
export interface ShipE2eSmokeResult {
|
|
101
|
+
/** True when every step completed (or was intentionally skipped). */
|
|
102
|
+
success: boolean;
|
|
103
|
+
/** Echo of the version under smoke. */
|
|
104
|
+
version: string;
|
|
105
|
+
/** True when --execute was passed; false for dry-run preview. */
|
|
106
|
+
executed: boolean;
|
|
107
|
+
/** Ordered step log. */
|
|
108
|
+
steps: ShipE2eSmokeStep[];
|
|
109
|
+
/** Lifecycle state at smoke termination. */
|
|
110
|
+
finalState: ShipE2eSmokeFinalState;
|
|
111
|
+
/** Total wall-clock duration across all steps, in milliseconds. */
|
|
112
|
+
totalDurationMs: number;
|
|
113
|
+
}
|