@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.
Files changed (54) hide show
  1. package/dist/boundary.js +5 -5
  2. package/dist/boundary.js.map +1 -1
  3. package/dist/errors.d.ts +61 -0
  4. package/dist/errors.d.ts.map +1 -1
  5. package/dist/errors.js +46 -0
  6. package/dist/errors.js.map +1 -1
  7. package/dist/exit-codes.d.ts +13 -0
  8. package/dist/exit-codes.d.ts.map +1 -1
  9. package/dist/exit-codes.js +13 -0
  10. package/dist/exit-codes.js.map +1 -1
  11. package/dist/index.d.ts +5 -3
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +5 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/release/ship-e2e-smoke.d.ts +97 -0
  16. package/dist/release/ship-e2e-smoke.d.ts.map +1 -0
  17. package/dist/release/ship-e2e-smoke.js +20 -0
  18. package/dist/release/ship-e2e-smoke.js.map +1 -0
  19. package/dist/render/__tests__/envelope.test.d.ts +14 -0
  20. package/dist/render/__tests__/envelope.test.d.ts.map +1 -0
  21. package/dist/render/__tests__/envelope.test.js +178 -0
  22. package/dist/render/__tests__/envelope.test.js.map +1 -0
  23. package/dist/render/envelope.d.ts +103 -0
  24. package/dist/render/envelope.d.ts.map +1 -0
  25. package/dist/render/envelope.js +53 -0
  26. package/dist/render/envelope.js.map +1 -0
  27. package/dist/render/index.d.ts +16 -0
  28. package/dist/render/index.d.ts.map +1 -0
  29. package/dist/render/index.js +12 -0
  30. package/dist/render/index.js.map +1 -0
  31. package/dist/render/list.d.ts +77 -0
  32. package/dist/render/list.d.ts.map +1 -0
  33. package/dist/render/list.js +39 -0
  34. package/dist/render/list.js.map +1 -0
  35. package/dist/render/table.d.ts +73 -0
  36. package/dist/render/table.d.ts.map +1 -0
  37. package/dist/render/table.js +32 -0
  38. package/dist/render/table.js.map +1 -0
  39. package/dist/render/tree.d.ts +94 -0
  40. package/dist/render/tree.d.ts.map +1 -0
  41. package/dist/render/tree.js +30 -0
  42. package/dist/render/tree.js.map +1 -0
  43. package/package.json +2 -2
  44. package/src/boundary.ts +5 -5
  45. package/src/errors.ts +71 -0
  46. package/src/exit-codes.ts +13 -0
  47. package/src/index.ts +15 -1
  48. package/src/release/ship-e2e-smoke.ts +113 -0
  49. package/src/render/__tests__/envelope.test.ts +194 -0
  50. package/src/render/envelope.ts +112 -0
  51. package/src/render/index.ts +40 -0
  52. package/src/render/list.ts +90 -0
  53. package/src/render/table.ts +85 -0
  54. 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.103",
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.103"
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) — 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.',
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
+ }