@jamie-tam/forge 6.0.0

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 (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +389 -0
  3. package/agents/architect.md +92 -0
  4. package/agents/builder.md +122 -0
  5. package/agents/code-reviewer.md +107 -0
  6. package/agents/concept-designer.md +207 -0
  7. package/agents/craft-reviewer.md +132 -0
  8. package/agents/critic.md +130 -0
  9. package/agents/doc-writer.md +85 -0
  10. package/agents/dreamer.md +129 -0
  11. package/agents/e2e-runner.md +89 -0
  12. package/agents/gotcha-hunter.md +127 -0
  13. package/agents/prototype-builder.md +193 -0
  14. package/agents/prototype-codifier.md +204 -0
  15. package/agents/prototype-reviewer.md +163 -0
  16. package/agents/security-reviewer.md +108 -0
  17. package/agents/spec-reviewer.md +94 -0
  18. package/agents/tracer.md +98 -0
  19. package/agents/wireframer.md +109 -0
  20. package/commands/abort.md +25 -0
  21. package/commands/bugfix.md +151 -0
  22. package/commands/evolve.md +118 -0
  23. package/commands/feature.md +236 -0
  24. package/commands/forge.md +100 -0
  25. package/commands/greenfield.md +185 -0
  26. package/commands/hotfix.md +98 -0
  27. package/commands/refactor.md +147 -0
  28. package/commands/resume.md +25 -0
  29. package/commands/setup.md +201 -0
  30. package/commands/status.md +27 -0
  31. package/commands/task-force.md +110 -0
  32. package/commands/validate.md +12 -0
  33. package/dist/__tests__/active-manifest.test.js +272 -0
  34. package/dist/__tests__/copy.test.js +96 -0
  35. package/dist/__tests__/gate-check.test.js +384 -0
  36. package/dist/__tests__/wiki.test.js +472 -0
  37. package/dist/__tests__/work-manifest.test.js +304 -0
  38. package/dist/active-manifest.js +229 -0
  39. package/dist/cli.js +158 -0
  40. package/dist/copy.js +124 -0
  41. package/dist/gate-check.js +326 -0
  42. package/dist/hooks.js +60 -0
  43. package/dist/init.js +140 -0
  44. package/dist/manifest.js +90 -0
  45. package/dist/merge.js +77 -0
  46. package/dist/paths.js +36 -0
  47. package/dist/uninstall.js +216 -0
  48. package/dist/update.js +158 -0
  49. package/dist/verify-manifest.js +65 -0
  50. package/dist/verify.js +98 -0
  51. package/dist/wiki-ui.js +310 -0
  52. package/dist/wiki.js +364 -0
  53. package/dist/work-manifest.js +798 -0
  54. package/hooks/config/gate-requirements.json +79 -0
  55. package/hooks/hooks.json +143 -0
  56. package/hooks/scripts/analyze-telemetry.sh +114 -0
  57. package/hooks/scripts/gate-enforcer.sh +164 -0
  58. package/hooks/scripts/pre-compact.sh +90 -0
  59. package/hooks/scripts/session-start.sh +81 -0
  60. package/hooks/scripts/telemetry.sh +41 -0
  61. package/hooks/scripts/wiki-lint.sh +87 -0
  62. package/hooks/templates/AGENTS.md.template +48 -0
  63. package/hooks/templates/CLAUDE.md.template +45 -0
  64. package/package.json +55 -0
  65. package/protocols/README.md +40 -0
  66. package/protocols/codex.md +151 -0
  67. package/protocols/graphify.md +156 -0
  68. package/references/common/agent-coordination.md +65 -0
  69. package/references/common/coding-standards.md +54 -0
  70. package/references/common/feature-tracking.md +21 -0
  71. package/references/common/io-protocol.md +36 -0
  72. package/references/common/phases.md +57 -0
  73. package/references/common/quality-gates.md +130 -0
  74. package/references/common/skill-authoring.md +154 -0
  75. package/references/common/skill-compliance.md +30 -0
  76. package/references/python/standards.md +44 -0
  77. package/references/react/standards.md +61 -0
  78. package/references/typescript/standards.md +42 -0
  79. package/rules/common/forge-system.md +59 -0
  80. package/rules/common/git-workflow.md +40 -0
  81. package/rules/common/guardrails.md +37 -0
  82. package/rules/common/quality-gates.md +18 -0
  83. package/rules/common/security.md +50 -0
  84. package/rules/common/skill-selection.md +78 -0
  85. package/rules/common/testing.md +58 -0
  86. package/rules/common/verification.md +39 -0
  87. package/skills/build-pr-workflow/SKILL.md +301 -0
  88. package/skills/build-pr-workflow/references/pr-template.md +62 -0
  89. package/skills/build-pr-workflow/references/subagent-merge.md +47 -0
  90. package/skills/build-pr-workflow/references/worktree-setup.md +125 -0
  91. package/skills/build-prototype/SKILL.md +264 -0
  92. package/skills/build-scaffold/SKILL.md +340 -0
  93. package/skills/build-tdd/SKILL.md +89 -0
  94. package/skills/build-wireframe/SKILL.md +110 -0
  95. package/skills/build-wireframe/assets/baseline-template.html +486 -0
  96. package/skills/build-wireframe/references/demo-walkthroughs.md +170 -0
  97. package/skills/build-wireframe/references/gotchas.md +188 -0
  98. package/skills/build-wireframe/references/legend-lines.md +141 -0
  99. package/skills/concept-slides/SKILL.md +192 -0
  100. package/skills/deliver-db-migration/SKILL.md +466 -0
  101. package/skills/deliver-deploy/SKILL.md +407 -0
  102. package/skills/deliver-onboarding/SKILL.md +198 -0
  103. package/skills/deliver-onboarding/references/document-templates.md +393 -0
  104. package/skills/deliver-onboarding/templates/getting-started.md +122 -0
  105. package/skills/discover-codebase-analysis/SKILL.md +448 -0
  106. package/skills/discover-requirements/SKILL.md +418 -0
  107. package/skills/discover-requirements/templates/prd.md +99 -0
  108. package/skills/discover-requirements/templates/technical-spec.md +123 -0
  109. package/skills/discover-requirements/templates/user-stories.md +76 -0
  110. package/skills/harden/SKILL.md +214 -0
  111. package/skills/iterate-prototype/SKILL.md +241 -0
  112. package/skills/plan-architecture/SKILL.md +457 -0
  113. package/skills/plan-architecture/templates/adr-template.md +52 -0
  114. package/skills/plan-architecture/templates/api-contract.md +99 -0
  115. package/skills/plan-architecture/templates/db-schema.md +81 -0
  116. package/skills/plan-architecture/templates/system-design.md +111 -0
  117. package/skills/plan-brainstorm/SKILL.md +433 -0
  118. package/skills/plan-design-system/SKILL.md +279 -0
  119. package/skills/plan-task-decompose/SKILL.md +454 -0
  120. package/skills/quality-code-review/SKILL.md +286 -0
  121. package/skills/quality-security-audit/SKILL.md +292 -0
  122. package/skills/quality-security-audit/references/audit-report-template.md +89 -0
  123. package/skills/quality-security-audit/references/owasp-checks.md +178 -0
  124. package/skills/quality-test-execution/SKILL.md +435 -0
  125. package/skills/quality-test-plan/SKILL.md +297 -0
  126. package/skills/quality-test-plan/references/test-type-guide.md +263 -0
  127. package/skills/quality-test-plan/templates/e2e-test-plan.md +72 -0
  128. package/skills/quality-test-plan/templates/integration-test-plan.md +74 -0
  129. package/skills/quality-test-plan/templates/load-test-plan.md +111 -0
  130. package/skills/quality-test-plan/templates/smoke-test-plan.md +68 -0
  131. package/skills/quality-test-plan/templates/unit-test-plan.md +56 -0
  132. package/skills/quality-uiux/SKILL.md +481 -0
  133. package/skills/support-debug/SKILL.md +464 -0
  134. package/skills/support-dream/SKILL.md +213 -0
  135. package/skills/support-gotcha/SKILL.md +249 -0
  136. package/skills/support-runtime-reachability/SKILL.md +190 -0
  137. package/skills/support-runtime-reachability/scripts/__fixtures__/case-01-passes-app-use/src/app.ts +7 -0
  138. package/skills/support-runtime-reachability/scripts/__fixtures__/case-01-passes-app-use/src/handlers/cases.ts +7 -0
  139. package/skills/support-runtime-reachability/scripts/__fixtures__/case-02-orphan-no-app-use/src/app.ts +8 -0
  140. package/skills/support-runtime-reachability/scripts/__fixtures__/case-02-orphan-no-app-use/src/handlers/cases.ts +7 -0
  141. package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/App.tsx +5 -0
  142. package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/components/RingingBanner.tsx +7 -0
  143. package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/hooks/useTwilio.ts +6 -0
  144. package/skills/support-runtime-reachability/scripts/__fixtures__/case-04-jsx-component-rendered/src/App.tsx +5 -0
  145. package/skills/support-runtime-reachability/scripts/__fixtures__/case-04-jsx-component-rendered/src/components/MyComp.tsx +3 -0
  146. package/skills/support-runtime-reachability/scripts/__fixtures__/case-05-jsx-component-not-rendered/src/App.tsx +3 -0
  147. package/skills/support-runtime-reachability/scripts/__fixtures__/case-05-jsx-component-not-rendered/src/components/Orphan.tsx +3 -0
  148. package/skills/support-runtime-reachability/scripts/__fixtures__/case-06-class-instantiated/src/lib/Service.ts +6 -0
  149. package/skills/support-runtime-reachability/scripts/__fixtures__/case-06-class-instantiated/src/main.ts +4 -0
  150. package/skills/support-runtime-reachability/scripts/__fixtures__/case-07-class-not-instantiated/src/lib/Lonely.ts +5 -0
  151. package/skills/support-runtime-reachability/scripts/__fixtures__/case-07-class-not-instantiated/src/main.ts +2 -0
  152. package/skills/support-runtime-reachability/scripts/__fixtures__/case-08-default-export-imported-and-called/src/handler.ts +3 -0
  153. package/skills/support-runtime-reachability/scripts/__fixtures__/case-08-default-export-imported-and-called/src/main.ts +3 -0
  154. package/skills/support-runtime-reachability/scripts/__fixtures__/case-09-default-export-orphan/src/handler.ts +3 -0
  155. package/skills/support-runtime-reachability/scripts/__fixtures__/case-09-default-export-orphan/src/main.ts +2 -0
  156. package/skills/support-runtime-reachability/scripts/__fixtures__/case-10-aliased-named-export/src/lib.ts +5 -0
  157. package/skills/support-runtime-reachability/scripts/__fixtures__/case-10-aliased-named-export/src/main.ts +3 -0
  158. package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/lib/index.ts +1 -0
  159. package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/lib/internal.ts +3 -0
  160. package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/main.ts +3 -0
  161. package/skills/support-runtime-reachability/scripts/__fixtures__/case-12-test-only-caller/src/util.test.ts +5 -0
  162. package/skills/support-runtime-reachability/scripts/__fixtures__/case-12-test-only-caller/src/util.ts +3 -0
  163. package/skills/support-runtime-reachability/scripts/__fixtures__/case-13-gated-pending-annotation/src/future.ts +4 -0
  164. package/skills/support-runtime-reachability/scripts/__fixtures__/case-14-untraceable-annotation/src/decorated.ts +4 -0
  165. package/skills/support-runtime-reachability/scripts/__fixtures__/case-15-untraceable-empty/src/lazy.ts +4 -0
  166. package/skills/support-runtime-reachability/scripts/__fixtures__/case-16-python-module/src/lib.py +15 -0
  167. package/skills/support-runtime-reachability/scripts/__fixtures__/case-16-python-module/src/main.py +5 -0
  168. package/skills/support-runtime-reachability/scripts/__fixtures__/case-17-router-use/src/parent.ts +5 -0
  169. package/skills/support-runtime-reachability/scripts/__fixtures__/case-17-router-use/src/routes/cases.ts +5 -0
  170. package/skills/support-runtime-reachability/scripts/__fixtures__/case-18-shadowed-name-fp/src/lib/foo.ts +3 -0
  171. package/skills/support-runtime-reachability/scripts/__fixtures__/case-18-shadowed-name-fp/src/other.ts +8 -0
  172. package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/handlers/cases.ts +4 -0
  173. package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/handlers/users.ts +4 -0
  174. package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/main.ts +5 -0
  175. package/skills/support-runtime-reachability/scripts/__fixtures__/case-20-aliased-import-usage/src/handlers/cases.ts +3 -0
  176. package/skills/support-runtime-reachability/scripts/__fixtures__/case-20-aliased-import-usage/src/main.ts +4 -0
  177. package/skills/support-runtime-reachability/scripts/__fixtures__/case-21-mixed-default-and-named/src/lib.ts +5 -0
  178. package/skills/support-runtime-reachability/scripts/__fixtures__/case-21-mixed-default-and-named/src/main.ts +5 -0
  179. package/skills/support-runtime-reachability/scripts/__fixtures__/case-22-dynamic-import-then-caller/src/lib.ts +3 -0
  180. package/skills/support-runtime-reachability/scripts/__fixtures__/case-22-dynamic-import-then-caller/src/main.ts +8 -0
  181. package/skills/support-runtime-reachability/scripts/__fixtures__/case-23-dynamic-import-with-space/src/lib.ts +3 -0
  182. package/skills/support-runtime-reachability/scripts/__fixtures__/case-23-dynamic-import-with-space/src/main.ts +7 -0
  183. package/skills/support-runtime-reachability/scripts/check.mjs +638 -0
  184. package/skills/support-runtime-reachability/scripts/check.test.mjs +244 -0
  185. package/skills/support-skill-validator/SKILL.md +194 -0
  186. package/skills/support-skill-validator/references/false-positives.md +59 -0
  187. package/skills/support-skill-validator/references/validation-checks.md +280 -0
  188. package/skills/support-system-guide/SKILL.md +311 -0
  189. package/skills/support-task-force/SKILL.md +265 -0
  190. package/skills/support-task-force/references/dispatch-pattern.md +178 -0
  191. package/skills/support-task-force/references/synthesis-template.md +126 -0
  192. package/skills/support-wiki-bootstrap/SKILL.md +37 -0
  193. package/skills/support-wiki-lint/SKILL.md +196 -0
  194. package/skills/support-wiki-lint/scripts/lint.mjs +488 -0
  195. package/skills/support-wiki-lint/scripts/lint.test.mjs +196 -0
  196. package/templates/README.md +23 -0
  197. package/templates/aiwiki/CLAUDE.md.template +78 -0
  198. package/templates/aiwiki/schemas/architecture.md +118 -0
  199. package/templates/aiwiki/schemas/convention.md +112 -0
  200. package/templates/aiwiki/schemas/decision.md +144 -0
  201. package/templates/aiwiki/schemas/gotcha.md +118 -0
  202. package/templates/aiwiki/schemas/oracle.md +105 -0
  203. package/templates/aiwiki/schemas/session.md +125 -0
  204. package/templates/manifests/bugfix.yaml +41 -0
  205. package/templates/manifests/feature.yaml +69 -0
  206. package/templates/manifests/greenfield.yaml +61 -0
  207. package/templates/manifests/hotfix.yaml +45 -0
  208. package/templates/manifests/refactor.yaml +44 -0
  209. package/templates/manifests/v5/SCHEMA.md +327 -0
  210. package/templates/manifests/v5/feature.yaml +77 -0
  211. package/templates/manifests/v6/SCHEMA.md +199 -0
  212. package/templates/wiki-html/dream-detail.html +378 -0
  213. package/templates/wiki-html/dreams-list.html +155 -0
@@ -0,0 +1,244 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import * as path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { check } from './check.mjs';
6
+
7
+ const here = path.dirname(fileURLToPath(import.meta.url));
8
+ const fxRoot = path.join(here, '__fixtures__');
9
+
10
+ function fx(caseDir) {
11
+ return path.join(fxRoot, caseDir);
12
+ }
13
+
14
+ function findExport(result, name) {
15
+ const e = result.exports.find((x) => x.name === name);
16
+ if (!e) throw new Error(`expected export '${name}' in result, got: ${result.exports.map((x) => x.name).join(', ')}`);
17
+ return e;
18
+ }
19
+
20
+ test('case-01: route mounted via app.use is reachable', () => {
21
+ const result = check({ files: ['src/handlers/cases.ts'], root: fx('case-01-passes-app-use') });
22
+ assert.equal(result.exports.length, 1);
23
+ const e = findExport(result, 'casesRouter');
24
+ assert.equal(e.kind, 'const');
25
+ assert.equal(e.annotation, null);
26
+ assert.ok(e.production_callers.length >= 1, 'expected at least one production caller');
27
+ assert.ok(
28
+ e.production_callers.some((c) => c.pattern === 'mount'),
29
+ 'expected an app.use mount caller',
30
+ );
31
+ assert.equal(result.stats.orphans_unannotated, 0);
32
+ });
33
+
34
+ test('case-02: route imported but never mounted is orphan', () => {
35
+ const result = check({ files: ['src/handlers/cases.ts'], root: fx('case-02-orphan-no-app-use') });
36
+ const e = findExport(result, 'casesRouter');
37
+ assert.equal(e.production_callers.length, 0);
38
+ assert.equal(e.annotation, null);
39
+ assert.equal(result.stats.orphans_unannotated, 1);
40
+ });
41
+
42
+ test('case-03: hook imported but never invoked is orphan', () => {
43
+ const result = check({ files: ['src/hooks/useTwilio.ts'], root: fx('case-03-orphan-import-only') });
44
+ const e = findExport(result, 'useTwilioDevice');
45
+ assert.equal(e.production_callers.length, 0);
46
+ assert.equal(result.stats.orphans_unannotated, 1);
47
+ });
48
+
49
+ test('case-04: JSX component rendered is reachable', () => {
50
+ const result = check({ files: ['src/components/MyComp.tsx'], root: fx('case-04-jsx-component-rendered') });
51
+ const e = findExport(result, 'MyComp');
52
+ assert.ok(
53
+ e.production_callers.some((c) => c.pattern === 'jsx'),
54
+ 'expected JSX caller',
55
+ );
56
+ });
57
+
58
+ test('case-05: JSX component not rendered is orphan', () => {
59
+ const result = check({ files: ['src/components/Orphan.tsx'], root: fx('case-05-jsx-component-not-rendered') });
60
+ const e = findExport(result, 'Orphan');
61
+ assert.equal(e.production_callers.length, 0);
62
+ });
63
+
64
+ test('case-06: class instantiated via new is reachable', () => {
65
+ const result = check({ files: ['src/lib/Service.ts'], root: fx('case-06-class-instantiated') });
66
+ const e = findExport(result, 'Service');
67
+ assert.equal(e.kind, 'class');
68
+ assert.ok(
69
+ e.production_callers.some((c) => c.pattern === 'new'),
70
+ 'expected new caller',
71
+ );
72
+ });
73
+
74
+ test('case-07: class never instantiated is orphan', () => {
75
+ const result = check({ files: ['src/lib/Lonely.ts'], root: fx('case-07-class-not-instantiated') });
76
+ const e = findExport(result, 'Lonely');
77
+ assert.equal(e.production_callers.length, 0);
78
+ });
79
+
80
+ test('case-08: default export imported and called is reachable', () => {
81
+ const result = check({ files: ['src/handler.ts'], root: fx('case-08-default-export-imported-and-called') });
82
+ const e = findExport(result, 'handler');
83
+ assert.equal(e.kind, 'default');
84
+ assert.ok(e.production_callers.length >= 1, 'expected production caller for default export');
85
+ });
86
+
87
+ test('case-09: default export with no caller is orphan', () => {
88
+ const result = check({ files: ['src/handler.ts'], root: fx('case-09-default-export-orphan') });
89
+ const e = findExport(result, 'handler');
90
+ assert.equal(e.production_callers.length, 0);
91
+ });
92
+
93
+ test('case-10: aliased named export resolves via alias', () => {
94
+ const result = check({ files: ['src/lib.ts'], root: fx('case-10-aliased-named-export') });
95
+ const e = findExport(result, 'bar');
96
+ assert.equal(e.kind, 'reexport');
97
+ assert.ok(e.production_callers.length >= 1, 'expected caller via alias');
98
+ });
99
+
100
+ test('case-11: re-export chain forwards to consumer', () => {
101
+ // The slice diff includes the internal export; the consumer imports from the index re-export.
102
+ const result = check({ files: ['src/lib/internal.ts'], root: fx('case-11-re-export-chain') });
103
+ const e = findExport(result, 'doStuff');
104
+ // The consumer (main.ts) imports from './lib' (index), not directly from internal. The check
105
+ // should recognize this via the index file's re-export OR the consumer's call.
106
+ assert.ok(
107
+ e.production_callers.length >= 1,
108
+ `expected caller via re-export chain, got: ${JSON.stringify(e.production_callers)}`,
109
+ );
110
+ });
111
+
112
+ test('case-12: test-only callers do not count as production', () => {
113
+ const result = check({ files: ['src/util.ts'], root: fx('case-12-test-only-caller') });
114
+ const e = findExport(result, 'helperOnly');
115
+ assert.equal(e.production_callers.length, 0);
116
+ assert.ok(e.test_callers.length >= 1, 'expected the test caller to land in test_callers');
117
+ });
118
+
119
+ test('case-13: gated-pending annotation is reported', () => {
120
+ const result = check({ files: ['src/future.ts'], root: fx('case-13-gated-pending-annotation') });
121
+ const e = findExport(result, 'futureHandler');
122
+ assert.deepEqual(e.annotation, {
123
+ kind: 'gated-pending',
124
+ value: 'phase-2-wiring',
125
+ raw: '// gated-pending: phase-2-wiring',
126
+ });
127
+ assert.equal(e.production_callers.length, 0);
128
+ assert.equal(result.stats.orphans_unannotated, 0); // annotated, not unannotated orphan
129
+ });
130
+
131
+ test('case-14: untraceable annotation with rationale is reported', () => {
132
+ const result = check({ files: ['src/decorated.ts'], root: fx('case-14-untraceable-annotation') });
133
+ // Named default export: `export default function GET(...)`.
134
+ const e = findExport(result, 'GET');
135
+ assert.equal(e.kind, 'default');
136
+ assert.equal(e.annotation?.kind, 'untraceable');
137
+ assert.equal(e.annotation?.value, 'framework-mounted (Next.js app router)');
138
+ });
139
+
140
+ test('case-15: untraceable with empty rationale reports empty value', () => {
141
+ const result = check({ files: ['src/lazy.ts'], root: fx('case-15-untraceable-empty') });
142
+ const e = findExport(result, 'lazy');
143
+ assert.equal(e.annotation?.kind, 'untraceable');
144
+ assert.equal(e.annotation?.value, ''); // agent decides this is malformed
145
+ });
146
+
147
+ test('case-16: Python module exports filter underscore names', () => {
148
+ const result = check({ files: ['src/lib.py'], root: fx('case-16-python-module') });
149
+ const names = result.exports.map((e) => e.name).sort();
150
+ assert.deepEqual(names, ['PublicClass', 'public_fn']);
151
+
152
+ const fn = findExport(result, 'public_fn');
153
+ assert.equal(fn.kind, 'def');
154
+ assert.ok(fn.production_callers.length >= 1, 'expected python from-import caller');
155
+
156
+ const cls = findExport(result, 'PublicClass');
157
+ assert.equal(cls.kind, 'pyclass');
158
+ assert.equal(cls.production_callers.length, 0); // never instantiated
159
+ });
160
+
161
+ test('case-17: router.use mount counts as production caller', () => {
162
+ const result = check({ files: ['src/routes/cases.ts'], root: fx('case-17-router-use') });
163
+ const e = findExport(result, 'casesRouter');
164
+ assert.ok(
165
+ e.production_callers.some((c) => c.pattern === 'mount'),
166
+ 'expected router.use mount caller',
167
+ );
168
+ });
169
+
170
+ test('case-18: same-name caller without import is not counted (FP mitigation)', () => {
171
+ const result = check({ files: ['src/lib/foo.ts'], root: fx('case-18-shadowed-name-fp') });
172
+ const e = findExport(result, 'shared');
173
+ assert.equal(
174
+ e.production_callers.length,
175
+ 0,
176
+ `expected zero callers for shadowed name without import, got: ${JSON.stringify(e.production_callers)}`,
177
+ );
178
+ });
179
+
180
+ test('case-19: same name imported from a different module is not credited', () => {
181
+ // Slice exports `handler` from src/handlers/cases.ts. main.ts imports
182
+ // `handler` from src/handlers/users.ts (a different module). Without
183
+ // path-provenance verification this would be a false PASS.
184
+ const result = check({
185
+ files: ['src/handlers/cases.ts'],
186
+ root: fx('case-19-same-name-different-module'),
187
+ });
188
+ const e = findExport(result, 'handler');
189
+ assert.equal(
190
+ e.production_callers.length,
191
+ 0,
192
+ `expected zero callers (cross-module same-name FP), got: ${JSON.stringify(e.production_callers)}`,
193
+ );
194
+ });
195
+
196
+ test('case-23: dynamic `import (...)` with whitespace before paren still excluded from skip-mode', () => {
197
+ // `import ('./x')` is valid JS and still dynamic. The static import below
198
+ // must continue to register the caller.
199
+ const result = check({ files: ['src/lib.ts'], root: fx('case-23-dynamic-import-with-space') });
200
+ const e = findExport(result, 'realCaller');
201
+ assert.ok(
202
+ e.production_callers.length >= 1,
203
+ `expected static import to be detected after whitespace-before-paren dynamic import, got: ${JSON.stringify(e.production_callers)}`,
204
+ );
205
+ });
206
+
207
+ test('case-22: dynamic `import()` does not suppress later static import detection', () => {
208
+ // Earlier versions skipped to EOF after a leading `import('...')` because the
209
+ // skip-mode never saw a `from` clause. Real caller `actualCaller(...)` must
210
+ // still be detected.
211
+ const result = check({ files: ['src/lib.ts'], root: fx('case-22-dynamic-import-then-caller') });
212
+ const e = findExport(result, 'actualCaller');
213
+ assert.ok(
214
+ e.production_callers.length >= 1,
215
+ `expected static import after dynamic import() to still register, got: ${JSON.stringify(e.production_callers)}`,
216
+ );
217
+ });
218
+
219
+ test('case-21: mixed default+named import (`import x, { foo } from m`) credits both', () => {
220
+ const result = check({ files: ['src/lib.ts'], root: fx('case-21-mixed-default-and-named') });
221
+ const meta = findExport(result, 'meta');
222
+ const def = findExport(result, 'makeThing'); // named default
223
+ assert.equal(def.kind, 'default');
224
+ assert.ok(meta.production_callers.length >= 1, 'expected named meta to have caller');
225
+ assert.ok(def.production_callers.length >= 1, 'expected default to have caller via mixed import');
226
+ });
227
+
228
+ test('case-20: aliased import `import { x as y }; y()` is credited as caller', () => {
229
+ // Production caller imports under a local alias and invokes via the alias.
230
+ // The check must scan for the local alias name, not the original.
231
+ const result = check({
232
+ files: ['src/handlers/cases.ts'],
233
+ root: fx('case-20-aliased-import-usage'),
234
+ });
235
+ const e = findExport(result, 'handler');
236
+ assert.ok(
237
+ e.production_callers.length >= 1,
238
+ `expected aliased call to be a production caller, got: ${JSON.stringify(e.production_callers)}`,
239
+ );
240
+ assert.ok(
241
+ e.production_callers.some((c) => c.pattern === 'invocation'),
242
+ 'expected invocation pattern',
243
+ );
244
+ });
@@ -0,0 +1,194 @@
1
+ ---
2
+ name: support-skill-validator
3
+ description: "Use when skills, rules, or commands have been edited to verify no contradictions, frontmatter conformance, or I/O gaps."
4
+ ---
5
+
6
+ # Support: Skill Validator
7
+
8
+ ## Overview
9
+
10
+ As the forge harness grows -- skills added, rules promoted from gotchas, commands modified -- contradictions and gaps can creep in. This skill performs systematic validation to catch inconsistencies before they cause confusion or incorrect behavior.
11
+
12
+ **Core Principle:** A system that contradicts itself is worse than no system at all. Every directive must be unambiguous, every I/O chain must be connected, and every responsibility must have exactly one owner.
13
+
14
+ ## When to Use
15
+
16
+ | Trigger | Validation Scope | Performance Target |
17
+ |---|---|---|
18
+ | `/validate` command | Full scan (all 5 checks) | Under 30 seconds |
19
+ | `/evolve` modifies a skill | Pre-apply check (modified skill vs all others) | Under 10 seconds |
20
+ | Session start | Lightweight I/O graph check only | Under 3 seconds |
21
+ | Manual request | Full scan or targeted check | Depends on scope |
22
+ | After gotcha promotion | Promoted rule vs existing rules and skills | Under 10 seconds |
23
+
24
+ ## When to load references
25
+
26
+ - **`references/validation-checks.md`** — full procedure for each of the 5 checks (directives, I/O graph, overlaps, gates, drift), with examples. Load this when actually running a scan.
27
+ - **`references/false-positives.md`** — known false-positive patterns and report-writing guidance. Load before writing a report so noise doesn't drown real findings.
28
+
29
+ ## I/O Contract
30
+
31
+ | Field | Value |
32
+ |---|---|
33
+ | **Requires** | All `SKILL.md` files + all `rules/*.md` files + all `commands/*.md` files |
34
+ | **Produces** | Validation report (structured, actionable) |
35
+ | **Feeds into** | `/evolve` command (fix contradictions before applying changes) |
36
+ | **Updates** | Nothing directly -- produces a report for human/AI action |
37
+
38
+ ### Input Files
39
+
40
+ ```
41
+ skills/
42
+ */SKILL.md -> All skill definitions
43
+ rules/
44
+ common/*.md -> Always-on rules
45
+ {language}/*.md -> Language-specific rules
46
+ references/
47
+ common/*.md -> On-demand references
48
+ commands/
49
+ *.md -> Command definitions
50
+ agents/
51
+ *.md -> Agent definitions
52
+ ```
53
+
54
+ ### Output Format
55
+
56
+ ```
57
+ VALIDATION REPORT
58
+ =================
59
+ Timestamp: {ISO 8601}
60
+ Scope: {full | targeted | lightweight}
61
+ Files Scanned: {count}
62
+
63
+ CONFLICTS ({count}):
64
+ {list of conflicts with file references and line context}
65
+
66
+ OVERLAPS ({count}):
67
+ {list of responsibility overlaps}
68
+
69
+ I/O GAPS ({count}):
70
+ {list of missing producers, orphaned outputs, circular deps}
71
+
72
+ GATE COVERAGE ({count of issues}):
73
+ {list of undocumented gate exemptions}
74
+
75
+ DRIFT ({count}):
76
+ {list of post-modification inconsistencies}
77
+
78
+ SUMMARY:
79
+ Errors: {count} (must fix before proceeding)
80
+ Warnings: {count} (should fix, may cause confusion)
81
+ Info: {count} (minor, for awareness)
82
+ ```
83
+
84
+ ## The Five Validation Checks
85
+
86
+ | # | Check | Catches | Severity range |
87
+ |---|---|---|---|
88
+ | 1 | **Directive Conflicts** | MUST vs NEVER contradictions across SKILL.md, rules, commands | ERROR – WARNING |
89
+ | 2 | **I/O Graph Integrity** | Missing producers, orphaned outputs, cycles in the produces→requires graph | INFO – ERROR |
90
+ | 3 | **Responsibility Overlaps** | Multiple skills/rules claiming the same responsibility | WARNING – INFO |
91
+ | 4 | **Gate Completeness** | Missing or vague quality gates on command transitions | ERROR – WARNING |
92
+ | 5 | **Post-Evolve Drift** | New inconsistencies introduced by a recent skill/rule modification | ERROR – INFO |
93
+
94
+ Full procedure, examples, and the I/O graph severity baseline for each check live in **`references/validation-checks.md`** — load that before running any check. Always pair with **`references/false-positives.md`** before writing the report.
95
+
96
+ ## Validation Severity Levels
97
+
98
+ | Level | Meaning | Action Required |
99
+ |---|---|---|
100
+ | **ERROR** | Definite contradiction, broken pipeline, or missing producer that demonstrably blocks a downstream consumer (named skill, named artifact, observed failure path) | Must fix before the system can be relied upon |
101
+ | **WARNING** | Potential issue, ambiguity, or undocumented exception that a reasonable reader would misinterpret | Should fix to prevent confusion |
102
+ | **INFO** | Asymmetric metadata, intentional omission, layered enforcement, or correct-but-could-be-clearer | Fix when convenient, or document as intentional |
103
+
104
+ **Key rule:** Optional-by-rule fields (per `references/common/skill-authoring.md` — `Requires`/`Produces`/`Feeds into` are "Usually" not "Always") cannot generate ERROR or WARNING by their absence alone. They generate ERROR/WARNING only when their absence breaks a *named* downstream consumer.
105
+
106
+ ## Integration Points
107
+
108
+ ### /validate Command (Full Scan)
109
+
110
+ Run all 5 checks. Output the full VALIDATION REPORT (above). Target: under 30 seconds.
111
+
112
+ ```
113
+ User: /validate
114
+ Running full validation...
115
+ Scanning: {N} skills, {N} common rules, {N} references, {N} language rule sets, {N} commands, {N} agents
116
+
117
+ VALIDATION REPORT
118
+ =================
119
+ [...]
120
+
121
+ SUMMARY:
122
+ Errors: 0
123
+ Warnings: 3
124
+ Info: 1
125
+
126
+ No blocking errors. System is consistent.
127
+ Warnings should be reviewed when convenient.
128
+ ```
129
+
130
+ ### /evolve Command (Pre-Apply Check)
131
+
132
+ Before `/evolve` applies any skill modification:
133
+
134
+ 1. Receive the proposed change
135
+ 2. Apply the change to a temporary copy
136
+ 3. Run targeted validation (Check 1 + Check 5)
137
+ 4. If errors found: BLOCK the change, present the conflicts
138
+ 5. If only warnings: WARN but allow (with user confirmation)
139
+ 6. If clean: ALLOW the change
140
+
141
+ ```
142
+ /evolve proposes modifying build-tdd...
143
+
144
+ Pre-apply validation:
145
+ [PASS] No directive conflicts
146
+ [PASS] I/O graph intact
147
+ [PASS] No new overlaps
148
+ [PASS] Gate coverage maintained
149
+ [PASS] No drift issues
150
+
151
+ Change is safe to apply.
152
+ ```
153
+
154
+ ### Session Start (Lightweight Check)
155
+
156
+ On session start, run only Check 2 (I/O Graph Integrity):
157
+
158
+ - Fast (reads I/O contracts only, not full file content)
159
+ - Catches broken connections from manual edits
160
+ - Reports only errors, suppresses warnings/info
161
+ - Target: under 3 seconds
162
+
163
+ ```
164
+ Session start I/O check: All clear ({N} skills, 0 gaps)
165
+ ```
166
+
167
+ ### After Gotcha Promotion
168
+
169
+ When `support-gotcha` promotes a gotcha to a rule:
170
+
171
+ 1. Receive the new rule text
172
+ 2. Run Check 1 (does new rule contradict existing rules?)
173
+ 3. Run Check 3 (does new rule overlap with existing skill responsibilities?)
174
+ 4. Report results before the promotion is finalized
175
+
176
+ ## Red Flags -- The Validator Itself Needs Validation
177
+
178
+ | Situation | Action |
179
+ |---|---|
180
+ | Validator reports 0 issues on a known-inconsistent system | Validator has a bug -- manual review needed |
181
+ | Validator reports 50+ errors | Likely false positives -- calibrate severity thresholds |
182
+ | Same finding appears in every report | Either a real systemic issue or a false positive pattern |
183
+ | Validator conflicts with itself | Meta-problem -- review validator logic |
184
+ | Report is longer than 100 lines | Too verbose -- summarize and link to details |
185
+
186
+ ## Quick Reference
187
+
188
+ | Check | What It Catches | Severity Range |
189
+ |---|---|---|
190
+ | **1. Directives** | MUST vs NEVER contradictions | ERROR - WARNING |
191
+ | **2. I/O Graph** | Missing producers, orphaned outputs, cycles | INFO - ERROR (see Severity Baseline in `references/validation-checks.md`) |
192
+ | **3. Overlaps** | Multiple owners for same responsibility | WARNING - INFO |
193
+ | **4. Gates** | Missing or vague transition gates | ERROR - WARNING |
194
+ | **5. Drift** | Post-modification inconsistencies | ERROR - INFO |
@@ -0,0 +1,59 @@
1
+ # False positives and effective reports
2
+
3
+ Two pieces of validator hygiene: knowing when a "finding" isn't a real defect, and writing reports that surface real issues without drowning them in noise.
4
+
5
+ ---
6
+
7
+ ## Handling False Positives
8
+
9
+ Not every flagged issue is a real problem. The validator should:
10
+
11
+ 1. **Check for documented exceptions**
12
+ - If a file contains an explicit "Exception:" or "Note:" about the flagged issue, downgrade to INFO
13
+ - Example: `/hotfix` documents that it skips brainstorming -- this is not a missing gate
14
+
15
+ 2. **Check for layered enforcement**
16
+ - Rules setting standards + skills enforcing them is correct design, not overlap
17
+ - Flag as INFO (acknowledged), not WARNING
18
+
19
+ 3. **Check for context-specific directives**
20
+ - "NEVER deploy without tests" (general) vs "Skip E2E tests for documentation-only PRs" (specific context)
21
+ - The specific context exception does not contradict the general rule
22
+ - Flag as INFO if contexts are clearly different
23
+
24
+ 4. **Maintain a known-exceptions file**
25
+ - `.claude/validation-exceptions.md` can list known acceptable findings
26
+ - Validator checks this file before reporting
27
+ - Each exception must explain WHY it is acceptable
28
+
29
+ 5. **Check for intentional methodology-only skills**
30
+ - Per `references/common/skill-authoring.md`, I/O Contract is OPTIONAL — pure methodology skills can omit it
31
+ - A skill (e.g., `build-tdd`) may have no `## I/O Contract` section by design when:
32
+ - The agent it dispatches is self-contained (carries its own protocol inline)
33
+ - The calling command (e.g., `/feature`) owns task scope and artifact paths
34
+ - Adding a contract would only duplicate orchestration that lives elsewhere
35
+ - Flag as **INFO** with the note "I/O Contract intentionally omitted — pure methodology skill"
36
+ - **Never report this as ERROR or WARNING.** A blocking severity here is a validator bug, not a skill defect.
37
+
38
+ 6. **Check for asymmetric `Feeds into` declarations**
39
+ - `Feeds into` is "Usually", not mandatory (per `skill-authoring.md`)
40
+ - If skill A says `Feeds into: B` but B doesn't list A's artifact in `Requires`, this is **INFO**, not WARNING
41
+ - Escalate only if you can name an actual workflow where B fails because the artifact is missing
42
+
43
+ ---
44
+
45
+ ## Writing Effective Validation Reports
46
+
47
+ ### Do
48
+ - Reference specific files and quote the conflicting text
49
+ - Suggest concrete resolutions
50
+ - Classify severity correctly (ERROR only for true contradictions)
51
+ - Group related findings together
52
+ - Include line context so the reader can find the issue
53
+
54
+ ### Do Not
55
+ - Flag every minor wording difference as a conflict
56
+ - Report style differences as errors (INFO at most)
57
+ - Flood the report with INFO items that obscure real issues
58
+ - Suggest resolutions that require rewriting entire skills
59
+ - Report the same issue multiple times from different angles