@cleocode/contracts 2026.5.126 → 2026.5.127
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__/enums.test.js +1 -1
- package/dist/__tests__/enums.test.js.map +1 -1
- package/dist/dispatch/operations-registry.d.ts.map +1 -1
- package/dist/dispatch/operations-registry.js +48 -3
- package/dist/dispatch/operations-registry.js.map +1 -1
- package/dist/enums.d.ts +5 -3
- package/dist/enums.d.ts.map +1 -1
- package/dist/enums.js +5 -3
- package/dist/enums.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/operations/docs.d.ts +42 -1
- package/dist/operations/docs.d.ts.map +1 -1
- package/dist/operations/docs.js +4 -0
- package/dist/operations/docs.js.map +1 -1
- package/dist/operations/tasks.d.ts +11 -21
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js.map +1 -1
- package/dist/taxonomy.d.ts +136 -0
- package/dist/taxonomy.d.ts.map +1 -0
- package/dist/taxonomy.js +562 -0
- package/dist/taxonomy.js.map +1 -0
- package/dist/workgraph.d.ts +19 -3
- package/dist/workgraph.d.ts.map +1 -1
- package/dist/workgraph.js +29 -9
- package/dist/workgraph.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/enums.test.ts +1 -1
- package/src/dispatch/operations-registry.ts +49 -3
- package/src/enums.ts +5 -3
- package/src/index.ts +18 -0
- package/src/operations/docs.ts +47 -0
- package/src/operations/tasks.ts +13 -23
- package/src/taxonomy.ts +634 -0
- package/src/workgraph.ts +32 -9
package/src/taxonomy.ts
ADDED
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Taxonomy Registry (T11186).
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for classification tags across decisions,
|
|
5
|
+
* documents, tasks, and routing surfaces. Unifies the previously
|
|
6
|
+
* fragmented vocabularies:
|
|
7
|
+
*
|
|
8
|
+
* - `BRAIN_DECISION_TYPES` (memory-schema.ts) — 5 values
|
|
9
|
+
* - `BRAIN_DECISION_CATEGORIES` (memory-schema.ts) — 3 values
|
|
10
|
+
* - `BrainCognitiveType` (brain.ts) — 3 values
|
|
11
|
+
* - `BUILTIN_DOC_KINDS` (docs-taxonomy.ts) — 10 values
|
|
12
|
+
* - `labels_json` (tasks schema) — free-form, 30+ ad-hoc labels
|
|
13
|
+
* - `BrainEdgeKind` (operations/brain.ts) — open-ended
|
|
14
|
+
* - `BRAIN_PATTERN_TYPES` (memory-schema.ts) — 5 values
|
|
15
|
+
* - `BRAIN_OBSERVATION_TYPES` (memory-schema.ts) — 8 values
|
|
16
|
+
*
|
|
17
|
+
* The taxonomy is organized into **axes** — each tag is classified
|
|
18
|
+
* by what dimension of classification it represents. A single tag
|
|
19
|
+
* may belong to multiple axes (e.g., `adr` is both a doc_kind and
|
|
20
|
+
* a decision type).
|
|
21
|
+
*
|
|
22
|
+
* ## Axes
|
|
23
|
+
*
|
|
24
|
+
* | Axis | Purpose |
|
|
25
|
+
* |-------------|----------------------------------------------|
|
|
26
|
+
* | `domain` | Architectural subsystem or component area |
|
|
27
|
+
* | `type` | Kind of artifact, decision, or entry |
|
|
28
|
+
* | `lifecycle` | Phase in the RCASD→IVTR pipeline |
|
|
29
|
+
* | `priority` | Severity, urgency, or importance |
|
|
30
|
+
* | `doc_kind` | Document classification (absorbs docs-taxonomy)|
|
|
31
|
+
*
|
|
32
|
+
* @task T11186
|
|
33
|
+
* @epic T10520
|
|
34
|
+
* @saga T10516
|
|
35
|
+
* @see ADR-073 §1 — Task Hierarchy Charter
|
|
36
|
+
* @see packages/contracts/src/docs-taxonomy.ts — absorbed doc-kind subset
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Tag metadata interface
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
/** Allowed tag axis values. */
|
|
44
|
+
export type TaxonomyAxis = 'domain' | 'type' | 'lifecycle' | 'priority' | 'doc_kind';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Metadata for a single canonical tag in the unified taxonomy.
|
|
48
|
+
*/
|
|
49
|
+
export interface CanonicalTagMetadata {
|
|
50
|
+
/** Canonical tag id, lowercase kebab-case. */
|
|
51
|
+
readonly tag: string;
|
|
52
|
+
/** Human label for display in CLI output and UIs. */
|
|
53
|
+
readonly label: string;
|
|
54
|
+
/** One-line description. */
|
|
55
|
+
readonly description: string;
|
|
56
|
+
/** Which classification axes this tag belongs to. */
|
|
57
|
+
readonly axes: ReadonlyArray<TaxonomyAxis>;
|
|
58
|
+
/**
|
|
59
|
+
* Ad-hoc labels from tasks.labels_json that normalize to this tag.
|
|
60
|
+
* Used by the backfill script.
|
|
61
|
+
*/
|
|
62
|
+
readonly adhocAliases: ReadonlyArray<string>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Built-in canonical tags
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Every canonical tag known to the system.
|
|
71
|
+
*
|
|
72
|
+
* Adding a tag here automatically makes it available for decisions,
|
|
73
|
+
* task labels, doc classification, and routing surfaces. No other
|
|
74
|
+
* file needs to be updated.
|
|
75
|
+
*/
|
|
76
|
+
export const BUILTIN_TAXONOMY_TAGS: ReadonlyArray<CanonicalTagMetadata> = [
|
|
77
|
+
// ─── Domain tags ───────────────────────────────────────────────
|
|
78
|
+
{
|
|
79
|
+
tag: 'architecture',
|
|
80
|
+
label: 'Architecture',
|
|
81
|
+
description: 'Cross-cutting architectural decisions, patterns, and subsystem-level concerns',
|
|
82
|
+
axes: ['domain', 'type'],
|
|
83
|
+
adhocAliases: ['architecture'],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
tag: 'cli',
|
|
87
|
+
label: 'CLI',
|
|
88
|
+
description: 'Command-line interface, dispatch, and command infrastructure',
|
|
89
|
+
axes: ['domain'],
|
|
90
|
+
adhocAliases: ['cli'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
tag: 'core',
|
|
94
|
+
label: 'Core',
|
|
95
|
+
description: 'Core engine, shared utilities, and foundational modules',
|
|
96
|
+
axes: ['domain'],
|
|
97
|
+
adhocAliases: ['core', 'pm-core-v2', 'foundation'],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
tag: 'contracts',
|
|
101
|
+
label: 'Contracts',
|
|
102
|
+
description: 'Type contracts, schemas, and public API surface definitions',
|
|
103
|
+
axes: ['domain'],
|
|
104
|
+
adhocAliases: ['contracts', 'schema'],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
tag: 'caamp',
|
|
108
|
+
label: 'CAAMP',
|
|
109
|
+
description: 'Cross-Agent Adaptive Messaging Protocol — hooks, adapters, providers',
|
|
110
|
+
axes: ['domain'],
|
|
111
|
+
adhocAliases: ['caamp', 'cant', 'pi', 'cant-dsl'],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
tag: 'skills',
|
|
115
|
+
label: 'Skills',
|
|
116
|
+
description: 'Agent skill definitions, injection, dispatch, and guard patterns',
|
|
117
|
+
axes: ['domain'],
|
|
118
|
+
adhocAliases: ['skills'],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
tag: 'brain',
|
|
122
|
+
label: 'BRAIN',
|
|
123
|
+
description: 'Cognitive memory system — decisions, patterns, observations, learnings',
|
|
124
|
+
axes: ['domain'],
|
|
125
|
+
adhocAliases: ['brain'],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
tag: 'nexus',
|
|
129
|
+
label: 'Nexus',
|
|
130
|
+
description: 'Code intelligence surface — project indexing, impact analysis, cross-repo',
|
|
131
|
+
axes: ['domain'],
|
|
132
|
+
adhocAliases: ['nexus'],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
tag: 'orchestration',
|
|
136
|
+
label: 'Orchestration',
|
|
137
|
+
description: 'Multi-agent orchestration, playbooks, LOOM lifecycle, spawn dispatch',
|
|
138
|
+
axes: ['domain'],
|
|
139
|
+
adhocAliases: ['orchestration', 'orchestrate'],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
tag: 'sessions',
|
|
143
|
+
label: 'Sessions',
|
|
144
|
+
description: 'Session lifecycle — handoff, debrief, briefing, snapshot, state',
|
|
145
|
+
axes: ['domain'],
|
|
146
|
+
adhocAliases: ['sessions'],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
tag: 'tasks',
|
|
150
|
+
label: 'Tasks',
|
|
151
|
+
description: 'Task management — CRUD, gates, evidence, acceptance criteria, lifecycle',
|
|
152
|
+
axes: ['domain'],
|
|
153
|
+
adhocAliases: ['tasks'],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
tag: 'docs',
|
|
157
|
+
label: 'Documents',
|
|
158
|
+
description: 'Document storage, retrieval, taxonomy, publish, and provenance',
|
|
159
|
+
axes: ['domain'],
|
|
160
|
+
adhocAliases: ['docs', 'documentation'],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
tag: 'cleoos',
|
|
164
|
+
label: 'CleoOS',
|
|
165
|
+
description: 'CleoOS runtime — gateway, daemon, injection facade, agents',
|
|
166
|
+
axes: ['domain'],
|
|
167
|
+
adhocAliases: ['cleoos', 'facade', 'sentient'],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
tag: 'worktrunk',
|
|
171
|
+
label: 'Worktrunk',
|
|
172
|
+
description: 'Git worktree management — spawn, adopt, prune, lifecycle',
|
|
173
|
+
axes: ['domain'],
|
|
174
|
+
adhocAliases: ['worktrunk', 'worktrunk-ssot'],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
tag: 'agents',
|
|
178
|
+
label: 'Agents',
|
|
179
|
+
description: 'Agent profiles, execution learning, registry, dispatch',
|
|
180
|
+
axes: ['domain'],
|
|
181
|
+
adhocAliases: ['agents'],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
tag: 'routing',
|
|
185
|
+
label: 'Routing',
|
|
186
|
+
description: 'Operation routing, capability matrix, dispatch path resolution',
|
|
187
|
+
axes: ['domain'],
|
|
188
|
+
adhocAliases: ['routing'],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
tag: 'studio',
|
|
192
|
+
label: 'Studio',
|
|
193
|
+
description: 'Web UI, dashboard, and visual interfaces',
|
|
194
|
+
axes: ['domain'],
|
|
195
|
+
adhocAliases: ['studio'],
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
// ─── Type tags ─────────────────────────────────────────────────
|
|
199
|
+
{
|
|
200
|
+
tag: 'architectural',
|
|
201
|
+
label: 'Architectural Decision',
|
|
202
|
+
description: 'Cross-cutting architectural decision with durable rationale',
|
|
203
|
+
axes: ['type'],
|
|
204
|
+
adhocAliases: ['architectural'],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
tag: 'technical',
|
|
208
|
+
label: 'Technical Decision',
|
|
209
|
+
description: 'Implementation-scoped technical decision (library, pattern, algorithm)',
|
|
210
|
+
axes: ['type'],
|
|
211
|
+
adhocAliases: ['technical'],
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
tag: 'process',
|
|
215
|
+
label: 'Process Decision',
|
|
216
|
+
description: 'Workflow, methodology, or operational process decision',
|
|
217
|
+
axes: ['type'],
|
|
218
|
+
adhocAliases: ['process'],
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
tag: 'strategic',
|
|
222
|
+
label: 'Strategic Decision',
|
|
223
|
+
description: 'Long-horizon direction-setting decision spanning multiple epics',
|
|
224
|
+
axes: ['type'],
|
|
225
|
+
adhocAliases: ['strategic'],
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
tag: 'tactical',
|
|
229
|
+
label: 'Tactical Decision',
|
|
230
|
+
description: 'Short-horizon execution-level decision (AGT-* dispatch, within-sprint)',
|
|
231
|
+
axes: ['type'],
|
|
232
|
+
adhocAliases: ['tactical'],
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
tag: 'operational',
|
|
236
|
+
label: 'Operational Decision',
|
|
237
|
+
description: 'Infrastructure, deployment, or runtime configuration decision',
|
|
238
|
+
axes: ['type'],
|
|
239
|
+
adhocAliases: ['operational'],
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
tag: 'bugfix',
|
|
243
|
+
label: 'Bug Fix',
|
|
244
|
+
description: 'Defect correction or regression fix',
|
|
245
|
+
axes: ['type'],
|
|
246
|
+
adhocAliases: ['bugfix', 'bug-fix', 'hygiene'],
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
tag: 'refactor',
|
|
250
|
+
label: 'Refactor',
|
|
251
|
+
description: 'Internal restructuring without functional change',
|
|
252
|
+
axes: ['type'],
|
|
253
|
+
adhocAliases: ['refactor'],
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
tag: 'feature',
|
|
257
|
+
label: 'Feature',
|
|
258
|
+
description: 'New capability or user-facing enhancement',
|
|
259
|
+
axes: ['type'],
|
|
260
|
+
adhocAliases: ['feature'],
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
tag: 'discovery',
|
|
264
|
+
label: 'Discovery',
|
|
265
|
+
description: 'Exploratory research, investigation, or spike outcome',
|
|
266
|
+
axes: ['type'],
|
|
267
|
+
adhocAliases: ['discovery', 'exploration', 'research-type'],
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
tag: 'migration',
|
|
271
|
+
label: 'Migration',
|
|
272
|
+
description: 'Data or schema migration, backfill, or upgrade path',
|
|
273
|
+
axes: ['type'],
|
|
274
|
+
adhocAliases: ['migration', 'migrations'],
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
tag: 'unification',
|
|
278
|
+
label: 'Unification',
|
|
279
|
+
description: 'Consolidation of fragmented systems into a single SSoT',
|
|
280
|
+
axes: ['type'],
|
|
281
|
+
adhocAliases: ['unification'],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
tag: 'bootstrap',
|
|
285
|
+
label: 'Bootstrap',
|
|
286
|
+
description: 'Initial setup, scaffolding, or seed infrastructure',
|
|
287
|
+
axes: ['type'],
|
|
288
|
+
adhocAliases: ['bootstrap'],
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// ─── Lifecycle tags ────────────────────────────────────────────
|
|
292
|
+
{
|
|
293
|
+
tag: 'research',
|
|
294
|
+
label: 'Research',
|
|
295
|
+
description: 'LOOM stage 1: information gathering and domain understanding',
|
|
296
|
+
axes: ['lifecycle'],
|
|
297
|
+
adhocAliases: ['research', 'wave-0'],
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
tag: 'consensus',
|
|
301
|
+
label: 'Consensus',
|
|
302
|
+
description: 'LOOM stage 2: multi-agent consensus and validation of findings',
|
|
303
|
+
axes: ['lifecycle'],
|
|
304
|
+
adhocAliases: ['consensus'],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
tag: 'design',
|
|
308
|
+
label: 'Architecture Decision',
|
|
309
|
+
description: 'LOOM stage 3: architecture decision recording and validation',
|
|
310
|
+
axes: ['lifecycle'],
|
|
311
|
+
adhocAliases: ['design', 'architecture_decision'],
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
tag: 'specification',
|
|
315
|
+
label: 'Specification',
|
|
316
|
+
description: 'LOOM stage 4: technical specification authoring',
|
|
317
|
+
axes: ['lifecycle'],
|
|
318
|
+
adhocAliases: ['specification', 'rfc'],
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
tag: 'decomposition',
|
|
322
|
+
label: 'Decomposition',
|
|
323
|
+
description: 'LOOM stage 5: task decomposition and dependency modeling',
|
|
324
|
+
axes: ['lifecycle'],
|
|
325
|
+
adhocAliases: ['decomposition'],
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
tag: 'implementation',
|
|
329
|
+
label: 'Implementation',
|
|
330
|
+
description: 'LOOM stage 6: code authoring and unit-level verification',
|
|
331
|
+
axes: ['lifecycle'],
|
|
332
|
+
adhocAliases: [
|
|
333
|
+
'implementation',
|
|
334
|
+
'wave-1',
|
|
335
|
+
'wave-2',
|
|
336
|
+
'wave-3',
|
|
337
|
+
'wave-4',
|
|
338
|
+
'wave-5',
|
|
339
|
+
'wave.1',
|
|
340
|
+
'wave.2',
|
|
341
|
+
'wave.3',
|
|
342
|
+
],
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
tag: 'validation',
|
|
346
|
+
label: 'Validation',
|
|
347
|
+
description: 'LOOM stage 7: integration testing and gate verification',
|
|
348
|
+
axes: ['lifecycle'],
|
|
349
|
+
adhocAliases: ['validation', 'testing'],
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
tag: 'release',
|
|
353
|
+
label: 'Release',
|
|
354
|
+
description: 'LOOM stage 9: versioning, changelog, npm publish, tag push',
|
|
355
|
+
axes: ['lifecycle', 'type'],
|
|
356
|
+
adhocAliases: ['release'],
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
// ─── Priority tags ─────────────────────────────────────────────
|
|
360
|
+
{
|
|
361
|
+
tag: 'p0',
|
|
362
|
+
label: 'P0 — Critical',
|
|
363
|
+
description: 'Drop-everything critical: blocks release, data loss, security incident',
|
|
364
|
+
axes: ['priority'],
|
|
365
|
+
adhocAliases: ['p0', 'critical', 'prime-tier1'],
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
tag: 'p1',
|
|
369
|
+
label: 'P1 — High',
|
|
370
|
+
description: 'High priority: blocks dependent work, user-visible degradation',
|
|
371
|
+
axes: ['priority'],
|
|
372
|
+
adhocAliases: ['p1'],
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
tag: 'p2',
|
|
376
|
+
label: 'P2 — Medium',
|
|
377
|
+
description: 'Medium priority: should fix, not blocking current wave',
|
|
378
|
+
axes: ['priority'],
|
|
379
|
+
adhocAliases: ['p2'],
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
tag: 'p3',
|
|
383
|
+
label: 'P3 — Low',
|
|
384
|
+
description: 'Low priority: nice-to-have, cleanup, future consideration',
|
|
385
|
+
axes: ['priority'],
|
|
386
|
+
adhocAliases: ['p3'],
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
// ─── Doc-kind tags (absorb docs-taxonomy) ──────────────────────
|
|
390
|
+
{
|
|
391
|
+
tag: 'adr',
|
|
392
|
+
label: 'Architecture Decision Record',
|
|
393
|
+
description: 'Formal architecture decision record with rationale and alternatives',
|
|
394
|
+
axes: ['doc_kind'],
|
|
395
|
+
adhocAliases: ['adr'],
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
tag: 'spec',
|
|
399
|
+
label: 'Specification',
|
|
400
|
+
description: 'Technical specification for a feature, protocol, or subsystem',
|
|
401
|
+
axes: ['doc_kind'],
|
|
402
|
+
adhocAliases: ['spec'],
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
tag: 'research',
|
|
406
|
+
label: 'Research Note',
|
|
407
|
+
description: 'Investigation findings, surveys, and domain analysis',
|
|
408
|
+
axes: ['doc_kind'],
|
|
409
|
+
adhocAliases: ['research'],
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
tag: 'handoff',
|
|
413
|
+
label: 'Handoff Note',
|
|
414
|
+
description: 'Cross-session handoff with state, blockers, and next actions',
|
|
415
|
+
axes: ['doc_kind'],
|
|
416
|
+
adhocAliases: ['handoff'],
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
tag: 'note',
|
|
420
|
+
label: 'Note',
|
|
421
|
+
description: 'General-purpose note, observation, or free-form documentation',
|
|
422
|
+
axes: ['doc_kind'],
|
|
423
|
+
adhocAliases: ['note'],
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
tag: 'llmreadme',
|
|
427
|
+
label: 'LLM README',
|
|
428
|
+
description: 'Agent-consumable project overview, conventions, and context',
|
|
429
|
+
axes: ['doc_kind'],
|
|
430
|
+
adhocAliases: ['llmreadme', 'llm-readme'],
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
tag: 'designmd',
|
|
434
|
+
label: 'Design Doc',
|
|
435
|
+
description: 'Google DESIGN.md token spec for agent interface contracts',
|
|
436
|
+
axes: ['doc_kind'],
|
|
437
|
+
adhocAliases: ['designmd', 'design-md'],
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
tag: 'changeset',
|
|
441
|
+
label: 'Changeset',
|
|
442
|
+
description: 'User-facing changelog entry for a single change',
|
|
443
|
+
axes: ['doc_kind'],
|
|
444
|
+
adhocAliases: ['changeset'],
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
tag: 'changelog',
|
|
448
|
+
label: 'Changelog',
|
|
449
|
+
description: 'Aggregated release changelog for a version',
|
|
450
|
+
axes: ['doc_kind'],
|
|
451
|
+
adhocAliases: ['changelog'],
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
|
|
455
|
+
// ---------------------------------------------------------------------------
|
|
456
|
+
// Derived constants
|
|
457
|
+
// ---------------------------------------------------------------------------
|
|
458
|
+
|
|
459
|
+
/** All canonical tag string values. */
|
|
460
|
+
export const CANONICAL_TAG_VALUES = BUILTIN_TAXONOMY_TAGS.map((t) => t.tag);
|
|
461
|
+
|
|
462
|
+
/** Every tag indexed by its canonical id. */
|
|
463
|
+
const _TAG_BY_ID = new Map<string, CanonicalTagMetadata>(
|
|
464
|
+
BUILTIN_TAXONOMY_TAGS.map((t) => [t.tag, t]),
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
/** Ad-hoc label → canonical tag lookup. */
|
|
468
|
+
const ADHOC_TO_CANONICAL = new Map<string, string>();
|
|
469
|
+
for (const tag of BUILTIN_TAXONOMY_TAGS) {
|
|
470
|
+
for (const alias of tag.adhocAliases) {
|
|
471
|
+
ADHOC_TO_CANONICAL.set(alias, tag.tag);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** Tags grouped by axis. */
|
|
476
|
+
const TAGS_BY_AXIS = new Map<TaxonomyAxis, CanonicalTagMetadata[]>();
|
|
477
|
+
for (const axis of ['domain', 'type', 'lifecycle', 'priority', 'doc_kind'] as TaxonomyAxis[]) {
|
|
478
|
+
TAGS_BY_AXIS.set(
|
|
479
|
+
axis,
|
|
480
|
+
BUILTIN_TAXONOMY_TAGS.filter((t) => t.axes.includes(axis)),
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** All canonical tag type values (subset of ALL_TAGS with axis='type'). */
|
|
485
|
+
export const CANONICAL_TYPE_TAGS: ReadonlyArray<string> = TAGS_BY_AXIS.get('type')!.map(
|
|
486
|
+
(t) => t.tag,
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
/** All canonical domain tag values. */
|
|
490
|
+
export const CANONICAL_DOMAIN_TAGS: ReadonlyArray<string> = TAGS_BY_AXIS.get('domain')!.map(
|
|
491
|
+
(t) => t.tag,
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
/** Canonical priority tag values in order. */
|
|
495
|
+
export const CANONICAL_PRIORITY_TAGS = ['p0', 'p1', 'p2', 'p3'] as const;
|
|
496
|
+
|
|
497
|
+
/** Canonical lifecycle tag values in LOOM order. */
|
|
498
|
+
export const CANONICAL_LIFECYCLE_TAGS = [
|
|
499
|
+
'research',
|
|
500
|
+
'consensus',
|
|
501
|
+
'design',
|
|
502
|
+
'specification',
|
|
503
|
+
'decomposition',
|
|
504
|
+
'implementation',
|
|
505
|
+
'validation',
|
|
506
|
+
'release',
|
|
507
|
+
] as const;
|
|
508
|
+
|
|
509
|
+
/** Doc-kind tags (absorbed from docs-taxonomy). */
|
|
510
|
+
export const CANONICAL_DOC_KIND_TAGS: ReadonlyArray<string> = TAGS_BY_AXIS.get('doc_kind')!.map(
|
|
511
|
+
(t) => t.tag,
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
// ---------------------------------------------------------------------------
|
|
515
|
+
// Taxonomy Registry
|
|
516
|
+
// ---------------------------------------------------------------------------
|
|
517
|
+
|
|
518
|
+
/** Error thrown for taxonomy validation failures. */
|
|
519
|
+
export class TaxonomyError extends Error {
|
|
520
|
+
constructor(
|
|
521
|
+
message: string,
|
|
522
|
+
public readonly invalidTags: string[],
|
|
523
|
+
) {
|
|
524
|
+
super(message);
|
|
525
|
+
this.name = 'TaxonomyError';
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Canonical Taxonomy Registry — runtime accessor and validator.
|
|
531
|
+
*
|
|
532
|
+
* Use {@link TaxonomyRegistry.default} for the built-in singleton,
|
|
533
|
+
* or construct with a custom tag list for testing.
|
|
534
|
+
*/
|
|
535
|
+
export class TaxonomyRegistry {
|
|
536
|
+
private readonly byId: Map<string, CanonicalTagMetadata>;
|
|
537
|
+
private readonly adhocMap: Map<string, string>;
|
|
538
|
+
|
|
539
|
+
constructor(tags: ReadonlyArray<CanonicalTagMetadata>) {
|
|
540
|
+
this.byId = new Map(tags.map((t) => [t.tag, t]));
|
|
541
|
+
this.adhocMap = new Map<string, string>();
|
|
542
|
+
for (const tag of tags) {
|
|
543
|
+
for (const alias of tag.adhocAliases) {
|
|
544
|
+
const existingTarget = this.adhocMap.get(alias);
|
|
545
|
+
if (existingTarget !== undefined && existingTarget !== tag.tag) {
|
|
546
|
+
throw new TaxonomyError(
|
|
547
|
+
`Ad-hoc alias '${alias}' maps to both '${existingTarget}' and '${tag.tag}' — aliases must be unique across targets`,
|
|
548
|
+
[alias],
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
this.adhocMap.set(alias, tag.tag);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/** Default built-in registry singleton. */
|
|
557
|
+
static readonly default = new TaxonomyRegistry(BUILTIN_TAXONOMY_TAGS);
|
|
558
|
+
|
|
559
|
+
/** True when `tag` is a canonical tag. */
|
|
560
|
+
isCanonical(tag: string): boolean {
|
|
561
|
+
return this.byId.has(tag);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** Look up metadata for a canonical tag. */
|
|
565
|
+
get(tag: string): CanonicalTagMetadata | undefined {
|
|
566
|
+
return this.byId.get(tag);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/** List all canonical tags. */
|
|
570
|
+
list(): ReadonlyArray<CanonicalTagMetadata> {
|
|
571
|
+
return [...this.byId.values()];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/** List tags for a specific axis. */
|
|
575
|
+
listByAxis(axis: TaxonomyAxis): ReadonlyArray<CanonicalTagMetadata> {
|
|
576
|
+
return this.list().filter((t) => t.axes.includes(axis));
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Normalize an ad-hoc label to its canonical form.
|
|
581
|
+
*
|
|
582
|
+
* Returns the canonical tag id, or `undefined` if the label has no
|
|
583
|
+
* known canonical mapping.
|
|
584
|
+
*/
|
|
585
|
+
normalize(adhoc: string): string | undefined {
|
|
586
|
+
return this.adhocMap.get(adhoc);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Validate that every tag in `tags` is canonical.
|
|
591
|
+
*
|
|
592
|
+
* @param tags - Tags to validate.
|
|
593
|
+
* @returns An array of invalid tags. Empty array = all valid.
|
|
594
|
+
*/
|
|
595
|
+
validate(tags: ReadonlyArray<string>): string[] {
|
|
596
|
+
return tags.filter((t) => !this.byId.has(t));
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Validate tags and throw on failure.
|
|
601
|
+
*
|
|
602
|
+
* @param tags - Tags to validate.
|
|
603
|
+
* @param context - Human-readable context for the error message.
|
|
604
|
+
* @throws TaxonomyError when any tag is not canonical.
|
|
605
|
+
*/
|
|
606
|
+
validateOrThrow(tags: ReadonlyArray<string>, context: string): void {
|
|
607
|
+
const invalid = this.validate(tags);
|
|
608
|
+
if (invalid.length > 0) {
|
|
609
|
+
throw new TaxonomyError(
|
|
610
|
+
`${context}: unknown tags [${invalid.join(', ')}]. ` +
|
|
611
|
+
`Use canonical tags. Run 'cleo taxonomy list' to see all valid tags.`,
|
|
612
|
+
invalid,
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Normalize a set of ad-hoc labels to canonical form.
|
|
619
|
+
*
|
|
620
|
+
* Unknown labels are passed through unchanged (they may be
|
|
621
|
+
* project-specific extensions or task-id references).
|
|
622
|
+
*
|
|
623
|
+
* @param labels - Ad-hoc labels to normalize.
|
|
624
|
+
* @returns Normalized label set (deduplicated).
|
|
625
|
+
*/
|
|
626
|
+
normalizeSet(labels: ReadonlyArray<string>): string[] {
|
|
627
|
+
const result = new Set<string>();
|
|
628
|
+
for (const label of labels) {
|
|
629
|
+
const canonical = this.normalize(label);
|
|
630
|
+
result.add(canonical ?? label);
|
|
631
|
+
}
|
|
632
|
+
return [...result];
|
|
633
|
+
}
|
|
634
|
+
}
|
package/src/workgraph.ts
CHANGED
|
@@ -1058,13 +1058,36 @@ export interface WorkGraphHierarchyValidationOptions {
|
|
|
1058
1058
|
readonly throwOnViolation?: boolean;
|
|
1059
1059
|
}
|
|
1060
1060
|
|
|
1061
|
+
const ROOT_TASK_TYPES: ReadonlySet<TaskType> = new Set(['saga', 'epic']);
|
|
1062
|
+
|
|
1061
1063
|
const PARENT_TYPE_MATRIX: Readonly<Record<TaskType, readonly TaskType[]>> = {
|
|
1062
1064
|
saga: [],
|
|
1063
|
-
epic: [],
|
|
1065
|
+
epic: ['saga'],
|
|
1064
1066
|
task: ['epic'],
|
|
1065
|
-
subtask: ['
|
|
1067
|
+
subtask: ['task'],
|
|
1066
1068
|
};
|
|
1067
1069
|
|
|
1070
|
+
/**
|
|
1071
|
+
* Return true when the task type can exist without a parent in the canonical
|
|
1072
|
+
* PM-Core V2 containment tree.
|
|
1073
|
+
*
|
|
1074
|
+
* @param type - Task type to test.
|
|
1075
|
+
*/
|
|
1076
|
+
export function canWorkGraphTaskTypeBeRoot(type: TaskType): boolean {
|
|
1077
|
+
return ROOT_TASK_TYPES.has(type);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Return true when a parent type can contain a child type in the canonical
|
|
1082
|
+
* PM-Core V2 containment tree.
|
|
1083
|
+
*
|
|
1084
|
+
* @param childType - Child task type.
|
|
1085
|
+
* @param parentType - Candidate parent task type.
|
|
1086
|
+
*/
|
|
1087
|
+
export function isAllowedWorkGraphParentType(childType: TaskType, parentType: TaskType): boolean {
|
|
1088
|
+
return PARENT_TYPE_MATRIX[childType].includes(parentType);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1068
1091
|
function describeAllowedParents(type: TaskType): string {
|
|
1069
1092
|
const allowed = PARENT_TYPE_MATRIX[type];
|
|
1070
1093
|
return allowed.length === 0 ? 'root only' : allowed.join('|');
|
|
@@ -1107,7 +1130,7 @@ function makeViolation(
|
|
|
1107
1130
|
taskType: node.type,
|
|
1108
1131
|
parentId,
|
|
1109
1132
|
parentType,
|
|
1110
|
-
message: `tasks.parent_id must follow saga
|
|
1133
|
+
message: `tasks.parent_id must follow saga->epic, epic->task, and task->subtask; sagas and standalone epics may be roots; expected ${node.type} parent ${expected}, got ${actual}`,
|
|
1111
1134
|
};
|
|
1112
1135
|
}
|
|
1113
1136
|
|
|
@@ -1115,9 +1138,10 @@ function makeViolation(
|
|
|
1115
1138
|
* Validate WorkGraph hierarchy rows against CLEO's canonical type/parent matrix.
|
|
1116
1139
|
*
|
|
1117
1140
|
* The validator mirrors the SQLite trigger invariant in a storage-agnostic form
|
|
1118
|
-
* for core adapters, projections, and API callers: sagas and epics
|
|
1119
|
-
*
|
|
1120
|
-
* are leaves. Saga membership
|
|
1141
|
+
* for core adapters, projections, and API callers: sagas and standalone epics
|
|
1142
|
+
* may be roots, sagas may contain epics, epics may contain tasks, tasks may
|
|
1143
|
+
* contain subtasks, and subtasks are leaves. Saga membership uses `parentId`
|
|
1144
|
+
* containment, never `task_relations.groups`.
|
|
1121
1145
|
*
|
|
1122
1146
|
* @param nodes - Task-like hierarchy rows to validate.
|
|
1123
1147
|
* @param options - Optional fail-fast behavior for enforcement call sites.
|
|
@@ -1136,11 +1160,10 @@ export function validateWorkGraphHierarchy(
|
|
|
1136
1160
|
for (const node of nodes) {
|
|
1137
1161
|
const parentId = node.parentId ?? null;
|
|
1138
1162
|
const parent = parentId === null ? undefined : byId.get(parentId);
|
|
1139
|
-
const allowedParents = PARENT_TYPE_MATRIX[node.type];
|
|
1140
1163
|
const valid =
|
|
1141
1164
|
parentId === null
|
|
1142
|
-
?
|
|
1143
|
-
: parent !== undefined &&
|
|
1165
|
+
? canWorkGraphTaskTypeBeRoot(node.type)
|
|
1166
|
+
: parent !== undefined && isAllowedWorkGraphParentType(node.type, parent.type);
|
|
1144
1167
|
|
|
1145
1168
|
if (!valid) {
|
|
1146
1169
|
const violation = makeViolation(node, parent);
|