@specship/specship-win32-arm64 0.24.0 → 0.28.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/bin/specship.cmd +31 -1
- package/lib/dist/activation/cheatsheet.d.ts +17 -1
- package/lib/dist/activation/cheatsheet.d.ts.map +1 -1
- package/lib/dist/activation/cheatsheet.js +47 -3
- package/lib/dist/activation/cheatsheet.js.map +1 -1
- package/lib/dist/activation/hook-output.d.ts +80 -0
- package/lib/dist/activation/hook-output.d.ts.map +1 -0
- package/lib/dist/activation/hook-output.js +103 -0
- package/lib/dist/activation/hook-output.js.map +1 -0
- package/lib/dist/activation/search-intercept.d.ts +60 -29
- package/lib/dist/activation/search-intercept.d.ts.map +1 -1
- package/lib/dist/activation/search-intercept.js +159 -55
- package/lib/dist/activation/search-intercept.js.map +1 -1
- package/lib/dist/activation/steering.d.ts +11 -13
- package/lib/dist/activation/steering.d.ts.map +1 -1
- package/lib/dist/activation/steering.js +25 -10
- package/lib/dist/activation/steering.js.map +1 -1
- package/lib/dist/behaviour/behaviour-surface.d.ts +47 -0
- package/lib/dist/behaviour/behaviour-surface.d.ts.map +1 -1
- package/lib/dist/behaviour/behaviour-surface.js +108 -2
- package/lib/dist/behaviour/behaviour-surface.js.map +1 -1
- package/lib/dist/bin/node-version-check.d.ts +22 -7
- package/lib/dist/bin/node-version-check.d.ts.map +1 -1
- package/lib/dist/bin/node-version-check.js +39 -9
- package/lib/dist/bin/node-version-check.js.map +1 -1
- package/lib/dist/bin/specship.js +989 -116
- package/lib/dist/bin/specship.js.map +1 -1
- package/lib/dist/commands/specship/learn.md +27 -3
- package/lib/dist/commands/specship/spec.md +85 -15
- package/lib/dist/db/spec-queries.d.ts +73 -3
- package/lib/dist/db/spec-queries.d.ts.map +1 -1
- package/lib/dist/db/spec-queries.js +139 -5
- package/lib/dist/db/spec-queries.js.map +1 -1
- package/lib/dist/db/sqlite-adapter.d.ts +7 -5
- package/lib/dist/db/sqlite-adapter.d.ts.map +1 -1
- package/lib/dist/db/sqlite-adapter.js +11 -9
- package/lib/dist/db/sqlite-adapter.js.map +1 -1
- package/lib/dist/enforce/changed-files.d.ts +39 -0
- package/lib/dist/enforce/changed-files.d.ts.map +1 -0
- package/lib/dist/enforce/changed-files.js +114 -0
- package/lib/dist/enforce/changed-files.js.map +1 -0
- package/lib/dist/enforce/enforce.d.ts +53 -2
- package/lib/dist/enforce/enforce.d.ts.map +1 -1
- package/lib/dist/enforce/enforce.js +95 -8
- package/lib/dist/enforce/enforce.js.map +1 -1
- package/lib/dist/enforce/mutation.d.ts +46 -0
- package/lib/dist/enforce/mutation.d.ts.map +1 -0
- package/lib/dist/enforce/mutation.js +104 -0
- package/lib/dist/enforce/mutation.js.map +1 -0
- package/lib/dist/enforce/suppressions.d.ts +33 -0
- package/lib/dist/enforce/suppressions.d.ts.map +1 -0
- package/lib/dist/enforce/suppressions.js +159 -0
- package/lib/dist/enforce/suppressions.js.map +1 -0
- package/lib/dist/extraction/grammars.d.ts +10 -0
- package/lib/dist/extraction/grammars.d.ts.map +1 -1
- package/lib/dist/extraction/grammars.js +32 -3
- package/lib/dist/extraction/grammars.js.map +1 -1
- package/lib/dist/extraction/index.d.ts +7 -0
- package/lib/dist/extraction/index.d.ts.map +1 -1
- package/lib/dist/extraction/index.js.map +1 -1
- package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts +7 -0
- package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/specs/markdown-spec-extractor.js +97 -10
- package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm.gz +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm.gz +0 -0
- package/lib/dist/graph/spec-coverage.d.ts +142 -0
- package/lib/dist/graph/spec-coverage.d.ts.map +1 -0
- package/lib/dist/graph/spec-coverage.js +276 -0
- package/lib/dist/graph/spec-coverage.js.map +1 -0
- package/lib/dist/health/smoke-check.js +2 -2
- package/lib/dist/health/smoke-check.js.map +1 -1
- package/lib/dist/index.d.ts +75 -7
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +191 -23
- package/lib/dist/index.js.map +1 -1
- package/lib/dist/installer/gemini-commands.d.ts +107 -0
- package/lib/dist/installer/gemini-commands.d.ts.map +1 -0
- package/lib/dist/installer/gemini-commands.js +264 -0
- package/lib/dist/installer/gemini-commands.js.map +1 -0
- package/lib/dist/installer/index.d.ts +55 -8
- package/lib/dist/installer/index.d.ts.map +1 -1
- package/lib/dist/installer/index.js +72 -53
- package/lib/dist/installer/index.js.map +1 -1
- package/lib/dist/installer/init-offer.d.ts +14 -9
- package/lib/dist/installer/init-offer.d.ts.map +1 -1
- package/lib/dist/installer/init-offer.js +13 -9
- package/lib/dist/installer/init-offer.js.map +1 -1
- package/lib/dist/installer/install-defaults.d.ts +81 -0
- package/lib/dist/installer/install-defaults.d.ts.map +1 -0
- package/lib/dist/installer/install-defaults.js +95 -0
- package/lib/dist/installer/install-defaults.js.map +1 -0
- package/lib/dist/installer/instructions-template.d.ts +22 -0
- package/lib/dist/installer/instructions-template.d.ts.map +1 -1
- package/lib/dist/installer/instructions-template.js +37 -1
- package/lib/dist/installer/instructions-template.js.map +1 -1
- package/lib/dist/installer/targets/claude.d.ts +18 -0
- package/lib/dist/installer/targets/claude.d.ts.map +1 -1
- package/lib/dist/installer/targets/claude.js +70 -23
- package/lib/dist/installer/targets/claude.js.map +1 -1
- package/lib/dist/installer/targets/gemini.d.ts +85 -12
- package/lib/dist/installer/targets/gemini.d.ts.map +1 -1
- package/lib/dist/installer/targets/gemini.js +527 -30
- package/lib/dist/installer/targets/gemini.js.map +1 -1
- package/lib/dist/installer/targets/shared.d.ts +13 -0
- package/lib/dist/installer/targets/shared.d.ts.map +1 -1
- package/lib/dist/installer/targets/shared.js +22 -4
- package/lib/dist/installer/targets/shared.js.map +1 -1
- package/lib/dist/installer/targets/types.d.ts +12 -0
- package/lib/dist/installer/targets/types.d.ts.map +1 -1
- package/lib/dist/jira/auto-publish.d.ts +71 -4
- package/lib/dist/jira/auto-publish.d.ts.map +1 -1
- package/lib/dist/jira/auto-publish.js +186 -17
- package/lib/dist/jira/auto-publish.js.map +1 -1
- package/lib/dist/jira/reconcile.d.ts +24 -1
- package/lib/dist/jira/reconcile.d.ts.map +1 -1
- package/lib/dist/jira/reconcile.js +46 -2
- package/lib/dist/jira/reconcile.js.map +1 -1
- package/lib/dist/jira/repo-config.d.ts +9 -0
- package/lib/dist/jira/repo-config.d.ts.map +1 -1
- package/lib/dist/jira/repo-config.js +11 -0
- package/lib/dist/jira/repo-config.js.map +1 -1
- package/lib/dist/jira/types.d.ts +7 -0
- package/lib/dist/jira/types.d.ts.map +1 -1
- package/lib/dist/jira/types.js.map +1 -1
- package/lib/dist/knowledge/enforce.d.ts +75 -0
- package/lib/dist/knowledge/enforce.d.ts.map +1 -0
- package/lib/dist/knowledge/enforce.js +250 -0
- package/lib/dist/knowledge/enforce.js.map +1 -0
- package/lib/dist/knowledge/facts.d.ts +33 -0
- package/lib/dist/knowledge/facts.d.ts.map +1 -0
- package/lib/dist/knowledge/facts.js +117 -0
- package/lib/dist/knowledge/facts.js.map +1 -0
- package/lib/dist/knowledge/proposals.d.ts +88 -0
- package/lib/dist/knowledge/proposals.d.ts.map +1 -0
- package/lib/dist/knowledge/proposals.js +360 -0
- package/lib/dist/knowledge/proposals.js.map +1 -0
- package/lib/dist/knowledge/stale.d.ts +64 -0
- package/lib/dist/knowledge/stale.d.ts.map +1 -0
- package/lib/dist/knowledge/stale.js +174 -0
- package/lib/dist/knowledge/stale.js.map +1 -0
- package/lib/dist/mcp/fitness-tool.d.ts +5 -6
- package/lib/dist/mcp/fitness-tool.d.ts.map +1 -1
- package/lib/dist/mcp/fitness-tool.js +0 -16
- package/lib/dist/mcp/fitness-tool.js.map +1 -1
- package/lib/dist/mcp/gemini-session.d.ts +50 -0
- package/lib/dist/mcp/gemini-session.d.ts.map +1 -0
- package/lib/dist/mcp/gemini-session.js +159 -0
- package/lib/dist/mcp/gemini-session.js.map +1 -0
- package/lib/dist/mcp/health-tool.d.ts +26 -0
- package/lib/dist/mcp/health-tool.d.ts.map +1 -0
- package/lib/dist/mcp/health-tool.js +50 -0
- package/lib/dist/mcp/health-tool.js.map +1 -0
- package/lib/dist/mcp/jira-tools.d.ts.map +1 -1
- package/lib/dist/mcp/jira-tools.js +25 -0
- package/lib/dist/mcp/jira-tools.js.map +1 -1
- package/lib/dist/mcp/maintainability-tool.d.ts +5 -7
- package/lib/dist/mcp/maintainability-tool.d.ts.map +1 -1
- package/lib/dist/mcp/maintainability-tool.js +0 -16
- package/lib/dist/mcp/maintainability-tool.js.map +1 -1
- package/lib/dist/mcp/model-context.d.ts +31 -7
- package/lib/dist/mcp/model-context.d.ts.map +1 -1
- package/lib/dist/mcp/model-context.js +70 -21
- package/lib/dist/mcp/model-context.js.map +1 -1
- package/lib/dist/mcp/proxy.d.ts.map +1 -1
- package/lib/dist/mcp/proxy.js +5 -2
- package/lib/dist/mcp/proxy.js.map +1 -1
- package/lib/dist/mcp/server-instructions.d.ts +16 -15
- package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
- package/lib/dist/mcp/server-instructions.js +72 -5
- package/lib/dist/mcp/server-instructions.js.map +1 -1
- package/lib/dist/mcp/session.js +1 -1
- package/lib/dist/mcp/session.js.map +1 -1
- package/lib/dist/mcp/spec-tools.d.ts +9 -1
- package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
- package/lib/dist/mcp/spec-tools.js +282 -49
- package/lib/dist/mcp/spec-tools.js.map +1 -1
- package/lib/dist/mcp/tools.d.ts +49 -9
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +210 -58
- package/lib/dist/mcp/tools.js.map +1 -1
- package/lib/dist/pulse/pulse.d.ts +93 -0
- package/lib/dist/pulse/pulse.d.ts.map +1 -0
- package/lib/dist/pulse/pulse.js +401 -0
- package/lib/dist/pulse/pulse.js.map +1 -0
- package/lib/dist/reflect/apply.d.ts.map +1 -1
- package/lib/dist/reflect/apply.js +38 -3
- package/lib/dist/reflect/apply.js.map +1 -1
- package/lib/dist/reflect/auto-sweep.d.ts +26 -0
- package/lib/dist/reflect/auto-sweep.d.ts.map +1 -0
- package/lib/dist/reflect/auto-sweep.js +90 -0
- package/lib/dist/reflect/auto-sweep.js.map +1 -0
- package/lib/dist/reflect/cli.d.ts +62 -0
- package/lib/dist/reflect/cli.d.ts.map +1 -0
- package/lib/dist/reflect/cli.js +163 -0
- package/lib/dist/reflect/cli.js.map +1 -0
- package/lib/dist/reflect/miner.d.ts.map +1 -1
- package/lib/dist/reflect/miner.js +119 -0
- package/lib/dist/reflect/miner.js.map +1 -1
- package/lib/dist/reflect/sweep.d.ts +1 -0
- package/lib/dist/reflect/sweep.d.ts.map +1 -1
- package/lib/dist/reflect/sweep.js +62 -13
- package/lib/dist/reflect/sweep.js.map +1 -1
- package/lib/dist/reflect/targets.d.ts +2 -0
- package/lib/dist/reflect/targets.d.ts.map +1 -1
- package/lib/dist/reflect/targets.js +33 -0
- package/lib/dist/reflect/targets.js.map +1 -1
- package/lib/dist/reflect/types.d.ts +19 -2
- package/lib/dist/reflect/types.d.ts.map +1 -1
- package/lib/dist/resolution/spec-link-resolver.d.ts +44 -2
- package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
- package/lib/dist/resolution/spec-link-resolver.js +132 -0
- package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
- package/lib/dist/resolution/test-title-links.d.ts +62 -0
- package/lib/dist/resolution/test-title-links.d.ts.map +1 -0
- package/lib/dist/resolution/test-title-links.js +97 -0
- package/lib/dist/resolution/test-title-links.js.map +1 -0
- package/lib/dist/search/query-parser.d.ts.map +1 -1
- package/lib/dist/search/query-parser.js +6 -1
- package/lib/dist/search/query-parser.js.map +1 -1
- package/lib/dist/server/project-registry.js +1 -1
- package/lib/dist/server/routes/claude.js +24 -43
- package/lib/dist/server/routes/graph.js +53 -7
- package/lib/dist/server/routes/knowledge.js +118 -0
- package/lib/dist/server/routes/mcp.js +4 -3
- package/lib/dist/server/routes/projects.js +4 -4
- package/lib/dist/server/routes/spec.js +49 -7
- package/lib/dist/server/server.js +31 -17
- package/lib/dist/skills/spec-author/SKILL.md +164 -0
- package/lib/dist/skills/spec-author/references/format.md +210 -0
- package/lib/dist/skills/spec-author/references/gap-questions.md +152 -0
- package/lib/dist/skills/spec-author/references/quality-rubric.md +125 -0
- package/lib/dist/skills/spec-author/references/review-checklist.md +175 -0
- package/lib/dist/spec-lint/lint.d.ts +82 -0
- package/lib/dist/spec-lint/lint.d.ts.map +1 -0
- package/lib/dist/spec-lint/lint.js +491 -0
- package/lib/dist/spec-lint/lint.js.map +1 -0
- package/lib/dist/spec-quality/criterion-ids.d.ts +13 -0
- package/lib/dist/spec-quality/criterion-ids.d.ts.map +1 -0
- package/lib/dist/spec-quality/criterion-ids.js +38 -0
- package/lib/dist/spec-quality/criterion-ids.js.map +1 -0
- package/lib/dist/spec-quality/evidence-kind.d.ts +54 -0
- package/lib/dist/spec-quality/evidence-kind.d.ts.map +1 -0
- package/lib/dist/spec-quality/evidence-kind.js +183 -0
- package/lib/dist/spec-quality/evidence-kind.js.map +1 -0
- package/lib/dist/spec-quality/link-persist.d.ts +65 -0
- package/lib/dist/spec-quality/link-persist.d.ts.map +1 -0
- package/lib/dist/spec-quality/link-persist.js +249 -0
- package/lib/dist/spec-quality/link-persist.js.map +1 -0
- package/lib/dist/spec-quality/plan-scope.d.ts +38 -0
- package/lib/dist/spec-quality/plan-scope.d.ts.map +1 -0
- package/lib/dist/spec-quality/plan-scope.js +129 -0
- package/lib/dist/spec-quality/plan-scope.js.map +1 -0
- package/lib/dist/spec-quality/postcheck.d.ts +85 -0
- package/lib/dist/spec-quality/postcheck.d.ts.map +1 -0
- package/lib/dist/spec-quality/postcheck.js +390 -0
- package/lib/dist/spec-quality/postcheck.js.map +1 -0
- package/lib/dist/spec-quality/readiness.d.ts +72 -0
- package/lib/dist/spec-quality/readiness.d.ts.map +1 -0
- package/lib/dist/spec-quality/readiness.js +226 -0
- package/lib/dist/spec-quality/readiness.js.map +1 -0
- package/lib/dist/statusline/index.js.map +1 -1
- package/lib/dist/statusline/render.d.ts +1 -1
- package/lib/dist/statusline/render.d.ts.map +1 -1
- package/lib/dist/statusline/render.js +3 -1
- package/lib/dist/statusline/render.js.map +1 -1
- package/lib/dist/ui/assets/index-BzuUWUAE.js +50 -0
- package/lib/dist/ui/index.html +1 -1
- package/lib/dist/verify/report-ingest.d.ts +88 -0
- package/lib/dist/verify/report-ingest.d.ts.map +1 -0
- package/lib/dist/verify/report-ingest.js +269 -0
- package/lib/dist/verify/report-ingest.js.map +1 -0
- package/lib/dist/workflows/agents/claude.d.ts +16 -0
- package/lib/dist/workflows/agents/claude.d.ts.map +1 -0
- package/lib/dist/workflows/agents/claude.js +82 -0
- package/lib/dist/workflows/agents/claude.js.map +1 -0
- package/lib/dist/workflows/agents/gemini.d.ts +21 -0
- package/lib/dist/workflows/agents/gemini.d.ts.map +1 -0
- package/lib/dist/workflows/agents/gemini.js +124 -0
- package/lib/dist/workflows/agents/gemini.js.map +1 -0
- package/lib/dist/workflows/agents/index.d.ts +28 -0
- package/lib/dist/workflows/agents/index.d.ts.map +1 -0
- package/lib/dist/workflows/agents/index.js +104 -0
- package/lib/dist/workflows/agents/index.js.map +1 -0
- package/lib/dist/workflows/agents/types.d.ts +55 -0
- package/lib/dist/workflows/agents/types.d.ts.map +1 -0
- package/lib/dist/workflows/agents/types.js +46 -0
- package/lib/dist/workflows/agents/types.js.map +1 -0
- package/lib/dist/workflows/batch.d.ts +63 -0
- package/lib/dist/workflows/batch.d.ts.map +1 -0
- package/lib/dist/workflows/batch.js +151 -0
- package/lib/dist/workflows/batch.js.map +1 -0
- package/lib/dist/workflows/defaults/claude-design-implement.yaml +3 -4
- package/lib/dist/workflows/defaults/spec-author.yaml +11 -9
- package/lib/dist/workflows/defaults/spec-fix.yaml +103 -12
- package/lib/dist/workflows/defaults/spec-implement-mixed.yaml +21 -193
- package/lib/dist/workflows/defaults/spec-implement.yaml +701 -41
- package/lib/dist/workflows/defaults/spec-verify.yaml +60 -9
- package/lib/dist/workflows/discovery.d.ts +13 -0
- package/lib/dist/workflows/discovery.d.ts.map +1 -1
- package/lib/dist/workflows/discovery.js +79 -17
- package/lib/dist/workflows/discovery.js.map +1 -1
- package/lib/dist/workflows/executor.d.ts +18 -0
- package/lib/dist/workflows/executor.d.ts.map +1 -1
- package/lib/dist/workflows/executor.js +130 -33
- package/lib/dist/workflows/executor.js.map +1 -1
- package/lib/dist/workflows/gc.d.ts +50 -0
- package/lib/dist/workflows/gc.d.ts.map +1 -0
- package/lib/dist/workflows/gc.js +120 -0
- package/lib/dist/workflows/gc.js.map +1 -0
- package/lib/dist/workflows/merge-queue.d.ts +64 -0
- package/lib/dist/workflows/merge-queue.d.ts.map +1 -0
- package/lib/dist/workflows/merge-queue.js +162 -0
- package/lib/dist/workflows/merge-queue.js.map +1 -0
- package/lib/dist/workflows/runners/approval.d.ts +1 -1
- package/lib/dist/workflows/runners/approval.d.ts.map +1 -1
- package/lib/dist/workflows/runners/approval.js +11 -2
- package/lib/dist/workflows/runners/approval.js.map +1 -1
- package/lib/dist/workflows/runners/bash.d.ts +15 -0
- package/lib/dist/workflows/runners/bash.d.ts.map +1 -1
- package/lib/dist/workflows/runners/bash.js +50 -1
- package/lib/dist/workflows/runners/bash.js.map +1 -1
- package/lib/dist/workflows/runners/prompt.d.ts +16 -41
- package/lib/dist/workflows/runners/prompt.d.ts.map +1 -1
- package/lib/dist/workflows/runners/prompt.js +55 -120
- package/lib/dist/workflows/runners/prompt.js.map +1 -1
- package/lib/dist/workflows/runners/types.d.ts +12 -0
- package/lib/dist/workflows/runners/types.d.ts.map +1 -1
- package/lib/dist/workflows/schemas/workflow.d.ts +17 -0
- package/lib/dist/workflows/schemas/workflow.d.ts.map +1 -1
- package/lib/dist/workflows/schemas/workflow.js +27 -0
- package/lib/dist/workflows/schemas/workflow.js.map +1 -1
- package/lib/node_modules/.package-lock.json +1 -1
- package/lib/package.json +8 -4
- package/package.json +5 -3
- package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
- package/lib/dist/server/ingest/impact-query.js +0 -343
- package/lib/dist/server/routes/events.js +0 -152
- package/lib/dist/ui/assets/index-CjjaftDw.js +0 -50
- package/node.exe +0 -4
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence kinds (PIPETRUST-DOC, REQ-PIPETRUST-001 / REQ-PIPETRUST-002).
|
|
3
|
+
*
|
|
4
|
+
* Coverage used to count "a test" without asking what kind. Every acceptance
|
|
5
|
+
* criterion now resolves to exactly one REQUIRED kind of evidence, and every
|
|
6
|
+
* test file classifies to the kind it actually PROVIDES — both
|
|
7
|
+
* deterministically, so coverage can compare the two.
|
|
8
|
+
*
|
|
9
|
+
* - required: an inline `(evidence: <kind>)` tag wins; otherwise derived from
|
|
10
|
+
* the criterion's shape (invariant phrasing → property) and the
|
|
11
|
+
* requirement's direct links (UI → e2e, route → contract), else unit.
|
|
12
|
+
* - present: read from the test file alone — library imports and path
|
|
13
|
+
* segments. Never throws; anything unreadable is `unit`.
|
|
14
|
+
*/
|
|
15
|
+
export type EvidenceKind = 'unit' | 'property' | 'e2e' | 'contract';
|
|
16
|
+
/** The four kinds, in the order reports list them. */
|
|
17
|
+
export declare const EVIDENCE_KINDS: readonly EvidenceKind[];
|
|
18
|
+
/**
|
|
19
|
+
* Read an inline `(evidence: <kind>)` tag from an acceptance bullet
|
|
20
|
+
* (REQ-PIPETRUST-001.A1). No tag → `{}`; a known kind → `{ kind }`; an
|
|
21
|
+
* unknown kind → `{ invalid: <what was written> }` (a lint error, A3).
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseDeclaredEvidenceKind(text: string): {
|
|
24
|
+
kind?: EvidenceKind;
|
|
25
|
+
invalid?: string;
|
|
26
|
+
};
|
|
27
|
+
export interface EvidenceKindContext {
|
|
28
|
+
/** The requirement is directly linked (implements) to UI code. */
|
|
29
|
+
linksUi?: boolean;
|
|
30
|
+
/** The requirement is directly linked (implements) to a `route` node. */
|
|
31
|
+
linksRoute?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The single evidence kind a criterion requires (REQ-PIPETRUST-001.A1/.A2).
|
|
35
|
+
* Precedence: declared tag → invariant phrasing (property) → UI link (e2e) →
|
|
36
|
+
* route link (contract) → unit. An invalid declared kind falls through to the
|
|
37
|
+
* derived kind — the lint reports it; resolution never fails.
|
|
38
|
+
*/
|
|
39
|
+
export declare function requiredEvidenceKind(criterionText: string, ctx?: EvidenceKindContext): EvidenceKind;
|
|
40
|
+
/**
|
|
41
|
+
* Remove block comments, `//` line comments and `#` line comments (Python /
|
|
42
|
+
* shell test files). Deliberately simple: a `//` inside a string literal also
|
|
43
|
+
* ends the line's code, which can only make classification more conservative.
|
|
44
|
+
*/
|
|
45
|
+
export declare function stripComments(source: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Classify a test file by the kind of evidence it provides
|
|
48
|
+
* (REQ-PIPETRUST-002.A1). Source signals are checked before path signals, and
|
|
49
|
+
* property before e2e before contract. `source` is read from `filePath` when
|
|
50
|
+
* omitted; a missing or unreadable file classifies as `unit` (A2) — this
|
|
51
|
+
* function never throws.
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyTestEvidence(filePath: string, source?: string): EvidenceKind;
|
|
54
|
+
//# sourceMappingURL=evidence-kind.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence-kind.d.ts","sourceRoot":"","sources":["../../src/spec-quality/evidence-kind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,KAAK,GAAG,UAAU,CAAC;AAEpE,sDAAsD;AACtD,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA4C,CAAC;AAkB/F;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,YAAY,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAMjG;AAED,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,GAAE,mBAAwB,GAAG,YAAY,CAOvG;AA2BD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMpD;AASD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAyBpF"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Evidence kinds (PIPETRUST-DOC, REQ-PIPETRUST-001 / REQ-PIPETRUST-002).
|
|
4
|
+
*
|
|
5
|
+
* Coverage used to count "a test" without asking what kind. Every acceptance
|
|
6
|
+
* criterion now resolves to exactly one REQUIRED kind of evidence, and every
|
|
7
|
+
* test file classifies to the kind it actually PROVIDES — both
|
|
8
|
+
* deterministically, so coverage can compare the two.
|
|
9
|
+
*
|
|
10
|
+
* - required: an inline `(evidence: <kind>)` tag wins; otherwise derived from
|
|
11
|
+
* the criterion's shape (invariant phrasing → property) and the
|
|
12
|
+
* requirement's direct links (UI → e2e, route → contract), else unit.
|
|
13
|
+
* - present: read from the test file alone — library imports and path
|
|
14
|
+
* segments. Never throws; anything unreadable is `unit`.
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.EVIDENCE_KINDS = void 0;
|
|
51
|
+
exports.parseDeclaredEvidenceKind = parseDeclaredEvidenceKind;
|
|
52
|
+
exports.requiredEvidenceKind = requiredEvidenceKind;
|
|
53
|
+
exports.stripComments = stripComments;
|
|
54
|
+
exports.classifyTestEvidence = classifyTestEvidence;
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const behaviour_surface_1 = require("../behaviour/behaviour-surface");
|
|
57
|
+
/** The four kinds, in the order reports list them. */
|
|
58
|
+
exports.EVIDENCE_KINDS = ['unit', 'property', 'e2e', 'contract'];
|
|
59
|
+
/** `(evidence: <kind>)` — case-insensitive; the kind is any non-`)` run. */
|
|
60
|
+
const EVIDENCE_TAG = /\(\s*evidence\s*:\s*([^)]*?)\s*\)/i;
|
|
61
|
+
/**
|
|
62
|
+
* Drop inline code spans and fenced blocks first: a spec that DOCUMENTS the
|
|
63
|
+
* tag syntax writes `` `(evidence: <kind>)` `` in backticks and is not itself
|
|
64
|
+
* declaring a kind (same convention as the lint's `[needs review]` scan).
|
|
65
|
+
*/
|
|
66
|
+
function stripCode(text) {
|
|
67
|
+
return text.replace(/```[\s\S]*?```/g, ' ').replace(/`[^`]*`/g, ' ');
|
|
68
|
+
}
|
|
69
|
+
function isEvidenceKind(s) {
|
|
70
|
+
return exports.EVIDENCE_KINDS.includes(s);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read an inline `(evidence: <kind>)` tag from an acceptance bullet
|
|
74
|
+
* (REQ-PIPETRUST-001.A1). No tag → `{}`; a known kind → `{ kind }`; an
|
|
75
|
+
* unknown kind → `{ invalid: <what was written> }` (a lint error, A3).
|
|
76
|
+
*/
|
|
77
|
+
function parseDeclaredEvidenceKind(text) {
|
|
78
|
+
const m = stripCode(text ?? '').match(EVIDENCE_TAG);
|
|
79
|
+
if (!m)
|
|
80
|
+
return {};
|
|
81
|
+
const raw = (m[1] ?? '').trim();
|
|
82
|
+
const norm = raw.toLowerCase();
|
|
83
|
+
return isEvidenceKind(norm) ? { kind: norm } : { invalid: raw };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The single evidence kind a criterion requires (REQ-PIPETRUST-001.A1/.A2).
|
|
87
|
+
* Precedence: declared tag → invariant phrasing (property) → UI link (e2e) →
|
|
88
|
+
* route link (contract) → unit. An invalid declared kind falls through to the
|
|
89
|
+
* derived kind — the lint reports it; resolution never fails.
|
|
90
|
+
*/
|
|
91
|
+
function requiredEvidenceKind(criterionText, ctx = {}) {
|
|
92
|
+
const declared = parseDeclaredEvidenceKind(criterionText).kind;
|
|
93
|
+
if (declared)
|
|
94
|
+
return declared;
|
|
95
|
+
if ((0, behaviour_surface_1.classifyCriterionShape)(criterionText ?? '') === 'invariant')
|
|
96
|
+
return 'property';
|
|
97
|
+
if (ctx.linksUi)
|
|
98
|
+
return 'e2e';
|
|
99
|
+
if (ctx.linksRoute)
|
|
100
|
+
return 'contract';
|
|
101
|
+
return 'unit';
|
|
102
|
+
}
|
|
103
|
+
/** Property-testing library use: fast-check, Hypothesis, jqwik, proptest, QuickCheck. */
|
|
104
|
+
const PROPERTY_SOURCE = [
|
|
105
|
+
/['"]fast-check['"]/,
|
|
106
|
+
/\bfc\.(assert|property|asyncProperty)\s*\(/,
|
|
107
|
+
/@given\s*\(/,
|
|
108
|
+
/\bfrom\s+hypothesis\b|\bimport\s+hypothesis\b/,
|
|
109
|
+
/\bnet\.jqwik\b|@Property\b/,
|
|
110
|
+
/\bproptest!?\b/,
|
|
111
|
+
/\bquickcheck\b/i,
|
|
112
|
+
];
|
|
113
|
+
/** Browser e2e frameworks. */
|
|
114
|
+
const E2E_SOURCE = [/['"]@playwright\/test['"]/, /['"]cypress['"]/, /\bcy\.(visit|get|request)\s*\(/];
|
|
115
|
+
/** Contract / API-schema testing tools. */
|
|
116
|
+
const CONTRACT_SOURCE = [
|
|
117
|
+
// Case-sensitive: the package and CLI are lowercase in real use, and an
|
|
118
|
+
// upper-case identifier (a constant named SCHEMATHESIS) is not a use.
|
|
119
|
+
/\bschemathesis\b/,
|
|
120
|
+
/@pact-foundation\//,
|
|
121
|
+
/\b(from|import)\s+pact\b/,
|
|
122
|
+
/\bnew\s+Pact(V\d)?\s*\(/,
|
|
123
|
+
/\bau\.com\.dius\.pact\b/,
|
|
124
|
+
];
|
|
125
|
+
/**
|
|
126
|
+
* Remove block comments, `//` line comments and `#` line comments (Python /
|
|
127
|
+
* shell test files). Deliberately simple: a `//` inside a string literal also
|
|
128
|
+
* ends the line's code, which can only make classification more conservative.
|
|
129
|
+
*/
|
|
130
|
+
function stripComments(source) {
|
|
131
|
+
return source
|
|
132
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
133
|
+
.split('\n')
|
|
134
|
+
.map((line) => line.replace(/(^|\s)\/\/.*$/, '$1').replace(/^\s*#(?!!).*$/, ''))
|
|
135
|
+
.join('\n');
|
|
136
|
+
}
|
|
137
|
+
function pathSegments(filePath) {
|
|
138
|
+
return filePath
|
|
139
|
+
.replace(/\\/g, '/')
|
|
140
|
+
.split('/')
|
|
141
|
+
.map((s) => s.toLowerCase());
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Classify a test file by the kind of evidence it provides
|
|
145
|
+
* (REQ-PIPETRUST-002.A1). Source signals are checked before path signals, and
|
|
146
|
+
* property before e2e before contract. `source` is read from `filePath` when
|
|
147
|
+
* omitted; a missing or unreadable file classifies as `unit` (A2) — this
|
|
148
|
+
* function never throws.
|
|
149
|
+
*/
|
|
150
|
+
function classifyTestEvidence(filePath, source) {
|
|
151
|
+
let text = source;
|
|
152
|
+
if (text === undefined) {
|
|
153
|
+
try {
|
|
154
|
+
text = fs.readFileSync(filePath, 'utf-8');
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
text = '';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// A tool named in prose (a doc comment explaining the classifier, say) is
|
|
161
|
+
// not a use of it — match against code with comments removed.
|
|
162
|
+
text = stripComments(text);
|
|
163
|
+
try {
|
|
164
|
+
if (PROPERTY_SOURCE.some((re) => re.test(text)))
|
|
165
|
+
return 'property';
|
|
166
|
+
if (E2E_SOURCE.some((re) => re.test(text)))
|
|
167
|
+
return 'e2e';
|
|
168
|
+
if (CONTRACT_SOURCE.some((re) => re.test(text)))
|
|
169
|
+
return 'contract';
|
|
170
|
+
const segs = pathSegments(filePath ?? '');
|
|
171
|
+
const dirs = segs.slice(0, -1);
|
|
172
|
+
const base = segs[segs.length - 1] ?? '';
|
|
173
|
+
if (dirs.includes('e2e') || /(^|[.\-_])e2e([.\-_]|$)/.test(base))
|
|
174
|
+
return 'e2e';
|
|
175
|
+
if (dirs.includes('contract') || dirs.includes('contracts'))
|
|
176
|
+
return 'contract';
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// fall through — classification is advisory, never fatal
|
|
180
|
+
}
|
|
181
|
+
return 'unit';
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=evidence-kind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence-kind.js","sourceRoot":"","sources":["../../src/spec-quality/evidence-kind.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable link assertions (THROUGHPUT-DOC, REQ-THRU-005).
|
|
3
|
+
*
|
|
4
|
+
* A link asserted through the link-assert tool lands as a DB row, and a DB row
|
|
5
|
+
* alone vanishes on a full reindex. This module writes the same link into the
|
|
6
|
+
* requirement's `implementations:` (kind `implements`) or `verifies:` (kind
|
|
7
|
+
* `tests`) block in its spec file, so the parser re-asserts it on every index.
|
|
8
|
+
*
|
|
9
|
+
* Rules:
|
|
10
|
+
* - an acceptance-criterion id (`REQ-X.A1`) persists into its parent
|
|
11
|
+
* requirement's block — link blocks live under the requirement, never in
|
|
12
|
+
* the Acceptance section;
|
|
13
|
+
* - one bullet per target; a missing block is created right before the
|
|
14
|
+
* requirement's `## Acceptance` heading (or at the end of its prose);
|
|
15
|
+
* - idempotent — re-persisting a listed target leaves the file
|
|
16
|
+
* byte-identical;
|
|
17
|
+
* - a target with no symbol, or a symbol not defined in its file, is refused
|
|
18
|
+
* and the spec file is never touched.
|
|
19
|
+
*/
|
|
20
|
+
export type PersistLinkKind = 'implements' | 'tests';
|
|
21
|
+
export interface PersistLinkOptions {
|
|
22
|
+
projectRoot: string;
|
|
23
|
+
/** Requirement id (`REQ-X`) or acceptance-criterion id (`REQ-X.A1`). */
|
|
24
|
+
specId: string;
|
|
25
|
+
kind: PersistLinkKind;
|
|
26
|
+
/** Project-relative path of the code/test file. */
|
|
27
|
+
filePath: string;
|
|
28
|
+
/** Symbol (qualified name or named test function) defined in `filePath`. */
|
|
29
|
+
symbol: string;
|
|
30
|
+
/**
|
|
31
|
+
* Spec file to edit (project-relative or absolute). Default: the file under
|
|
32
|
+
* `specs/` declaring the requirement's id marker.
|
|
33
|
+
*/
|
|
34
|
+
specSourcePath?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The caller already resolved `filePath:symbol` against the code graph —
|
|
37
|
+
* skip the textual definition check (it is a fallback for callers without
|
|
38
|
+
* a graph, and can miss syntax the extractor understands).
|
|
39
|
+
*/
|
|
40
|
+
symbolResolved?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface PersistLinkResult {
|
|
43
|
+
status: 'persisted' | 'unchanged' | 'refused';
|
|
44
|
+
/** Project-relative spec file path, when one was located. */
|
|
45
|
+
specFile?: string;
|
|
46
|
+
/** The requirement whose block was (or would have been) edited. */
|
|
47
|
+
requirementId: string;
|
|
48
|
+
/** Human-readable outcome / refusal reason. */
|
|
49
|
+
detail: string;
|
|
50
|
+
}
|
|
51
|
+
/** `REQ-X.A1` → `REQ-X`; a requirement id is returned unchanged. */
|
|
52
|
+
export declare function parentRequirementId(specId: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Textual "is `symbol` defined in this source" check, in the style of the
|
|
55
|
+
* postcheck verifies-coverage check, widened to qualified names
|
|
56
|
+
* (`Class.method` → both the container and the member must be declared) and
|
|
57
|
+
* class members / type declarations.
|
|
58
|
+
*/
|
|
59
|
+
export declare function symbolDefinedInSource(source: string, symbol: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Persist an asserted link into the spec file. Never throws — failures and
|
|
62
|
+
* refusals come back as `status: 'refused'` with the reason.
|
|
63
|
+
*/
|
|
64
|
+
export declare function persistLinkToSpecFile(opts: PersistLinkOptions): PersistLinkResult;
|
|
65
|
+
//# sourceMappingURL=link-persist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-persist.d.ts","sourceRoot":"","sources":["../../src/spec-quality/link-persist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,CAAC;IACtB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;IAC9C,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAMD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAkB7E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CA+IjF"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Durable link assertions (THROUGHPUT-DOC, REQ-THRU-005).
|
|
4
|
+
*
|
|
5
|
+
* A link asserted through the link-assert tool lands as a DB row, and a DB row
|
|
6
|
+
* alone vanishes on a full reindex. This module writes the same link into the
|
|
7
|
+
* requirement's `implementations:` (kind `implements`) or `verifies:` (kind
|
|
8
|
+
* `tests`) block in its spec file, so the parser re-asserts it on every index.
|
|
9
|
+
*
|
|
10
|
+
* Rules:
|
|
11
|
+
* - an acceptance-criterion id (`REQ-X.A1`) persists into its parent
|
|
12
|
+
* requirement's block — link blocks live under the requirement, never in
|
|
13
|
+
* the Acceptance section;
|
|
14
|
+
* - one bullet per target; a missing block is created right before the
|
|
15
|
+
* requirement's `## Acceptance` heading (or at the end of its prose);
|
|
16
|
+
* - idempotent — re-persisting a listed target leaves the file
|
|
17
|
+
* byte-identical;
|
|
18
|
+
* - a target with no symbol, or a symbol not defined in its file, is refused
|
|
19
|
+
* and the spec file is never touched.
|
|
20
|
+
*/
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.parentRequirementId = parentRequirementId;
|
|
56
|
+
exports.symbolDefinedInSource = symbolDefinedInSource;
|
|
57
|
+
exports.persistLinkToSpecFile = persistLinkToSpecFile;
|
|
58
|
+
const fs = __importStar(require("fs"));
|
|
59
|
+
const path = __importStar(require("path"));
|
|
60
|
+
const postcheck_1 = require("./postcheck");
|
|
61
|
+
const ID_MARKER = /^\s*<!--\s*id:\s*([A-Za-z0-9._-]+)\s*-->\s*$/;
|
|
62
|
+
const ACCEPTANCE_HEADING = /^#{1,6}\s+Acceptance\b/i;
|
|
63
|
+
const BULLET = /^\s+[-*]\s+\S/;
|
|
64
|
+
/** `REQ-X.A1` → `REQ-X`; a requirement id is returned unchanged. */
|
|
65
|
+
function parentRequirementId(specId) {
|
|
66
|
+
return specId.replace(/\.A\d+$/, '');
|
|
67
|
+
}
|
|
68
|
+
function escapeRe(s) {
|
|
69
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Textual "is `symbol` defined in this source" check, in the style of the
|
|
73
|
+
* postcheck verifies-coverage check, widened to qualified names
|
|
74
|
+
* (`Class.method` → both the container and the member must be declared) and
|
|
75
|
+
* class members / type declarations.
|
|
76
|
+
*/
|
|
77
|
+
function symbolDefinedInSource(source, symbol) {
|
|
78
|
+
const parts = symbol.split(/\.|::|#/).filter(Boolean);
|
|
79
|
+
if (parts.length === 0)
|
|
80
|
+
return false;
|
|
81
|
+
return parts.every((part, i) => {
|
|
82
|
+
const s = escapeRe(part);
|
|
83
|
+
const decl = new RegExp(`\\b(function\\*?|const|let|var|class|interface|type|enum|struct|trait|def|fn|func)\\s+${s}\\b` +
|
|
84
|
+
`|\\b${s}\\s*[:=]\\s*(async\\s+)?(function|\\()`);
|
|
85
|
+
if (decl.test(source))
|
|
86
|
+
return true;
|
|
87
|
+
if (i === 0 && parts.length > 1)
|
|
88
|
+
return false;
|
|
89
|
+
// Class member: ` async name(` / ` public static name<T>(`.
|
|
90
|
+
const member = new RegExp(`^\\s*(?:(?:public|private|protected|static|async|readonly|override|get|set)\\s+)*\\*?${s}\\s*(?:<[^>]*>)?\\s*\\(`, 'm');
|
|
91
|
+
return member.test(source);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Persist an asserted link into the spec file. Never throws — failures and
|
|
96
|
+
* refusals come back as `status: 'refused'` with the reason.
|
|
97
|
+
*/
|
|
98
|
+
function persistLinkToSpecFile(opts) {
|
|
99
|
+
const requirementId = parentRequirementId(opts.specId);
|
|
100
|
+
const refuse = (detail, specFile) => ({
|
|
101
|
+
status: 'refused',
|
|
102
|
+
requirementId,
|
|
103
|
+
detail,
|
|
104
|
+
...(specFile ? { specFile } : {}),
|
|
105
|
+
});
|
|
106
|
+
const symbol = (opts.symbol ?? '').trim();
|
|
107
|
+
const filePath = (opts.filePath ?? '').trim().replace(/\\/g, '/');
|
|
108
|
+
if (!symbol)
|
|
109
|
+
return refuse('target has no symbol — a link needs a `path:Symbol` target');
|
|
110
|
+
if (!filePath)
|
|
111
|
+
return refuse('target has no file path');
|
|
112
|
+
if (opts.kind !== 'implements' && opts.kind !== 'tests') {
|
|
113
|
+
return refuse(`kind "${String(opts.kind)}" has no spec-file block`);
|
|
114
|
+
}
|
|
115
|
+
const root = path.resolve(opts.projectRoot);
|
|
116
|
+
const inRoot = (abs) => {
|
|
117
|
+
const rel = path.relative(root, abs);
|
|
118
|
+
return !rel.startsWith('..') && !path.isAbsolute(rel);
|
|
119
|
+
};
|
|
120
|
+
try {
|
|
121
|
+
// 1. The target must be a resolvable `path:Symbol`.
|
|
122
|
+
if (!opts.symbolResolved) {
|
|
123
|
+
const targetAbs = path.resolve(root, filePath);
|
|
124
|
+
if (!inRoot(targetAbs))
|
|
125
|
+
return refuse(`${filePath} resolves outside the project root`);
|
|
126
|
+
let code;
|
|
127
|
+
try {
|
|
128
|
+
code = fs.readFileSync(targetAbs, 'utf8');
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return refuse(`${filePath} does not exist`);
|
|
132
|
+
}
|
|
133
|
+
if (!symbolDefinedInSource(code, symbol)) {
|
|
134
|
+
return refuse(`${symbol} is not defined in ${filePath}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// 2. Locate the spec file and the requirement's block.
|
|
138
|
+
const specAbs = opts.specSourcePath
|
|
139
|
+
? path.resolve(root, opts.specSourcePath)
|
|
140
|
+
: (0, postcheck_1.findSpecFile)(root, requirementId);
|
|
141
|
+
if (!specAbs)
|
|
142
|
+
return refuse(`no spec file under specs/ declares ${requirementId}`);
|
|
143
|
+
if (!inRoot(specAbs))
|
|
144
|
+
return refuse(`spec file resolves outside the project root`);
|
|
145
|
+
const specFile = path.relative(root, specAbs).replace(/\\/g, '/');
|
|
146
|
+
const source = fs.readFileSync(specAbs, 'utf8');
|
|
147
|
+
const block = (0, postcheck_1.requirementBlock)(source, requirementId);
|
|
148
|
+
if (block === null)
|
|
149
|
+
return refuse(`marker <!-- id: ${requirementId} --> not found`, specFile);
|
|
150
|
+
const blockName = opts.kind === 'tests' ? 'verifies' : 'implementations';
|
|
151
|
+
const target = `${filePath}:${symbol}`;
|
|
152
|
+
if ((0, postcheck_1.linkBullets)(block, blockName).includes(target)) {
|
|
153
|
+
return {
|
|
154
|
+
status: 'unchanged',
|
|
155
|
+
requirementId,
|
|
156
|
+
specFile,
|
|
157
|
+
detail: `${blockName}: already lists ${target}`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// 3. Line-level edit inside the requirement's region.
|
|
161
|
+
const eol = source.includes('\r\n') ? '\r\n' : '\n';
|
|
162
|
+
const lines = source.split(/\r?\n/);
|
|
163
|
+
let start = -1;
|
|
164
|
+
let end = lines.length;
|
|
165
|
+
for (let i = 0; i < lines.length; i++) {
|
|
166
|
+
const m = (lines[i] ?? '').match(ID_MARKER);
|
|
167
|
+
if (!m)
|
|
168
|
+
continue;
|
|
169
|
+
if (start === -1) {
|
|
170
|
+
if (m[1] === requirementId)
|
|
171
|
+
start = i;
|
|
172
|
+
}
|
|
173
|
+
else if (!m[1].startsWith(`${requirementId}.`)) {
|
|
174
|
+
end = i;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (start === -1)
|
|
179
|
+
return refuse(`marker <!-- id: ${requirementId} --> not found`, specFile);
|
|
180
|
+
const bullet = ` - ${target}`;
|
|
181
|
+
const header = new RegExp(`^${blockName}:\\s*$`);
|
|
182
|
+
let headerIdx = -1;
|
|
183
|
+
for (let i = start + 1; i < end; i++) {
|
|
184
|
+
if (header.test(lines[i] ?? '')) {
|
|
185
|
+
headerIdx = i;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (headerIdx !== -1) {
|
|
190
|
+
let last = headerIdx;
|
|
191
|
+
for (let i = headerIdx + 1; i < end; i++) {
|
|
192
|
+
const line = lines[i] ?? '';
|
|
193
|
+
if (BULLET.test(line)) {
|
|
194
|
+
last = i;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (line.trim() === '')
|
|
198
|
+
continue;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
lines.splice(last + 1, 0, bullet);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// Prose ends at `## Acceptance`, else at the first criterion marker,
|
|
205
|
+
// else at the end of the region.
|
|
206
|
+
let proseEnd = end;
|
|
207
|
+
for (let i = start + 1; i < end; i++) {
|
|
208
|
+
const line = lines[i] ?? '';
|
|
209
|
+
const m = line.match(ID_MARKER);
|
|
210
|
+
if (ACCEPTANCE_HEADING.test(line) || (m && m[1].startsWith(`${requirementId}.`))) {
|
|
211
|
+
proseEnd = i;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// An `implementations:` block goes before an existing `verifies:` one.
|
|
216
|
+
if (blockName === 'implementations') {
|
|
217
|
+
for (let i = start + 1; i < proseEnd; i++) {
|
|
218
|
+
if (/^verifies:\s*$/.test(lines[i] ?? '')) {
|
|
219
|
+
proseEnd = i;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
let insertAt = proseEnd;
|
|
225
|
+
while (insertAt > start + 1 && (lines[insertAt - 1] ?? '').trim() === '')
|
|
226
|
+
insertAt--;
|
|
227
|
+
const inserted = ['', `${blockName}:`, bullet];
|
|
228
|
+
if (insertAt < lines.length)
|
|
229
|
+
inserted.push('');
|
|
230
|
+
// Collapse the blank line(s) we trimmed past so spacing stays single.
|
|
231
|
+
let removeBlanks = 0;
|
|
232
|
+
while (insertAt + removeBlanks < proseEnd && (lines[insertAt + removeBlanks] ?? '').trim() === '') {
|
|
233
|
+
removeBlanks++;
|
|
234
|
+
}
|
|
235
|
+
lines.splice(insertAt, removeBlanks, ...inserted);
|
|
236
|
+
}
|
|
237
|
+
fs.writeFileSync(specAbs, lines.join(eol), 'utf8');
|
|
238
|
+
return {
|
|
239
|
+
status: 'persisted',
|
|
240
|
+
requirementId,
|
|
241
|
+
specFile,
|
|
242
|
+
detail: `${blockName}: entry ${target} written to ${specFile}`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
return refuse(err instanceof Error ? err.message : String(err));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=link-persist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-persist.js","sourceRoot":"","sources":["../../src/spec-quality/link-persist.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small-plan auto-approval (THROUGHPUT-DOC, REQ-THRU-003).
|
|
3
|
+
*
|
|
4
|
+
* A plan may skip the human plan gate ONLY when the project opted in
|
|
5
|
+
* (`workflow.autoApproveSmallPlans: true` in specship.config.json), every
|
|
6
|
+
* source file it names is already one of the requirement's
|
|
7
|
+
* `implementations:` files, and the requirement has at most
|
|
8
|
+
* `workflow.smallPlanMaxCriteria` (default 3) acceptance criteria. Test files,
|
|
9
|
+
* the requirement's own spec file and CHANGELOG.md never count against the
|
|
10
|
+
* scope — a good plan always names them. The final review is never skipped.
|
|
11
|
+
*
|
|
12
|
+
* Deterministic and file-based, so it runs inside a workflow worktree. The
|
|
13
|
+
* AUTO_APPROVE=yes|no marker is the contract the approval step reads.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_SMALL_PLAN_MAX_CRITERIA = 3;
|
|
16
|
+
export interface PlanScopeInput {
|
|
17
|
+
planText: string;
|
|
18
|
+
/** Files named by the requirement's `implementations:` bullets. */
|
|
19
|
+
implementationFiles: string[];
|
|
20
|
+
criteriaCount: number;
|
|
21
|
+
optIn: boolean;
|
|
22
|
+
maxCriteria?: number;
|
|
23
|
+
/** The requirement's spec file (project-relative), always in scope. */
|
|
24
|
+
specFile?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PlanScopeResult {
|
|
27
|
+
autoApprove: boolean;
|
|
28
|
+
reasons: string[];
|
|
29
|
+
/** Source files the plan names that are outside the requirement's implementations. */
|
|
30
|
+
outOfScope: string[];
|
|
31
|
+
}
|
|
32
|
+
/** Project-relative file paths a plan names (deduplicated, in order). */
|
|
33
|
+
export declare function filesNamedInPlan(planText: string): string[];
|
|
34
|
+
export declare function assessPlanScope(input: PlanScopeInput): PlanScopeResult;
|
|
35
|
+
/** File-based loader: reads the requirement block and the project's opt-in. */
|
|
36
|
+
export declare function assessPlanScopeFromFile(projectRoot: string, requirementId: string, planText: string): PlanScopeResult;
|
|
37
|
+
export declare function formatPlanScope(result: PlanScopeResult): string;
|
|
38
|
+
//# sourceMappingURL=plan-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-scope.d.ts","sourceRoot":"","sources":["../../src/spec-quality/plan-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sFAAsF;IACtF,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAOD,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAS3D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAmBtE;AAgBD,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAmBrH;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAE/D"}
|