@cleocode/contracts 2026.5.111 → 2026.5.113

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 (118) hide show
  1. package/dist/__tests__/cli-meta-suggested-next.test.d.ts +21 -0
  2. package/dist/__tests__/cli-meta-suggested-next.test.d.ts.map +1 -0
  3. package/dist/__tests__/cli-meta-suggested-next.test.js +70 -0
  4. package/dist/__tests__/cli-meta-suggested-next.test.js.map +1 -0
  5. package/dist/__tests__/evidence-atom-schema.test.d.ts +16 -0
  6. package/dist/__tests__/evidence-atom-schema.test.d.ts.map +1 -0
  7. package/dist/__tests__/evidence-atom-schema.test.js +394 -0
  8. package/dist/__tests__/evidence-atom-schema.test.js.map +1 -0
  9. package/dist/config/__tests__/manifest.test.d.ts +13 -0
  10. package/dist/config/__tests__/manifest.test.d.ts.map +1 -0
  11. package/dist/config/__tests__/manifest.test.js +143 -0
  12. package/dist/config/__tests__/manifest.test.js.map +1 -0
  13. package/dist/config/manifest.d.ts +158 -0
  14. package/dist/config/manifest.d.ts.map +1 -0
  15. package/dist/config/manifest.js +123 -0
  16. package/dist/config/manifest.js.map +1 -0
  17. package/dist/docs/__tests__/provenance.test.d.ts +14 -0
  18. package/dist/docs/__tests__/provenance.test.d.ts.map +1 -0
  19. package/dist/docs/__tests__/provenance.test.js +579 -0
  20. package/dist/docs/__tests__/provenance.test.js.map +1 -0
  21. package/dist/docs/provenance.d.ts +731 -0
  22. package/dist/docs/provenance.d.ts.map +1 -0
  23. package/dist/docs/provenance.js +258 -0
  24. package/dist/docs/provenance.js.map +1 -0
  25. package/dist/docs-taxonomy.d.ts +24 -0
  26. package/dist/docs-taxonomy.d.ts.map +1 -1
  27. package/dist/docs-taxonomy.js +27 -0
  28. package/dist/docs-taxonomy.js.map +1 -1
  29. package/dist/doctor.d.ts +101 -0
  30. package/dist/doctor.d.ts.map +1 -1
  31. package/dist/errors.d.ts +4 -0
  32. package/dist/errors.d.ts.map +1 -1
  33. package/dist/errors.js +4 -0
  34. package/dist/errors.js.map +1 -1
  35. package/dist/evidence-atom-schema.d.ts +407 -0
  36. package/dist/evidence-atom-schema.d.ts.map +1 -0
  37. package/dist/evidence-atom-schema.js +505 -0
  38. package/dist/evidence-atom-schema.js.map +1 -0
  39. package/dist/exit-codes.d.ts +37 -0
  40. package/dist/exit-codes.d.ts.map +1 -1
  41. package/dist/exit-codes.js +37 -0
  42. package/dist/exit-codes.js.map +1 -1
  43. package/dist/index.d.ts +13 -2
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +8 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/invariants/__tests__/adr-056-release.test.d.ts +16 -0
  48. package/dist/invariants/__tests__/adr-056-release.test.d.ts.map +1 -0
  49. package/dist/invariants/__tests__/adr-056-release.test.js +88 -0
  50. package/dist/invariants/__tests__/adr-056-release.test.js.map +1 -0
  51. package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts +22 -0
  52. package/dist/invariants/__tests__/adr-070-orchestration.test.d.ts.map +1 -0
  53. package/dist/invariants/__tests__/adr-070-orchestration.test.js +151 -0
  54. package/dist/invariants/__tests__/adr-070-orchestration.test.js.map +1 -0
  55. package/dist/invariants/__tests__/registry.test.d.ts +14 -0
  56. package/dist/invariants/__tests__/registry.test.d.ts.map +1 -0
  57. package/dist/invariants/__tests__/registry.test.js +113 -0
  58. package/dist/invariants/__tests__/registry.test.js.map +1 -0
  59. package/dist/invariants/adr-056-release.d.ts +42 -0
  60. package/dist/invariants/adr-056-release.d.ts.map +1 -0
  61. package/dist/invariants/adr-056-release.js +143 -0
  62. package/dist/invariants/adr-056-release.js.map +1 -0
  63. package/dist/invariants/adr-070-orchestration.d.ts +73 -0
  64. package/dist/invariants/adr-070-orchestration.d.ts.map +1 -0
  65. package/dist/invariants/adr-070-orchestration.js +290 -0
  66. package/dist/invariants/adr-070-orchestration.js.map +1 -0
  67. package/dist/invariants/adr-073-saga.d.ts +32 -0
  68. package/dist/invariants/adr-073-saga.d.ts.map +1 -0
  69. package/dist/invariants/adr-073-saga.js +155 -0
  70. package/dist/invariants/adr-073-saga.js.map +1 -0
  71. package/dist/invariants/index.d.ts +162 -0
  72. package/dist/invariants/index.d.ts.map +1 -0
  73. package/dist/invariants/index.js +118 -0
  74. package/dist/invariants/index.js.map +1 -0
  75. package/dist/operations/__tests__/input-contract.test.d.ts +20 -0
  76. package/dist/operations/__tests__/input-contract.test.d.ts.map +1 -0
  77. package/dist/operations/__tests__/input-contract.test.js +158 -0
  78. package/dist/operations/__tests__/input-contract.test.js.map +1 -0
  79. package/dist/operations/docs.d.ts +16 -0
  80. package/dist/operations/docs.d.ts.map +1 -1
  81. package/dist/operations/input-contract.d.ts +267 -0
  82. package/dist/operations/input-contract.d.ts.map +1 -0
  83. package/dist/operations/input-contract.js +29 -0
  84. package/dist/operations/input-contract.js.map +1 -0
  85. package/dist/templates/__tests__/manifest.test.d.ts +18 -0
  86. package/dist/templates/__tests__/manifest.test.d.ts.map +1 -0
  87. package/dist/templates/__tests__/manifest.test.js +123 -0
  88. package/dist/templates/__tests__/manifest.test.js.map +1 -0
  89. package/dist/templates/manifest.d.ts +186 -0
  90. package/dist/templates/manifest.d.ts.map +1 -0
  91. package/dist/templates/manifest.js +165 -0
  92. package/dist/templates/manifest.js.map +1 -0
  93. package/package.json +2 -2
  94. package/src/__tests__/cli-meta-suggested-next.test.ts +76 -0
  95. package/src/__tests__/evidence-atom-schema.test.ts +499 -0
  96. package/src/config/__tests__/manifest.test.ts +176 -0
  97. package/src/config/manifest.ts +218 -0
  98. package/src/docs/__tests__/__snapshots__/provenance.test.ts.snap +252 -0
  99. package/src/docs/__tests__/provenance.test.ts +648 -0
  100. package/src/docs/provenance.ts +601 -0
  101. package/src/docs-taxonomy.ts +55 -0
  102. package/src/doctor.ts +111 -0
  103. package/src/errors.ts +4 -0
  104. package/src/evidence-atom-schema.ts +628 -0
  105. package/src/exit-codes.ts +39 -0
  106. package/src/index.ts +129 -0
  107. package/src/invariants/__tests__/adr-056-release.test.ts +102 -0
  108. package/src/invariants/__tests__/adr-070-orchestration.test.ts +186 -0
  109. package/src/invariants/__tests__/registry.test.ts +136 -0
  110. package/src/invariants/adr-056-release.ts +154 -0
  111. package/src/invariants/adr-070-orchestration.ts +317 -0
  112. package/src/invariants/adr-073-saga.ts +167 -0
  113. package/src/invariants/index.ts +222 -0
  114. package/src/operations/__tests__/input-contract.test.ts +188 -0
  115. package/src/operations/docs.ts +16 -0
  116. package/src/operations/input-contract.ts +301 -0
  117. package/src/templates/__tests__/manifest.test.ts +140 -0
  118. package/src/templates/manifest.ts +208 -0
