-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspecwright.manifest.json
More file actions
512 lines (466 loc) · 34.9 KB
/
Copy pathspecwright.manifest.json
File metadata and controls
512 lines (466 loc) · 34.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
{
"$comment": [
"Canonical inventory contract for specwright. Read by scripts/validate.ps1 and",
"scripts/validate.sh (Check 7: docs consistency).",
"",
"This file stores NO counts. Counts are derived from the globs below at runtime, so",
"adding a command/agent/skill means adding the file and nothing else. A manifest that",
"hardcoded numbers would become a third place to update and would reintroduce exactly",
"the drift this check exists to prevent.",
"",
"Each area declares EITHER a 'glob' or an explicit 'files' list, never both.",
"Glob semantics: matches FILES only (directories are never counted), non-recursive,",
"with '*' matching one path segment. Supported by bash globbing and PowerShell",
"Get-ChildItem alike - do not introduce '**' without teaching both validators.",
"",
"Regex semantics: patterns must be valid in BOTH POSIX ERE (bash [[ =~ ]]) and .NET",
"(PowerShell Select-String). Use [0-9] not \\d, no lookarounds, and do not escape",
"backticks. Exactly one capture group, wrapping the number under test."
],
"areas": {
"commands": {
"glob": "commands/*.md",
"installTo": "commands/sd/",
"description": "Slash command workflow definitions"
},
"agents": {
"glob": "agents/*.md",
"installTo": "agents/sd/",
"description": "Subagent prompt files"
},
"skills": {
"glob": "skills/*/SKILL.md",
"installTo": "skills/sd/",
"description": "Reusable rule packs, one folder per skill"
},
"hooksPowerShell": {
"glob": "hooks/powershell/*.ps1",
"installTo": "hooks/sd/",
"description": "Hook scripts installed on Windows"
},
"hooksBash": {
"glob": "hooks/bash/*.sh",
"installTo": "hooks/sd/",
"description": "Hook scripts installed on Unix. Hook-pair parity (Check 3) already asserts this equals hooksPowerShell."
},
"templatesSetup": {
"glob": "templates/*",
"installTo": "templates/sd/",
"description": "Top-level setup templates. NOT a *.md glob - 2 of the 4 are .json."
},
"templatesSpec": {
"glob": "templates/specs/*",
"installTo": "templates/sd/specs/",
"description": "Per-workflow spec templates"
},
"workflowCommands": {
"files": [
"commands/feature.md",
"commands/bug.md",
"commands/rca.md",
"commands/refactor.md",
"commands/perf.md",
"commands/port.md"
],
"installTo": "commands/sd/",
"description": "Spec-producing pipelines - a SUBSET of commands (the rest are utilities: spec, explore, review, setup, release, adr, verify, status). Docs cite this subset count separately from the total, so it needs its own quantity. Declared as an explicit list because no glob expresses 'these 6 of the 14'; the validators also assert each file exists."
}
},
"derived": {
"templatesTotal": ["templatesSetup", "templatesSpec"],
"installTotal": [
"commands",
"agents",
"skills",
"hooksPowerShell",
"templatesSetup",
"templatesSpec"
],
"installTotalWithStamps": [
"commands",
"agents",
"skills",
"hooksPowerShell",
"templatesSetup",
"templatesSpec",
"installStamps"
]
},
"historicalExclusions": [
"docs/history/",
"docs/superpowers/",
"CHANGELOG.md",
"_bmad/",
"_bmad-output/",
"design-artifacts/",
"node_modules/",
".claude/",
".superpowers/"
],
"$exclusionsComment": [
"These paths intentionally contain superseded counts ('10 commands / 5 agents') as a",
"past-state record. They are excluded from the undeclared-claim scan and must never be",
"'fixed' to match current disk state. CHANGELOG.md is historical by definition.",
"The remaining entries are untracked/vendored noise, not documentation."
],
"claimPhrases": [
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) slash commands",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) workflow (commands|definitions)",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) commands",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (specialized )?subagents",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) subagent (definitions|prompt files)",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (specialist )?agents",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (cross-platform |guard-rail )?hooks",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (PowerShell|bash) hooks",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (cross-platform )?hook scripts",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (setup|spec) templates",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) templates",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (reusable )?skills",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) (reusable )?rule packs",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) hard gates?",
"([Oo]ne|[Tt]wo|[Tt]hree|[Ff]our|[Ff]ive|[Ss]ix|[Ss]even|[Ee]ight|[Nn]ine|[Tt]en|[Ee]leven|[Tt]welve|[Tt]hirteen|[Ff]ourteen|[Ff]ifteen|[Ss]ixteen|[Ss]eventeen|[Ee]ighteen|[Nn]ineteen|[Tt]wenty|[0-9]+) version stamps?"
],
"$claimPhrasesComment": [
"Vocabulary of what an inventory claim looks like. Any line in a tracked .md file that",
"matches one of these but is NOT covered by a docClaims entry below fails Check 7 as an",
"undeclared claim. This is what keeps docClaims canonical: a new doc that publishes a",
"number cannot silently escape the check. Files under historicalExclusions are skipped.",
"",
"The leading alternation is DETECTION vocabulary, not validation input. A docClaims",
"entry compares its captured group against an integer derived from disk, so a spelled-out",
"word can never be validated - only detected, and therefore only rejected as undeclared.",
"That is the intended pressure: see the policy below.",
"",
"Three escapes were found and closed on 2026-07-22 (SW-24). Each had let a real, wrong",
"claim sit in a tracked doc through many green runs:",
" 1. Spelled-out numbers. Every pattern was anchored on [0-9]+, so 'seven reusable",
" skills' in README.md was invisible from the moment an eighth skill shipped.",
" 2. Capitalisation. Adding a lowercase word alternation is NOT enough: a spelled-out",
" number is usually SENTENCE-INITIAL, which is exactly where it is capitalised.",
" 'Three hooks ship in ...' escaped a lowercase-only fix. POSIX ERE (bash [[ =~ ]])",
" has no inline case flag, so each word carries an explicit [Tt]-style class rather",
" than relying on a flag one engine supports and the other does not.",
" 3. Bare nouns. Only decorated forms were listed ('slash commands', 'workflow",
" commands'), so 'Five commands invoke no subagent' matched nothing. Bare 'commands'",
" and 'agents' are now in the vocabulary.",
"",
"The alternation is written out inline on every line rather than factored into a shared",
"key. Factoring it would require BOTH validators to learn how to splice it, and a",
"PowerShell/bash pair that must agree on string assembly is precisely the divergence this",
"repo keeps getting bitten by. The manifest stays declarative; the validators stay dumb."
],
"$claimPolicyComment": [
"POLICY for writing an inventory number in a tracked doc (SW-24):",
"",
" If the number is derivable from an 'areas' or 'derived' quantity, write it in DIGITS",
" and add a docClaims entry. Drift then fails the build.",
"",
" If it is not derivable, do not publish a number at all - name the things instead.",
" A count with no source of truth is a value waiting to rot, and the names beside it",
" usually already carry the meaning.",
"",
"Worked example from the same commit: docs/architecture.md said 'Five commands invoke no",
"subagent at all' and then listed all five by name. The number added nothing the list did",
"not already say, and no area derives it - so the number was removed rather than a new",
"area invented to guard it. By contrast 'Three hooks ship in cross-platform pairs' IS",
"hooksPowerShell, so it became '3 hooks ...' with a docClaims entry."
],
"docClaims": [
{ "file": "README.md", "pattern": "^> ([0-9]+) slash commands, ", "equals": "commands" },
{ "file": "README.md", "pattern": "^> [0-9]+ slash commands, ([0-9]+) specialized subagents", "equals": "agents" },
{ "file": "README.md", "pattern": "specialized subagents, ([0-9]+) guard-rail hooks", "equals": "hooksPowerShell" },
{ "file": "README.md", "pattern": "guard-rail hooks, ([0-9]+) templates", "equals": "templatesTotal" },
{ "file": "README.md", "pattern": "guard-rail hooks, [0-9]+ templates, ([0-9]+) reusable skills", "equals": "skills" },
{ "file": "README.md", "pattern": "^\\| \\*\\*([0-9]+) slash commands\\*\\* \\|", "equals": "commands" },
{ "file": "README.md", "pattern": "^\\| \\*\\*([0-9]+) specialized subagents\\*\\* \\|", "equals": "agents" },
{ "file": "README.md", "pattern": "^\\| \\*\\*([0-9]+) cross-platform hooks\\*\\* \\|", "equals": "hooksPowerShell" },
{ "file": "README.md", "pattern": "^\\| \\*\\*([0-9]+) templates\\*\\* \\|", "equals": "templatesTotal" },
{ "file": "README.md", "pattern": "templates\\*\\* \\| ([0-9]+) setup templates", "equals": "templatesSetup" },
{ "file": "README.md", "pattern": "setup templates \\+ ([0-9]+) spec templates", "equals": "templatesSpec" },
{ "file": "README.md", "pattern": "^\\| \\*\\*([0-9]+) reusable skills\\*\\* \\|", "equals": "skills" },
{ "file": "README.md", "pattern": "they orchestrate ([0-9]+) subagents", "equals": "agents" },
{ "file": "README.md", "pattern": "Cross-cutting rules live in \\*\\*([0-9]+) skills\\*\\*", "equals": "skills" },
{ "file": "CLAUDE.md", "pattern": "commands/sd/` \\| ([0-9]+) slash commands", "equals": "commands" },
{ "file": "CLAUDE.md", "pattern": "agents/sd/` \\| ([0-9]+) subagents", "equals": "agents" },
{ "file": "CLAUDE.md", "pattern": "hooks/sd/` \\| ([0-9]+) hooks ", "equals": "hooksPowerShell" },
{ "file": "CLAUDE.md", "pattern": "templates/sd/` \\| ([0-9]+) setup templates", "equals": "templatesSetup" },
{ "file": "CLAUDE.md", "pattern": "setup templates \\+ ([0-9]+) spec templates in", "equals": "templatesSpec" },
{ "file": "CLAUDE.md", "pattern": "skills/sd/` \\| ([0-9]+) rule packs", "equals": "skills" },
{ "file": "CONTRIBUTING.md", "pattern": "^ +commands/ +# ([0-9]+) slash commands", "equals": "commands" },
{ "file": "CONTRIBUTING.md", "pattern": "^ +agents/ +# ([0-9]+) subagent definitions", "equals": "agents" },
{ "file": "CONTRIBUTING.md", "pattern": "^ +powershell/ +# ([0-9]+) PowerShell hooks", "equals": "hooksPowerShell" },
{ "file": "CONTRIBUTING.md", "pattern": "^ +bash/ +# ([0-9]+) bash hooks", "equals": "hooksBash" },
{ "file": "CONTRIBUTING.md", "pattern": "^ +templates/ +# ([0-9]+) setup templates", "equals": "templatesSetup" },
{ "file": "CONTRIBUTING.md", "pattern": "^ +specs/ +# ([0-9]+) spec templates", "equals": "templatesSpec" },
{ "file": "install/README.md", "pattern": "commands/sd/ +([0-9]+) slash commands", "equals": "commands" },
{ "file": "install/README.md", "pattern": "agents/sd/ +([0-9]+) subagent definitions", "equals": "agents" },
{ "file": "install/README.md", "pattern": "hooks/sd/ +([0-9]+) hook scripts", "equals": "hooksPowerShell" },
{ "file": "install/README.md", "pattern": "templates/sd/ +([0-9]+) templates", "equals": "templatesTotal" },
{ "file": "install/README.md", "pattern": "templates \\(([0-9]+) setup", "equals": "templatesSetup" },
{ "file": "install/README.md", "pattern": "setup \\+ ([0-9]+) spec\\)", "equals": "templatesSpec" },
{ "file": "install/README.md", "pattern": "skills/sd/ +([0-9]+) skills", "equals": "skills" },
{ "file": "install/README.md", "pattern": "^\\| `commands/` \\| `commands/sd/` \\| ([0-9]+) \\|", "equals": "commands" },
{ "file": "install/README.md", "pattern": "^\\| `agents/` \\| `agents/sd/` \\| ([0-9]+) \\|", "equals": "agents" },
{ "file": "install/README.md", "pattern": "^\\| `hooks/powershell/`.*\\| ([0-9]+) \\|", "equals": "hooksPowerShell" },
{ "file": "install/README.md", "pattern": "^\\| `hooks/bash/`.*\\| ([0-9]+) \\|", "equals": "hooksBash" },
{ "file": "install/README.md", "pattern": "`templates/sd/` \\| ([0-9]+) \\+ [0-9]+ \\|", "equals": "templatesSetup" },
{ "file": "install/README.md", "pattern": "`templates/sd/` \\| [0-9]+ \\+ ([0-9]+) \\|", "equals": "templatesSpec" },
{ "file": "install/README.md", "pattern": "^\\| `skills/` \\| `skills/sd/` \\| ([0-9]+) \\|", "equals": "skills" },
{ "file": "install/README.md", "pattern": "^\\| _\\(generated at install\\)_ \\| `<area>/sd/specwright-version\\.txt` \\| ([0-9]+) \\|", "equals": "installStamps" },
{ "file": "install/README.md", "pattern": "\\*\\*Total\\*\\*: ([0-9]+) files per OS", "equals": "installTotalWithStamps" },
{ "file": "docs/architecture.md", "pattern": "commands/sd/ +([0-9]+) workflow definitions", "equals": "commands" },
{ "file": "docs/architecture.md", "pattern": "agents/sd/ +([0-9]+) subagent prompt files", "equals": "agents" },
{ "file": "docs/architecture.md", "pattern": "hooks/sd/ +([0-9]+) cross-platform hook scripts", "equals": "hooksPowerShell" },
{ "file": "docs/architecture.md", "pattern": "templates/sd/ +([0-9]+) setup \\+", "equals": "templatesSetup" },
{ "file": "docs/architecture.md", "pattern": "setup \\+ ([0-9]+) spec templates", "equals": "templatesSpec" },
{ "file": "docs/architecture.md", "pattern": "skills/sd/ +([0-9]+) reusable rule packs", "equals": "skills" },
{ "file": "docs/architecture.md", "pattern": "lists all ([0-9]+) commands", "equals": "commands" },
{ "file": "docs/architecture.md", "pattern": "^([0-9]+) hooks ship in cross-platform pairs", "equals": "hooksPowerShell" },
{ "file": "docs/architecture.md", "pattern": "^The ([0-9]+) workflow commands have these gate counts", "equals": "workflowCommands" },
{ "file": "commands/setup.md", "pattern": "commands/sd/ +\\(([0-9]+) workflow commands\\)", "equals": "commands" },
{ "file": "commands/setup.md", "pattern": "agents/sd/ +\\(([0-9]+) specialist agents\\)", "equals": "agents" },
{ "file": "commands/setup.md", "pattern": "hooks/sd/ +\\(([0-9]+) hooks\\)", "equals": "hooksPowerShell" },
{ "file": "commands/setup.md", "pattern": "skills/sd/ +\\(([0-9]+) skills:", "equals": "skills" },
{ "file": "README.md", "pattern": "^\\| `/sd:feature [^|]*\\| Workflow \\| ([0-9]+) \\|", "equals": "gatesFeature" },
{ "file": "README.md", "pattern": "^\\| `/sd:bug [^|]*\\| Workflow \\| ([0-9]+) \\|", "equals": "gatesBug" },
{ "file": "README.md", "pattern": "^\\| `/sd:rca [^|]*\\| Workflow \\| ([0-9]+) \\|", "equals": "gatesRca" },
{ "file": "README.md", "pattern": "^\\| `/sd:refactor [^|]*\\| Workflow \\| ([0-9]+) \\|", "equals": "gatesRefactor" },
{ "file": "README.md", "pattern": "^\\| `/sd:perf [^|]*\\| Workflow \\| ([0-9]+) \\|", "equals": "gatesPerf" },
{ "file": "README.md", "pattern": "^\\| `/sd:port [^|]*\\| Workflow \\| ([0-9]+) \\|", "equals": "gatesPort" },
{ "file": "README.md", "pattern": "^\\| `/sd:setup` \\| Utility \\| ([0-9]+) \\|", "equals": "gatesSetup" },
{ "file": "README.md", "pattern": "^\\| `/sd:release [^|]*\\| Utility \\| ([0-9]+) \\|", "equals": "gatesRelease" },
{ "file": "README.md", "pattern": "^\\| `/sd:adr .*\\| Utility \\| ([0-9]+) \\|", "equals": "gatesAdr" },
{ "file": "docs/architecture.md", "pattern": "^\\| `/sd:feature` \\| ([0-9]+) \\|", "equals": "gatesFeature" },
{ "file": "docs/architecture.md", "pattern": "^\\| `/sd:bug` \\| ([0-9]+) \\|", "equals": "gatesBug" },
{ "file": "docs/architecture.md", "pattern": "^\\| `/sd:rca` \\| ([0-9]+) \\|", "equals": "gatesRca" },
{ "file": "docs/architecture.md", "pattern": "^\\| `/sd:refactor` \\| ([0-9]+) \\|", "equals": "gatesRefactor" },
{ "file": "docs/architecture.md", "pattern": "^\\| `/sd:perf` \\| ([0-9]+) \\|", "equals": "gatesPerf" },
{ "file": "docs/architecture.md", "pattern": "^\\| `/sd:port` \\| ([0-9]+) \\|", "equals": "gatesPort" },
{ "file": "commands/feature.md", "pattern": "([0-9]+) hard gates", "equals": "gatesFeature" },
{ "file": "commands/bug.md", "pattern": "([0-9]+) hard gates", "equals": "gatesBug" },
{ "file": "commands/rca.md", "pattern": "([0-9]+) hard gates", "equals": "gatesRca" },
{ "file": "commands/refactor.md", "pattern": "([0-9]+) hard gates", "equals": "gatesRefactor" },
{ "file": "commands/perf.md", "pattern": "([0-9]+) hard gates", "equals": "gatesPerf" },
{ "file": "commands/port.md", "pattern": "([0-9]+) hard gates", "equals": "gatesPort" },
{ "file": "commands/adr.md", "pattern": "([0-9]+) hard gate", "equals": "gatesAdr" },
{ "file": "docs/usage.md", "pattern": "([0-9]+) hard gates", "equals": "gatesFeature" },
{ "file": "docs/adr/0003-adaptive-replan-loop.md", "pattern": "([0-9]+) hard gates", "equals": "gatesFeature" }
],
"versionClaims": [
{ "file": "ROADMAP.md", "pattern": "^Current released version: \\*\\*([0-9]+\\.[0-9]+\\.[0-9]+)\\*\\*" }
],
"$versionClaimsComment": [
"A version claim is a different shape from docClaims: docClaims compares a captured integer",
"against a manifest 'quantities' value derived from an areas/derived count, and needs 'equals'",
"to say which one. A version claim compares a captured x.y.z string against the newest dated",
"'## [x.y.z] - <date>' heading in CHANGELOG.md instead - there is no quantities entry for that,",
"so 'equals' does not apply here and is intentionally absent from these entries.",
"",
"v1 is an EXPLICIT LIST ONLY - unlike docClaims there is no undeclared-claim scan for version",
"strings. A semver-shaped phrase pattern would collide with legitimate non-claim version",
"strings already in the repo (keepachangelog.com/en/1.1.0/, semver.org/spec/v2.0.0.html,",
"illustrative versions in commands/release.md, docs/usage.md, template/fixture content), and",
"policing that needs a second exclusions/vocabulary surface disproportionate to the one real",
"offender this closes (SW-28). If a second tracked doc starts publishing a released-version",
"claim, add it here explicitly rather than building the scan; only build the scan if this list",
"grows enough that omissions become plausible."
],
"$contractLintComment": [
"Contract contract for specwright's PRODUCT (commands/agents/skills), read by",
"scripts/contract-lint.ps1 and scripts/contract-lint.sh (Check 8: cross-file contract lint).",
"Where 'areas' above governs INVENTORY (how many files exist), this subtree governs the",
"RELATIONSHIPS between those files - which agent a command invokes, which skill an agent",
"loads, how many hard gates a workflow declares.",
"",
"THE COUNTS QUESTION. The manifest's charter at the top says it stores no counts, and",
"'gates.<command>.hard' is a literal number. That is deliberate, and the two are not in",
"conflict, because the charter governs inventory. The test is:",
"",
" Can a script count it from disk with no judgement calls?",
" YES -> it is inventory. It belongs in 'areas' and must be derived.",
" NO -> it is a declared design contract. It belongs here, written down.",
"",
"'How many command files exist' passes that test, so hardcoding it is pure liability.",
"'How many hard gates /sd:feature declares' does NOT: nothing on disk is a second source",
"for it. If it were derived, CL302 would compare disk against itself and pass vacuously",
"forever - exactly the rot Check 7's vacuous-claim rule exists to catch. The number's job",
"is to make deleting a '### Gate 4' heading a deliberate two-file edit that shows up in",
"review. Feeding these into Check 7 as quantities gives README <- manifest <- disk, hence",
"transitively README == disk, with no gate parser duplicated into validate.",
"",
"FOUR FALSE POSITIVES the gate parser handles WITHOUT an exclusion list. Do not re-add one:",
" 1. 'commands/status.md' - '## Gate activity' is a report section, not a gate. Rejected",
" because 'Gate' followed by a lowercase word is never a gate heading.",
" 2. 'commands/feature.md' - '**Face B - Gate Complexity (HARD)**' is bold text, not a",
" heading. Rejected because a gate must match '^#{2,3} '.",
" 3. 'commands/bug.md' - 'Gate 3a' is authored BEFORE 'Gate 3'. CL303 compares SETS, never",
" file order.",
" 4. The ~20 literal STOPs in Phase 0 bootstrap error paths. CL300 only looks inside a gate",
" block, which ends at the next heading of any level.",
"",
"SCAN SCOPE IS LOAD-BEARING. It is commands/ agents/ skills/ and nothing else on purpose.",
"CLAUDE.md and CONTRIBUTING.md use 'sd-test' as a sandbox path and docs/architecture.md",
"carries a 'name: sd-debugger' frontmatter example - adding docs/** here produces ~18 CL001",
"false positives on day one.",
"",
"SEVERITY LIVES HERE, NEVER IN A RULE. Both linters look severity up from rules[] below, so",
"a BLOCK/WARN divergence between the PowerShell and bash twins is structurally impossible.",
"A registry parity guard inside each linter asserts that the set of ids below equals the set",
"of rules it dispatches, and exits 2 if not - so adding a wave-2 rule cannot be half-done."
],
"contractLint": {
"scanScope": ["commands/*.md", "agents/*.md", "skills/*/SKILL.md"],
"installNamespaceSegment": "sd",
"rules": [
{ "id": "CL001", "severity": "BLOCK", "wave": 1, "summary": "sd- reference resolving to no agent and no skill" },
{ "id": "CL002", "severity": "BLOCK", "wave": 1, "summary": "skills: frontmatter entry with no matching SKILL.md" },
{ "id": "CL003", "severity": "BLOCK", "wave": 1, "summary": "unresolved sd- reference on a skill-decorated line" },
{ "id": "CL004", "severity": "WARN", "wave": 1, "summary": "skill referenced by nobody in scan scope and not declared in skillConsumers" },
{ "id": "CL005", "severity": "BLOCK", "wave": 1, "summary": "templates/ path that does not exist on disk" },
{ "id": "CL006", "severity": "BLOCK", "wave": 1, "summary": "/sd:<name> reference with no matching command file" },
{ "id": "CL007", "severity": "WARN", "wave": 1, "summary": "agent mentioned by no command body" },
{ "id": "CL008", "severity": "BLOCK", "wave": 1, "summary": "numbered .specs artifact filename absent from specArtifacts" },
{ "id": "CL100", "severity": "BLOCK", "wave": 2, "summary": "invocation sets TASK/WORKFLOW_TYPE/TASK_TYPE to a mode the target agent does not declare" },
{ "id": "CL101", "severity": "WARN", "wave": 2, "summary": "agent declares a mode no command ever invokes" },
{ "id": "CL102", "severity": "BLOCK", "wave": 2, "summary": "invocation omits an input the declared mode marks required" },
{ "id": "CL103", "severity": "WARN", "wave": 2, "summary": "invocation passes an input token the declared mode declares nowhere" },
{ "id": "CL104", "severity": "BLOCK", "wave": 2, "summary": "two agent files share a frontmatter name:" },
{ "id": "CL200", "severity": "BLOCK", "wave": 3, "summary": "agent with no write tool is instructed to write, append or create" },
{ "id": "CL201", "severity": "BLOCK", "wave": 3, "summary": "agent listed in contractLint.readOnlyAgents declares a write tool" },
{ "id": "CL202", "severity": "WARN", "wave": 3, "summary": "mcp__* name absent from contractLint.knownMcpTools" },
{ "id": "CL203", "severity": "WARN", "wave": 3, "summary": "agent frontmatter declares a tool never mentioned in its own body" },
{ "id": "CL300", "severity": "BLOCK", "wave": 1, "summary": "gate block containing no literal STOP" },
{ "id": "CL301", "severity": "BLOCK", "wave": 1, "summary": "gate block offering no option set" },
{ "id": "CL302", "severity": "BLOCK", "wave": 1, "summary": "hard gate count on disk disagrees with gates.<file>.hard" },
{ "id": "CL303", "severity": "WARN", "wave": 1, "summary": "hard gate numbering is not exactly 1..N without duplicates" },
{ "id": "CL304", "severity": "BLOCK", "wave": 1, "summary": "conditional gate on disk undeclared, or declared and absent" },
{ "id": "CL305", "severity": "BLOCK", "wave": 1, "summary": "HARD gate listing an override token as a selectable option" },
{ "id": "CL306", "severity": "BLOCK", "wave": 3, "summary": "HARD gate prose describes an escape hatch with no contract-lint: allow suppression" },
{ "id": "CL400", "severity": "BLOCK", "wave": 3, "summary": "hardcoded stack command token outside a <<placeholder>>, fenced example or suppression comment" },
{ "id": "CL401", "severity": "WARN", "wave": 3, "summary": "hardcoded language/framework name in the same contexts (stays WARN permanently - often legitimate in prose)" },
{ "id": "CL402", "severity": "BLOCK", "wave": 3, "summary": "hardcoded absolute filesystem path (Windows drive letter or POSIX multi-segment) in scan scope" },
{ "id": "CL500", "severity": "WARN", "wave": 4, "summary": "file exceeds contractLint.budgets.<area>Bytes (stays WARN permanently - a ratchet, not a build gate)" },
{ "id": "CL900", "severity": "BLOCK", "wave": 1, "summary": "suppression comment carrying no usable reason" },
{ "id": "CL901", "severity": "BLOCK", "wave": 1, "summary": "suppression naming a rule id absent from this registry" },
{ "id": "CL902", "severity": "WARN", "wave": 1, "summary": "suppression that suppressed no finding" }
],
"gates": {
"commands/feature.md": { "quantity": "gatesFeature", "hard": 3, "conditional": ["Re-plan"] },
"commands/bug.md": { "quantity": "gatesBug", "hard": 5, "conditional": ["3a"] },
"commands/rca.md": { "quantity": "gatesRca", "hard": 3, "conditional": [] },
"commands/refactor.md": { "quantity": "gatesRefactor", "hard": 6, "conditional": ["Re-plan"] },
"commands/perf.md": { "quantity": "gatesPerf", "hard": 8, "conditional": [] },
"commands/port.md": { "quantity": "gatesPort", "hard": 6, "conditional": [] },
"commands/setup.md": { "quantity": "gatesSetup", "hard": 2, "conditional": [] },
"commands/release.md": { "quantity": "gatesRelease", "hard": 1, "conditional": [] },
"commands/adr.md": { "quantity": "gatesAdr", "hard": 1, "conditional": [] },
"skills/sd-replan-loop/SKILL.md": { "quantity": null, "hard": 0, "conditional": ["Re-plan"] }
},
"$gatesComment": [
"quantity is the Check 7 name this gate count is published under. null means the gate",
"block is real but nothing in the docs cites a number for it - the Re-plan protocol is",
"defined once in the skill and reached from commands/feature.md and commands/refactor.md,",
"so its count belongs to those workflows, not to the skill. Check 7's seeding loop skips",
"null quantities; CL302 and CL304 do not care either way."
],
"specArtifacts": [
"00-spec.md",
"01-plan.md",
"02-tasks.md",
"03-decisions.md",
"05-retro.md",
"06-verify.md"
],
"skillConsumers": {
"sd-retro-lessons": "scripts/validate-lessons.{ps1,sh}, scripts/aggregate-lessons.{ps1,sh}"
},
"overrideOptionTokens": ["skip", "override", "proceed anyway", "bypass", "force", "ignore"],
"gateProseEscapeTokens": ["insists on", "may override", "proceed anyway", "bypass this gate"],
"$gateProseEscapeTokensComment": [
"CL306's vocabulary, scoped to HARD gate blocks only. Deliberately narrow to control the",
"false-positive band this rule occupies (see docs/contract-lint.md) - it does not try to",
"catch every conceivable escape hatch, only the phrasings already proven to appear in",
"commands/bug.md and commands/release.md. A HARD gate whose prose matches one of these",
"phrases needs a '<!-- contract-lint: allow CL306 - <reason> -->' comment, the same",
"suppression convention CL305 already uses on commands/perf.md - there is no separate",
"per-gate declared-exception surface, on purpose."
],
"stackTokens": {
"commands": ["dotnet", "npm", "npx", "yarn", "pnpm", "pytest", "mvn", "gradle", "cargo", "go test", "rake", "bundle exec", "composer"],
"languages": ["C#", ".NET", "TypeScript", "JavaScript", "Python", "Java", "Go", "Rust", "Ruby", "PHP", "MSSQL", "SQL Server", "PostgreSQL", "MySQL", "MongoDB"]
},
"$stackTokensComment": [
"Hand-maintained, same category as overrideOptionTokens and knownMcpTools - nothing on",
"disk is a second source for 'which tokens count as a hardcoded stack assumption'.",
"commands.* is CL400's vocabulary (BLOCK, promoted from WARN 2026-07-31 after a clean",
"run across the engine tree under both implementations);",
"languages.* is CL401's (permanent WARN - a language name in prose is often legitimate,",
"e.g. an 'or the project's equivalent' example list). Both rules skip text inside",
"<<...>> placeholders, fenced code blocks, and lines covered by a",
"'<!-- contract-lint: allow CL40x - <reason> -->' comment."
],
"readOnlyAgents": ["sd-code-explorer", "sd-reviewer", "sd-debugger"],
"$readOnlyAgentsComment": [
"A DECLARED promise, not a derived fact: these three agents are architecturally",
"committed to staying read-only, and CL201 is the only rule that reads this list -",
"it fires the moment one of them grows Write/Edit/MultiEdit in its own tools: line.",
"CL200 does NOT consult this list. CL200 asks 'does THIS agent's frontmatter carry a",
"write tool right now', straight off disk, for every agent - so a new read-only agent",
"is protected on day one even if nobody remembers to add it here. This list exists so",
"the THREE AGENTS ABOVE SPECIFICALLY can never quietly gain a write tool, the same",
"declared-vs-disk shape CL304 already uses for conditional gates."
],
"knownMcpTools": [
"mcp__sequential-thinking__sequentialthinking",
"mcp__gitnexus__context",
"mcp__gitnexus__impact",
"mcp__gitnexus__query",
"mcp__gitnexus__list_repos",
"mcp__tavily__tavily_search",
"mcp__context7__query-docs",
"mcp__context7__resolve-library-id",
"mcp__atlassian__getJiraIssue",
"mcp__atlassian__searchJiraIssuesUsingJql",
"mcp__atlassian__getJiraIssueRemoteIssueLinks",
"mcp__atlassian__getConfluencePage"
],
"$knownMcpToolsComment": [
"Hand-maintained on purpose, not derived: nothing on disk is a second source for",
"which mcp__* tool names are real, since that comes from runtime MCP server",
"configuration this repo cannot see. That fails the manifest's own derivability",
"test (see $contractLintComment above), so it belongs here, written down, same",
"category as overrideOptionTokens and skillConsumers.",
"",
"THE DELIBERATE FAILURE MODE: this list goes stale the moment a new MCP tool is",
"wired into an agent and this file is not updated in the same PR. CL202 stays WARN",
"forever (see contractLint.rules above) specifically because of that risk - a stale",
"allowlist must never be able to block CI. Treat a CL202 hit as 'update this list or",
"explain why not', never as 'suppress and move on'."
],
"budgets": {
"commandsBytes": 32677,
"agentsBytes": 15232,
"skillsBytes": 12412
},
"$budgetsComment": [
"CL500's vocabulary. A RATCHET, not a design target: each ceiling is set at today's",
"largest file in that area, so the repo is green by construction on day one and every",
"later WARN is real growth, never a paragraph someone happened to write. CL500 stays",
"WARN forever (see contractLint.rules above) - promoting it to BLOCK would turn a",
"judgement call into a build failure, and the judgement is the valuable part. Raising",
"a number here is a real decision that belongs in a PR description, never a reflex to",
"a red CI run.",
"",
"The byte count is NORMALIZED, never a raw disk read: the sum of each line's UTF-8",
"byte length plus one separator per line boundary, computed off the same per-line",
"cache every other rule already reads. A raw ReadAllBytes().Length / `wc -c` would",
"make this rule disagree with itself between platforms for byte-identical content -",
"contractLint.scanScope is 'text=auto' (see .gitattributes), so *.md checks out LF on",
"a Linux CI runner and CRLF on Windows. Confirmed on this repo: commands/spec.md is",
"25979 bytes as a git blob (LF) but 26521 bytes on a native Windows checkout - a 542",
"byte, ~2% difference for identical content. Line count was rejected for the same",
"reason CLAUDE.md rejects it elsewhere: it is not a measure both implementations can",
"agree on without arguing about line endings."
]
}
}