@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.
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/agents/architect.md +92 -0
- package/agents/builder.md +122 -0
- package/agents/code-reviewer.md +107 -0
- package/agents/concept-designer.md +207 -0
- package/agents/craft-reviewer.md +132 -0
- package/agents/critic.md +130 -0
- package/agents/doc-writer.md +85 -0
- package/agents/dreamer.md +129 -0
- package/agents/e2e-runner.md +89 -0
- package/agents/gotcha-hunter.md +127 -0
- package/agents/prototype-builder.md +193 -0
- package/agents/prototype-codifier.md +204 -0
- package/agents/prototype-reviewer.md +163 -0
- package/agents/security-reviewer.md +108 -0
- package/agents/spec-reviewer.md +94 -0
- package/agents/tracer.md +98 -0
- package/agents/wireframer.md +109 -0
- package/commands/abort.md +25 -0
- package/commands/bugfix.md +151 -0
- package/commands/evolve.md +118 -0
- package/commands/feature.md +236 -0
- package/commands/forge.md +100 -0
- package/commands/greenfield.md +185 -0
- package/commands/hotfix.md +98 -0
- package/commands/refactor.md +147 -0
- package/commands/resume.md +25 -0
- package/commands/setup.md +201 -0
- package/commands/status.md +27 -0
- package/commands/task-force.md +110 -0
- package/commands/validate.md +12 -0
- package/dist/__tests__/active-manifest.test.js +272 -0
- package/dist/__tests__/copy.test.js +96 -0
- package/dist/__tests__/gate-check.test.js +384 -0
- package/dist/__tests__/wiki.test.js +472 -0
- package/dist/__tests__/work-manifest.test.js +304 -0
- package/dist/active-manifest.js +229 -0
- package/dist/cli.js +158 -0
- package/dist/copy.js +124 -0
- package/dist/gate-check.js +326 -0
- package/dist/hooks.js +60 -0
- package/dist/init.js +140 -0
- package/dist/manifest.js +90 -0
- package/dist/merge.js +77 -0
- package/dist/paths.js +36 -0
- package/dist/uninstall.js +216 -0
- package/dist/update.js +158 -0
- package/dist/verify-manifest.js +65 -0
- package/dist/verify.js +98 -0
- package/dist/wiki-ui.js +310 -0
- package/dist/wiki.js +364 -0
- package/dist/work-manifest.js +798 -0
- package/hooks/config/gate-requirements.json +79 -0
- package/hooks/hooks.json +143 -0
- package/hooks/scripts/analyze-telemetry.sh +114 -0
- package/hooks/scripts/gate-enforcer.sh +164 -0
- package/hooks/scripts/pre-compact.sh +90 -0
- package/hooks/scripts/session-start.sh +81 -0
- package/hooks/scripts/telemetry.sh +41 -0
- package/hooks/scripts/wiki-lint.sh +87 -0
- package/hooks/templates/AGENTS.md.template +48 -0
- package/hooks/templates/CLAUDE.md.template +45 -0
- package/package.json +55 -0
- package/protocols/README.md +40 -0
- package/protocols/codex.md +151 -0
- package/protocols/graphify.md +156 -0
- package/references/common/agent-coordination.md +65 -0
- package/references/common/coding-standards.md +54 -0
- package/references/common/feature-tracking.md +21 -0
- package/references/common/io-protocol.md +36 -0
- package/references/common/phases.md +57 -0
- package/references/common/quality-gates.md +130 -0
- package/references/common/skill-authoring.md +154 -0
- package/references/common/skill-compliance.md +30 -0
- package/references/python/standards.md +44 -0
- package/references/react/standards.md +61 -0
- package/references/typescript/standards.md +42 -0
- package/rules/common/forge-system.md +59 -0
- package/rules/common/git-workflow.md +40 -0
- package/rules/common/guardrails.md +37 -0
- package/rules/common/quality-gates.md +18 -0
- package/rules/common/security.md +50 -0
- package/rules/common/skill-selection.md +78 -0
- package/rules/common/testing.md +58 -0
- package/rules/common/verification.md +39 -0
- package/skills/build-pr-workflow/SKILL.md +301 -0
- package/skills/build-pr-workflow/references/pr-template.md +62 -0
- package/skills/build-pr-workflow/references/subagent-merge.md +47 -0
- package/skills/build-pr-workflow/references/worktree-setup.md +125 -0
- package/skills/build-prototype/SKILL.md +264 -0
- package/skills/build-scaffold/SKILL.md +340 -0
- package/skills/build-tdd/SKILL.md +89 -0
- package/skills/build-wireframe/SKILL.md +110 -0
- package/skills/build-wireframe/assets/baseline-template.html +486 -0
- package/skills/build-wireframe/references/demo-walkthroughs.md +170 -0
- package/skills/build-wireframe/references/gotchas.md +188 -0
- package/skills/build-wireframe/references/legend-lines.md +141 -0
- package/skills/concept-slides/SKILL.md +192 -0
- package/skills/deliver-db-migration/SKILL.md +466 -0
- package/skills/deliver-deploy/SKILL.md +407 -0
- package/skills/deliver-onboarding/SKILL.md +198 -0
- package/skills/deliver-onboarding/references/document-templates.md +393 -0
- package/skills/deliver-onboarding/templates/getting-started.md +122 -0
- package/skills/discover-codebase-analysis/SKILL.md +448 -0
- package/skills/discover-requirements/SKILL.md +418 -0
- package/skills/discover-requirements/templates/prd.md +99 -0
- package/skills/discover-requirements/templates/technical-spec.md +123 -0
- package/skills/discover-requirements/templates/user-stories.md +76 -0
- package/skills/harden/SKILL.md +214 -0
- package/skills/iterate-prototype/SKILL.md +241 -0
- package/skills/plan-architecture/SKILL.md +457 -0
- package/skills/plan-architecture/templates/adr-template.md +52 -0
- package/skills/plan-architecture/templates/api-contract.md +99 -0
- package/skills/plan-architecture/templates/db-schema.md +81 -0
- package/skills/plan-architecture/templates/system-design.md +111 -0
- package/skills/plan-brainstorm/SKILL.md +433 -0
- package/skills/plan-design-system/SKILL.md +279 -0
- package/skills/plan-task-decompose/SKILL.md +454 -0
- package/skills/quality-code-review/SKILL.md +286 -0
- package/skills/quality-security-audit/SKILL.md +292 -0
- package/skills/quality-security-audit/references/audit-report-template.md +89 -0
- package/skills/quality-security-audit/references/owasp-checks.md +178 -0
- package/skills/quality-test-execution/SKILL.md +435 -0
- package/skills/quality-test-plan/SKILL.md +297 -0
- package/skills/quality-test-plan/references/test-type-guide.md +263 -0
- package/skills/quality-test-plan/templates/e2e-test-plan.md +72 -0
- package/skills/quality-test-plan/templates/integration-test-plan.md +74 -0
- package/skills/quality-test-plan/templates/load-test-plan.md +111 -0
- package/skills/quality-test-plan/templates/smoke-test-plan.md +68 -0
- package/skills/quality-test-plan/templates/unit-test-plan.md +56 -0
- package/skills/quality-uiux/SKILL.md +481 -0
- package/skills/support-debug/SKILL.md +464 -0
- package/skills/support-dream/SKILL.md +213 -0
- package/skills/support-gotcha/SKILL.md +249 -0
- package/skills/support-runtime-reachability/SKILL.md +190 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-01-passes-app-use/src/app.ts +7 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-01-passes-app-use/src/handlers/cases.ts +7 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-02-orphan-no-app-use/src/app.ts +8 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-02-orphan-no-app-use/src/handlers/cases.ts +7 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/App.tsx +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/components/RingingBanner.tsx +7 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-03-orphan-import-only/src/hooks/useTwilio.ts +6 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-04-jsx-component-rendered/src/App.tsx +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-04-jsx-component-rendered/src/components/MyComp.tsx +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-05-jsx-component-not-rendered/src/App.tsx +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-05-jsx-component-not-rendered/src/components/Orphan.tsx +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-06-class-instantiated/src/lib/Service.ts +6 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-06-class-instantiated/src/main.ts +4 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-07-class-not-instantiated/src/lib/Lonely.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-07-class-not-instantiated/src/main.ts +2 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-08-default-export-imported-and-called/src/handler.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-08-default-export-imported-and-called/src/main.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-09-default-export-orphan/src/handler.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-09-default-export-orphan/src/main.ts +2 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-10-aliased-named-export/src/lib.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-10-aliased-named-export/src/main.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/lib/index.ts +1 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/lib/internal.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-11-re-export-chain/src/main.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-12-test-only-caller/src/util.test.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-12-test-only-caller/src/util.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-13-gated-pending-annotation/src/future.ts +4 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-14-untraceable-annotation/src/decorated.ts +4 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-15-untraceable-empty/src/lazy.ts +4 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-16-python-module/src/lib.py +15 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-16-python-module/src/main.py +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-17-router-use/src/parent.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-17-router-use/src/routes/cases.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-18-shadowed-name-fp/src/lib/foo.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-18-shadowed-name-fp/src/other.ts +8 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/handlers/cases.ts +4 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/handlers/users.ts +4 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-19-same-name-different-module/src/main.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-20-aliased-import-usage/src/handlers/cases.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-20-aliased-import-usage/src/main.ts +4 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-21-mixed-default-and-named/src/lib.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-21-mixed-default-and-named/src/main.ts +5 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-22-dynamic-import-then-caller/src/lib.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-22-dynamic-import-then-caller/src/main.ts +8 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-23-dynamic-import-with-space/src/lib.ts +3 -0
- package/skills/support-runtime-reachability/scripts/__fixtures__/case-23-dynamic-import-with-space/src/main.ts +7 -0
- package/skills/support-runtime-reachability/scripts/check.mjs +638 -0
- package/skills/support-runtime-reachability/scripts/check.test.mjs +244 -0
- package/skills/support-skill-validator/SKILL.md +194 -0
- package/skills/support-skill-validator/references/false-positives.md +59 -0
- package/skills/support-skill-validator/references/validation-checks.md +280 -0
- package/skills/support-system-guide/SKILL.md +311 -0
- package/skills/support-task-force/SKILL.md +265 -0
- package/skills/support-task-force/references/dispatch-pattern.md +178 -0
- package/skills/support-task-force/references/synthesis-template.md +126 -0
- package/skills/support-wiki-bootstrap/SKILL.md +37 -0
- package/skills/support-wiki-lint/SKILL.md +196 -0
- package/skills/support-wiki-lint/scripts/lint.mjs +488 -0
- package/skills/support-wiki-lint/scripts/lint.test.mjs +196 -0
- package/templates/README.md +23 -0
- package/templates/aiwiki/CLAUDE.md.template +78 -0
- package/templates/aiwiki/schemas/architecture.md +118 -0
- package/templates/aiwiki/schemas/convention.md +112 -0
- package/templates/aiwiki/schemas/decision.md +144 -0
- package/templates/aiwiki/schemas/gotcha.md +118 -0
- package/templates/aiwiki/schemas/oracle.md +105 -0
- package/templates/aiwiki/schemas/session.md +125 -0
- package/templates/manifests/bugfix.yaml +41 -0
- package/templates/manifests/feature.yaml +69 -0
- package/templates/manifests/greenfield.yaml +61 -0
- package/templates/manifests/hotfix.yaml +45 -0
- package/templates/manifests/refactor.yaml +44 -0
- package/templates/manifests/v5/SCHEMA.md +327 -0
- package/templates/manifests/v5/feature.yaml +77 -0
- package/templates/manifests/v6/SCHEMA.md +199 -0
- package/templates/wiki-html/dream-detail.html +378 -0
- 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
|