@@ -0,0 +1,218 @@
1
+ /**
2
+ * ConfigManifest contract — declarative registry of every config-like file CLEO
3
+ * reads, writes, or merges, with its scope, schema, merge precedence, and the
4
+ * drift-detection strategy applied to it.
5
+ *
6
+ * Foundation for the CORE SSoT config registry (T9878) and the `resolveCleoConfig`
7
+ * cascade resolver. Built-in entries cover the four canonical CLEO files:
8
+ *
9
+ * - `~/.cleo/config.json` (`global`, precedence 10)
10
+ * - `<project>/.cleo/config.json` (`project`, precedence 20)
11
+ * - `<project>/.cleo/project-info.json` (`metadata`, separate channel)
12
+ * - `<project>/.cleo/project-context.json` (`metadata`, separate channel)
13
+ *
14
+ * Merge semantics: higher `mergePrecedence` wins. The `metadata` scope is
15
+ * intentionally OUTSIDE the project ↔ global merge chain — those files are
16
+ * read-only consumer state and must NEVER be merged into resolved CleoConfig.
17
+ *
18
+ * @task T9876
19
+ * @saga T9855
20
+ * @adr 076
21
+ */
22
+
23
+ import { type ZodTypeAny, z } from 'zod';
24
+
25
+ // ───────────────────────────────────────────────────────────────────────────
26
+ // Scope + drift unions
27
+ // ───────────────────────────────────────────────────────────────────────────
28
+
29
+ /**
30
+ * Cascade-participating scopes for the project ↔ global merge chain.
31
+ *
32
+ * - `'global'` — `~/.cleo/config.json` (precedence 10)
33
+ * - `'project'` — `<project>/.cleo/config.json` (precedence 20, higher wins)
34
+ *
35
+ * Note: `'metadata'` is NOT part of this union — metadata-scoped files are
36
+ * read-only consumer state and live on a separate channel. See
37
+ * {@link ConfigManifestScope} for the full union including metadata.
38
+ *
39
+ * @task T9876
40
+ */
41
+ export type ConfigScope = 'global' | 'project';
42
+
43
+ /**
44
+ * Full scope union covering cascade scopes AND the read-only `metadata`
45
+ * channel used by `project-info.json` and `project-context.json`.
46
+ *
47
+ * Metadata-scoped entries are NEVER merged into the resolved CleoConfig —
48
+ * they are surfaced separately as consumer state and validated only for
49
+ * schema conformance / staleness.
50
+ *
51
+ * @task T9876
52
+ */
53
+ export type ConfigManifestScope = ConfigScope | 'metadata';
54
+
55
+ /**
56
+ * Drift-detection strategy applied to a manifest entry.
57
+ *
58
+ * - `'schema-validate'` — Re-parse the file's contents against {@link ConfigManifestEntry.schema}
59
+ * on read. Any parse error is reported as drift.
60
+ * - `'staleness-gate'` — Compare the file's `detectedAt` (or equivalent freshness key)
61
+ * against a freshness threshold. Stale files are flagged but not rejected.
62
+ * - `'value-diff'` — Diff the file's resolved values against the manifest's
63
+ * `defaults` baseline and report changed keys. Useful for invariant tracking.
64
+ * - `'none'` — No drift detection. Entry is informational-only.
65
+ *
66
+ * @task T9876
67
+ */
68
+ export type DriftDetection = 'schema-validate' | 'staleness-gate' | 'value-diff' | 'none';
69
+
70
+ // ───────────────────────────────────────────────────────────────────────────
71
+ // Manifest entry shape
72
+ // ───────────────────────────────────────────────────────────────────────────
73
+
74
+ /**
75
+ * A single registry entry describing one config-like file CLEO is aware of.
76
+ *
77
+ * `mergePrecedence` orders entries within the SAME scope channel. Across
78
+ * channels, the cascade-resolver consumes entries with `scope` in
79
+ * {@link ConfigScope} (global/project) and merges by ascending precedence
80
+ * (higher wins). Metadata entries are surfaced separately.
81
+ *
82
+ * @task T9876
83
+ */
84
+ export interface ConfigManifestEntry {
85
+ /** Stable identifier for the entry (e.g. `'cleo-config-project'`). */
86
+ id: string;
87
+ /** Where the file lives in the precedence hierarchy. */
88
+ scope: ConfigManifestScope;
89
+ /** Absolute or `~`-rooted path to the file on disk. */
90
+ path: string;
91
+ /**
92
+ * Optional Zod schema used by the `'schema-validate'` drift detector to
93
+ * re-parse the file's contents. `null` disables schema validation even when
94
+ * `driftDetection === 'schema-validate'` (advisory-only mode).
95
+ */
96
+ schema?: ZodTypeAny | null;
97
+ /**
98
+ * Numeric merge precedence within the cascade. Higher wins.
99
+ *
100
+ * Canonical values:
101
+ * - `0` — defaults / metadata (NOT part of the merge chain)
102
+ * - `10` — global
103
+ * - `20` — project
104
+ */
105
+ mergePrecedence: number;
106
+ /** Drift-detection strategy to apply to this entry. */
107
+ driftDetection: DriftDetection;
108
+ /** Optional default values used as the baseline for `'value-diff'` drift detection. */
109
+ defaults?: Record<string, unknown>;
110
+ }
111
+
112
+ // ───────────────────────────────────────────────────────────────────────────
113
+ // Built-in manifest entries (4 canonical CLEO files)
114
+ // ───────────────────────────────────────────────────────────────────────────
115
+
116
+ /**
117
+ * Manifest entry for `<project>/.cleo/project-info.json`.
118
+ *
119
+ * Metadata channel (NOT part of the merge cascade). Schema-validated on
120
+ * read because the file is consumer state with a stable shape.
121
+ *
122
+ * @task T9876
123
+ */
124
+ export const PROJECT_INFO_MANIFEST: ConfigManifestEntry = {
125
+ id: 'project-info',
126
+ scope: 'metadata',
127
+ path: '.cleo/project-info.json',
128
+ mergePrecedence: 0,
129
+ driftDetection: 'schema-validate',
130
+ };
131
+
132
+ /**
133
+ * Manifest entry for `<project>/.cleo/project-context.json`.
134
+ *
135
+ * Metadata channel. Staleness-gated because the file is regenerated by
136
+ * project-detection scans and consumers care about freshness, not exact shape.
137
+ *
138
+ * @task T9876
139
+ */
140
+ export const PROJECT_CONTEXT_MANIFEST: ConfigManifestEntry = {
141
+ id: 'project-context',
142
+ scope: 'metadata',
143
+ path: '.cleo/project-context.json',
144
+ mergePrecedence: 0,
145
+ driftDetection: 'staleness-gate',
146
+ };
147
+
148
+ /**
149
+ * Manifest entry for `<project>/.cleo/config.json` (project-scoped CleoConfig).
150
+ *
151
+ * Project scope; precedence 20 — wins over global. Schema-validated.
152
+ *
153
+ * @task T9876
154
+ */
155
+ export const CLEO_CONFIG_MANIFEST: ConfigManifestEntry = {
156
+ id: 'cleo-config-project',
157
+ scope: 'project',
158
+ path: '.cleo/config.json',
159
+ mergePrecedence: 20,
160
+ driftDetection: 'schema-validate',
161
+ };
162
+
163
+ /**
164
+ * Manifest entry for `~/.cleo/config.json` (global CleoConfig).
165
+ *
166
+ * Global scope; precedence 10 — loses to project. Schema-validated.
167
+ *
168
+ * @task T9876
169
+ */
170
+ export const GLOBAL_CLEO_CONFIG_MANIFEST: ConfigManifestEntry = {
171
+ id: 'cleo-config-global',
172
+ scope: 'global',
173
+ path: '~/.cleo/config.json',
174
+ mergePrecedence: 10,
175
+ driftDetection: 'schema-validate',
176
+ };
177
+
178
+ /**
179
+ * Frozen registry of all built-in manifest entries shipped with the contracts
180
+ * package. Order is informational only — consumers MUST sort by
181
+ * `mergePrecedence` when applying the cascade.
182
+ *
183
+ * @task T9876
184
+ */
185
+ export const CONFIG_MANIFEST_ENTRIES: readonly ConfigManifestEntry[] = Object.freeze([
186
+ PROJECT_INFO_MANIFEST,
187
+ PROJECT_CONTEXT_MANIFEST,
188
+ GLOBAL_CLEO_CONFIG_MANIFEST,
189
+ CLEO_CONFIG_MANIFEST,
190
+ ]);
191
+
192
+ // ───────────────────────────────────────────────────────────────────────────
193
+ // Runtime validation schema
194
+ // ───────────────────────────────────────────────────────────────────────────
195
+
196
+ /**
197
+ * Zod schema for {@link ConfigManifestEntry}.
198
+ *
199
+ * Treats `schema` as `unknown` because `ZodTypeAny` instances cannot be
200
+ * validated through Zod itself without infinite recursion — schema-shape
201
+ * checking is the responsibility of the consumer that constructs the entry.
202
+ *
203
+ * @task T9876
204
+ */
205
+ export const configManifestEntrySchema = z.object({
206
+ id: z.string().min(1),
207
+ scope: z.union([z.literal('global'), z.literal('project'), z.literal('metadata')]),
208
+ path: z.string().min(1),
209
+ schema: z.unknown().optional(),
210
+ mergePrecedence: z.number().int().nonnegative(),
211
+ driftDetection: z.union([
212
+ z.literal('schema-validate'),
213
+ z.literal('staleness-gate'),
214
+ z.literal('value-diff'),
215
+ z.literal('none'),
216
+ ]),
217
+ defaults: z.record(z.string(), z.unknown()).optional(),
218
+ });
@@ -0,0 +1,252 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`typical-sized graph (AC4) > matches the AC4 fixture snapshot (5 docs + 8 tasks + 4 decisions + 2 sessions) 1`] = `
4
+ {
5
+ "edges": [
6
+ {
7
+ "addedAt": "2026-05-22T18:00:00.000Z",
8
+ "from": "adr-078-docs-provenance-graph",
9
+ "fromKind": "doc",
10
+ "relation": "supersedes",
11
+ "to": "adr-066-task-creation-axes",
12
+ "toKind": "doc",
13
+ },
14
+ {
15
+ "addedAt": "2026-05-22T18:00:00.000Z",
16
+ "from": "adr-066-task-creation-axes",
17
+ "fromKind": "doc",
18
+ "relation": "superseded-by",
19
+ "to": "adr-078-docs-provenance-graph",
20
+ "toKind": "doc",
21
+ },
22
+ {
23
+ "addedAt": "2026-05-22T18:00:00.000Z",
24
+ "from": "adr-078-docs-provenance-graph",
25
+ "fromKind": "doc",
26
+ "relation": "attached-to",
27
+ "to": "T10157",
28
+ "toKind": "task",
29
+ },
30
+ {
31
+ "addedAt": "2026-05-22T18:05:00.000Z",
32
+ "from": "spec-t10157-e12-provenance",
33
+ "fromKind": "doc",
34
+ "relation": "attached-to",
35
+ "to": "T10157",
36
+ "toKind": "task",
37
+ },
38
+ {
39
+ "addedAt": "2026-05-21T10:00:00.000Z",
40
+ "from": "research-provenance-graph-design",
41
+ "fromKind": "doc",
42
+ "relation": "attached-to",
43
+ "to": "T10157",
44
+ "toKind": "task",
45
+ },
46
+ {
47
+ "addedAt": "2026-05-22T19:30:00.000Z",
48
+ "from": "handoff-sg-template-config-ssot-session-1",
49
+ "fromKind": "doc",
50
+ "relation": "attached-to",
51
+ "to": "T9855",
52
+ "toKind": "task",
53
+ },
54
+ {
55
+ "addedAt": "2026-05-22T18:00:00.000Z",
56
+ "from": "T10157",
57
+ "fromKind": "task",
58
+ "relation": "related-task",
59
+ "to": "T9855",
60
+ "toKind": "task",
61
+ },
62
+ {
63
+ "addedAt": "2026-05-22T18:00:00.000Z",
64
+ "from": "T10166",
65
+ "fromKind": "task",
66
+ "relation": "related-task",
67
+ "summary": "Compatible with TreeResponse renderer (AC3)",
68
+ "to": "T10138",
69
+ "toKind": "task",
70
+ },
71
+ {
72
+ "addedAt": "2026-05-21T12:00:00.000Z",
73
+ "from": "adr-078-docs-provenance-graph",
74
+ "fromKind": "doc",
75
+ "relation": "linked-decision",
76
+ "to": "D-prov-001",
77
+ "toKind": "decision",
78
+ },
79
+ {
80
+ "addedAt": "2026-05-21T13:00:00.000Z",
81
+ "from": "spec-t10157-e12-provenance",
82
+ "fromKind": "doc",
83
+ "relation": "linked-decision",
84
+ "to": "D-prov-002",
85
+ "toKind": "decision",
86
+ },
87
+ {
88
+ "addedAt": "2026-05-21T10:00:00.000Z",
89
+ "from": "research-provenance-graph-design",
90
+ "fromKind": "doc",
91
+ "relation": "derived-from",
92
+ "to": "ses_2026-05-21_research",
93
+ "toKind": "session",
94
+ },
95
+ {
96
+ "addedAt": "2026-05-22T19:30:00.000Z",
97
+ "from": "handoff-sg-template-config-ssot-session-1",
98
+ "fromKind": "doc",
99
+ "relation": "derived-from",
100
+ "to": "ses_2026-05-22_impl",
101
+ "toKind": "session",
102
+ },
103
+ ],
104
+ "nodes": [
105
+ {
106
+ "docKind": "adr",
107
+ "id": "adr-078-docs-provenance-graph",
108
+ "kind": "doc",
109
+ "lifecycleStatus": "active",
110
+ "publishedAt": "2026-05-22T18:00:00.000Z",
111
+ "slug": "adr-078-docs-provenance-graph",
112
+ "title": "ADR 078 — Docs Provenance Graph",
113
+ },
114
+ {
115
+ "docKind": "spec",
116
+ "id": "spec-t10157-e12-provenance",
117
+ "kind": "doc",
118
+ "lifecycleStatus": "active",
119
+ "publishedAt": "2026-05-22T18:05:00.000Z",
120
+ "slug": "spec-t10157-e12-provenance",
121
+ "title": "E12 Docs Provenance Specification",
122
+ },
123
+ {
124
+ "docKind": "research",
125
+ "id": "research-provenance-graph-design",
126
+ "kind": "doc",
127
+ "lifecycleStatus": "active",
128
+ "publishedAt": "2026-05-21T10:00:00.000Z",
129
+ "slug": "research-provenance-graph-design",
130
+ "title": "Research — Cross-entity provenance graph design",
131
+ },
132
+ {
133
+ "docKind": "adr",
134
+ "id": "adr-066-task-creation-axes",
135
+ "kind": "doc",
136
+ "lifecycleStatus": "superseded",
137
+ "publishedAt": "2026-03-01T00:00:00.000Z",
138
+ "slug": "adr-066-task-creation-axes",
139
+ "supersededAt": "2026-05-22T18:00:00.000Z",
140
+ "title": "ADR 066 — Task Creation Axes",
141
+ },
142
+ {
143
+ "docKind": "handoff",
144
+ "id": "handoff-sg-template-config-ssot-session-1",
145
+ "kind": "doc",
146
+ "lifecycleStatus": "active",
147
+ "publishedAt": "2026-05-22T19:30:00.000Z",
148
+ "slug": "handoff-sg-template-config-ssot-session-1",
149
+ "title": "SG-TEMPLATE-CONFIG-SSOT handoff — session 1",
150
+ },
151
+ {
152
+ "id": "T9855",
153
+ "kind": "task",
154
+ "status": "in_progress",
155
+ "taskType": "saga",
156
+ "title": "Saga T9855",
157
+ },
158
+ {
159
+ "id": "T10157",
160
+ "kind": "task",
161
+ "status": "in_progress",
162
+ "taskType": "epic",
163
+ "title": "E12 — Provenance Graph",
164
+ },
165
+ {
166
+ "id": "T10158",
167
+ "kind": "task",
168
+ "status": "done",
169
+ "taskType": "task",
170
+ "title": "C1 — attachments-table migration",
171
+ },
172
+ {
173
+ "id": "T10162",
174
+ "kind": "task",
175
+ "status": "pending",
176
+ "taskType": "task",
177
+ "title": "C5 — supersede verb",
178
+ },
179
+ {
180
+ "id": "T10164",
181
+ "kind": "task",
182
+ "status": "pending",
183
+ "taskType": "task",
184
+ "title": "C7 — graph CLI",
185
+ },
186
+ {
187
+ "id": "T10166",
188
+ "kind": "task",
189
+ "status": "in_progress",
190
+ "taskType": "task",
191
+ "title": "C9 — provenance graph contract",
192
+ },
193
+ {
194
+ "id": "T10138",
195
+ "kind": "task",
196
+ "status": "done",
197
+ "taskType": "task",
198
+ "title": "TreeResponse contract",
199
+ },
200
+ {
201
+ "id": "T10166-sub1",
202
+ "kind": "task",
203
+ "status": "in_progress",
204
+ "taskType": "subtask",
205
+ "title": "Snapshot test fixture",
206
+ },
207
+ {
208
+ "decidedAt": "2026-05-21T12:00:00.000Z",
209
+ "id": "D-prov-001",
210
+ "kind": "decision",
211
+ "outcome": "accepted",
212
+ "title": "Cross-entity graph (not doc-only)",
213
+ },
214
+ {
215
+ "decidedAt": "2026-05-21T13:00:00.000Z",
216
+ "id": "D-prov-002",
217
+ "kind": "decision",
218
+ "outcome": "accepted",
219
+ "title": "Discriminated unions over inheritance",
220
+ },
221
+ {
222
+ "decidedAt": "2026-05-21T14:00:00.000Z",
223
+ "id": "D-prov-003",
224
+ "kind": "decision",
225
+ "outcome": "accepted",
226
+ "title": "Edge carries fromKind/toKind for disambiguation",
227
+ },
228
+ {
229
+ "decidedAt": "2026-05-22T18:00:00.000Z",
230
+ "id": "D-prov-004",
231
+ "kind": "decision",
232
+ "outcome": "proposed",
233
+ "title": "Compatible with TreeResponse renderer",
234
+ },
235
+ {
236
+ "endedAt": "2026-05-21T14:30:00.000Z",
237
+ "id": "ses_2026-05-21_research",
238
+ "kind": "session",
239
+ "startedAt": "2026-05-21T10:00:00.000Z",
240
+ "title": "Research session — provenance graph design",
241
+ },
242
+ {
243
+ "id": "ses_2026-05-22_impl",
244
+ "kind": "session",
245
+ "startedAt": "2026-05-22T18:00:00.000Z",
246
+ "title": "Implementation session — T10166",
247
+ },
248
+ ],
249
+ "totalEdges": 12,
250
+ "totalNodes": 19,
251
+ }
252
+ `;