@specship/specship-win32-arm64 0.25.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 +8 -10
- package/lib/dist/activation/steering.d.ts.map +1 -1
- package/lib/dist/activation/steering.js +21 -6
- 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 +762 -81
- package/lib/dist/bin/specship.js.map +1 -1
- package/lib/dist/commands/specship/learn.md +22 -0
- package/lib/dist/commands/specship/spec.md +12 -0
- 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 +11 -0
- package/lib/dist/enforce/changed-files.d.ts.map +1 -1
- package/lib/dist/enforce/changed-files.js +42 -0
- package/lib/dist/enforce/changed-files.js.map +1 -1
- package/lib/dist/enforce/enforce.d.ts +25 -1
- package/lib/dist/enforce/enforce.d.ts.map +1 -1
- package/lib/dist/enforce/enforce.js +58 -3
- 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/specs/markdown-spec-extractor.d.ts.map +1 -1
- package/lib/dist/extraction/specs/markdown-spec-extractor.js +16 -2
- package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
- package/lib/dist/graph/spec-coverage.d.ts +57 -1
- package/lib/dist/graph/spec-coverage.d.ts.map +1 -1
- package/lib/dist/graph/spec-coverage.js +129 -5
- package/lib/dist/graph/spec-coverage.js.map +1 -1
- 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 +8 -3
- package/lib/dist/index.d.ts.map +1 -1
- package/lib/dist/index.js +65 -9
- 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 +23 -1
- package/lib/dist/installer/index.d.ts.map +1 -1
- package/lib/dist/installer/index.js +22 -3
- package/lib/dist/installer/index.js.map +1 -1
- package/lib/dist/installer/install-defaults.d.ts +19 -0
- package/lib/dist/installer/install-defaults.d.ts.map +1 -1
- package/lib/dist/installer/install-defaults.js +28 -0
- package/lib/dist/installer/install-defaults.js.map +1 -1
- 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 +4 -0
- package/lib/dist/installer/targets/claude.d.ts.map +1 -1
- package/lib/dist/installer/targets/claude.js +8 -25
- 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 +18 -0
- 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/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/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/model-context.d.ts +22 -9
- package/lib/dist/mcp/model-context.d.ts.map +1 -1
- package/lib/dist/mcp/model-context.js +35 -13
- package/lib/dist/mcp/model-context.js.map +1 -1
- package/lib/dist/mcp/proxy.js +1 -1
- 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 +67 -1
- 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 +69 -21
- package/lib/dist/mcp/spec-tools.js.map +1 -1
- package/lib/dist/mcp/tools.d.ts +37 -1
- package/lib/dist/mcp/tools.d.ts.map +1 -1
- package/lib/dist/mcp/tools.js +120 -9
- 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/test-title-links.d.ts.map +1 -1
- package/lib/dist/resolution/test-title-links.js +3 -14
- package/lib/dist/resolution/test-title-links.js.map +1 -1
- package/lib/dist/server/routes/claude.js +15 -7
- package/lib/dist/server/routes/knowledge.js +118 -0
- package/lib/dist/server/server.js +2 -0
- package/lib/dist/spec-lint/lint.d.ts +1 -1
- package/lib/dist/spec-lint/lint.d.ts.map +1 -1
- package/lib/dist/spec-lint/lint.js +63 -2
- package/lib/dist/spec-lint/lint.js.map +1 -1
- 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/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-implement-mixed.yaml +21 -436
- package/lib/dist/workflows/defaults/spec-implement.yaml +465 -46
- 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 +2 -2
- package/package.json +5 -3
- package/node.exe +0 -4
|
@@ -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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Small-plan auto-approval (THROUGHPUT-DOC, REQ-THRU-003).
|
|
4
|
+
*
|
|
5
|
+
* A plan may skip the human plan gate ONLY when the project opted in
|
|
6
|
+
* (`workflow.autoApproveSmallPlans: true` in specship.config.json), every
|
|
7
|
+
* source file it names is already one of the requirement's
|
|
8
|
+
* `implementations:` files, and the requirement has at most
|
|
9
|
+
* `workflow.smallPlanMaxCriteria` (default 3) acceptance criteria. Test files,
|
|
10
|
+
* the requirement's own spec file and CHANGELOG.md never count against the
|
|
11
|
+
* scope — a good plan always names them. The final review is never skipped.
|
|
12
|
+
*
|
|
13
|
+
* Deterministic and file-based, so it runs inside a workflow worktree. The
|
|
14
|
+
* AUTO_APPROVE=yes|no marker is the contract the approval step reads.
|
|
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.DEFAULT_SMALL_PLAN_MAX_CRITERIA = void 0;
|
|
51
|
+
exports.filesNamedInPlan = filesNamedInPlan;
|
|
52
|
+
exports.assessPlanScope = assessPlanScope;
|
|
53
|
+
exports.assessPlanScopeFromFile = assessPlanScopeFromFile;
|
|
54
|
+
exports.formatPlanScope = formatPlanScope;
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
const postcheck_1 = require("./postcheck");
|
|
58
|
+
exports.DEFAULT_SMALL_PLAN_MAX_CRITERIA = 3;
|
|
59
|
+
// A trailing full stop/!/? ends a sentence, not the path — a plan naming an
|
|
60
|
+
// out-of-scope file at the end of a sentence must still be caught.
|
|
61
|
+
const PATH_TOKEN = /(?:^|[\s`'"(\[])((?:\.{0,2}\/)?[A-Za-z0-9_@.-]+(?:\/[A-Za-z0-9_@.-]+)*\.[A-Za-z0-9]{1,6})(?=[\s`'")\]:,;.!?]|$)/g;
|
|
62
|
+
const TEST_FILE = /(^|\/)(__tests__|tests?|e2e|spec)\/|\.(test|spec)\.[A-Za-z0-9]+$/;
|
|
63
|
+
/** Project-relative file paths a plan names (deduplicated, in order). */
|
|
64
|
+
function filesNamedInPlan(planText) {
|
|
65
|
+
const out = [];
|
|
66
|
+
for (const m of planText.matchAll(PATH_TOKEN)) {
|
|
67
|
+
const p = m[1].replace(/^\.\//, '');
|
|
68
|
+
if (!p.includes('/') && !/^[A-Z][A-Za-z]+\.md$/.test(p))
|
|
69
|
+
continue; // bare words like "e.g." are not files
|
|
70
|
+
if (/^\d+(\.\d+)+$/.test(p) || /^https?:/.test(p))
|
|
71
|
+
continue;
|
|
72
|
+
if (!out.includes(p))
|
|
73
|
+
out.push(p);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function assessPlanScope(input) {
|
|
78
|
+
const maxCriteria = input.maxCriteria ?? exports.DEFAULT_SMALL_PLAN_MAX_CRITERIA;
|
|
79
|
+
const reasons = [];
|
|
80
|
+
if (!input.optIn) {
|
|
81
|
+
return { autoApprove: false, reasons: ['workflow.autoApproveSmallPlans is not enabled'], outOfScope: [] };
|
|
82
|
+
}
|
|
83
|
+
if (input.criteriaCount > maxCriteria) {
|
|
84
|
+
reasons.push(`requirement has ${input.criteriaCount} criteria (limit ${maxCriteria})`);
|
|
85
|
+
}
|
|
86
|
+
const allowed = new Set(input.implementationFiles);
|
|
87
|
+
const named = filesNamedInPlan(input.planText);
|
|
88
|
+
const outOfScope = named.filter((f) => !allowed.has(f) && !TEST_FILE.test(f) && f !== 'CHANGELOG.md' && f !== input.specFile);
|
|
89
|
+
if (outOfScope.length > 0)
|
|
90
|
+
reasons.push(`plan names files outside implementations: ${outOfScope.join(', ')}`);
|
|
91
|
+
if (named.filter((f) => allowed.has(f)).length === 0) {
|
|
92
|
+
reasons.push('plan names none of the requirement\'s implementation files');
|
|
93
|
+
}
|
|
94
|
+
return { autoApprove: reasons.length === 0, reasons, outOfScope };
|
|
95
|
+
}
|
|
96
|
+
function readWorkflowConfig(projectRoot) {
|
|
97
|
+
try {
|
|
98
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'specship.config.json'), 'utf8'));
|
|
99
|
+
return (cfg && typeof cfg.workflow === 'object' && cfg.workflow) || {};
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** File-based loader: reads the requirement block and the project's opt-in. */
|
|
106
|
+
function assessPlanScopeFromFile(projectRoot, requirementId, planText) {
|
|
107
|
+
const cfg = readWorkflowConfig(projectRoot);
|
|
108
|
+
const specFile = (0, postcheck_1.findSpecFile)(projectRoot, requirementId);
|
|
109
|
+
const block = specFile ? (0, postcheck_1.requirementBlock)(fs.readFileSync(specFile, 'utf8'), requirementId) : null;
|
|
110
|
+
if (!specFile || !block) {
|
|
111
|
+
return { autoApprove: false, reasons: [`no spec block for ${requirementId}`], outOfScope: [] };
|
|
112
|
+
}
|
|
113
|
+
const implementationFiles = (0, postcheck_1.linkBullets)(block, 'implementations').map((b) => {
|
|
114
|
+
const idx = b.lastIndexOf(':');
|
|
115
|
+
return idx > 0 ? b.slice(0, idx) : b;
|
|
116
|
+
});
|
|
117
|
+
return assessPlanScope({
|
|
118
|
+
planText,
|
|
119
|
+
implementationFiles,
|
|
120
|
+
criteriaCount: (0, postcheck_1.criterionIds)(block, requirementId).length,
|
|
121
|
+
optIn: cfg.autoApproveSmallPlans === true,
|
|
122
|
+
maxCriteria: typeof cfg.smallPlanMaxCriteria === 'number' ? cfg.smallPlanMaxCriteria : undefined,
|
|
123
|
+
specFile: path.relative(projectRoot, specFile),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function formatPlanScope(result) {
|
|
127
|
+
return [`AUTO_APPROVE=${result.autoApprove ? 'yes' : 'no'}`, ...result.reasons.map((r) => `- ${r}`)].join('\n');
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=plan-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-scope.js","sourceRoot":"","sources":["../../src/spec-quality/plan-scope.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCH,4CASC;AAED,0CAmBC;AAiBD,0DAmBC;AAED,0CAEC;AApGD,uCAAyB;AACzB,2CAA6B;AAC7B,2CAAwF;AAE3E,QAAA,+BAA+B,GAAG,CAAC,CAAC;AAoBjD,4EAA4E;AAC5E,mEAAmE;AACnE,MAAM,UAAU,GAAG,kHAAkH,CAAC;AACtI,MAAM,SAAS,GAAG,kEAAkE,CAAC;AAErF,yEAAyE;AACzE,SAAgB,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS,CAAC,uCAAuC;QAC1G,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QAC5D,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAgB,eAAe,CAAC,KAAqB;IACnD,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,uCAA+B,CAAC;IACzE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,+CAA+C,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC5G,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,GAAG,WAAW,EAAE,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,mBAAmB,KAAK,CAAC,aAAa,oBAAoB,WAAW,GAAG,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAC7B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,KAAK,KAAK,CAAC,QAAQ,CAC7F,CAAC;IACF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,6CAA6C,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9G,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AACpE,CAAC;AAOD,SAAS,kBAAkB,CAAC,WAAmB;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,sBAAsB,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAChG,OAAO,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,SAAgB,uBAAuB,CAAC,WAAmB,EAAE,aAAqB,EAAE,QAAgB;IAClG,MAAM,GAAG,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAA,wBAAY,EAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAA,4BAAgB,EAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnG,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;QACxB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,qBAAqB,aAAa,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IACjG,CAAC;IACD,MAAM,mBAAmB,GAAG,IAAA,uBAAW,EAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1E,MAAM,GAAG,GAAG,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IACH,OAAO,eAAe,CAAC;QACrB,QAAQ;QACR,mBAAmB;QACnB,aAAa,EAAE,IAAA,wBAAY,EAAC,KAAK,EAAE,aAAa,CAAC,CAAC,MAAM;QACxD,KAAK,EAAE,GAAG,CAAC,qBAAqB,KAAK,IAAI;QACzC,WAAW,EAAE,OAAO,GAAG,CAAC,oBAAoB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS;QAChG,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC;KAC/C,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,eAAe,CAAC,MAAuB;IACrD,OAAO,CAAC,gBAAgB,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClH,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mechanical post-implement checks (PIPETRUST-DOC, REQ-PIPETRUST-005).
|
|
3
|
+
*
|
|
4
|
+
* On 2026-10-26 every `spec-implement` run needed a human to finish it, for
|
|
5
|
+
* reasons a script can see: test evidence linked by `it()` title strings (no
|
|
6
|
+
* edge, so the requirement could never reach `verified`), criteria with no
|
|
7
|
+
* test, source changes with no CHANGELOG entry, generated reference docs not
|
|
8
|
+
* regenerated. Postcheck checks those mechanically after implement so the
|
|
9
|
+
* workflow can fix them before a human ever looks.
|
|
10
|
+
*
|
|
11
|
+
* File-based by design: it runs inside a workflow worktree, which has no
|
|
12
|
+
* SpecShip index, so it reads the spec file and the test files directly.
|
|
13
|
+
*
|
|
14
|
+
* 1. `verifies:` coverage — every bullet is `path:Symbol` with a real,
|
|
15
|
+
* space-free symbol defined in the test file; every acceptance criterion
|
|
16
|
+
* id appears in at least one of those test files.
|
|
17
|
+
* 2. CHANGELOG — when files under `src/` changed against the base ref, the
|
|
18
|
+
* `## [Unreleased]` section of CHANGELOG.md changed too.
|
|
19
|
+
* 3. Project checks — each `postcheck.commands` entry in specship.config.json
|
|
20
|
+
* (`{ name, check, fix? }`); with `fix`, the fix command runs first.
|
|
21
|
+
*/
|
|
22
|
+
export interface PostcheckFinding {
|
|
23
|
+
check: 'verifies' | 'changelog' | 'command' | 'knowledge' | 'mutation';
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PostcheckResult {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
findings: PostcheckFinding[];
|
|
29
|
+
/** One line per check that ran, for the report. */
|
|
30
|
+
ran: string[];
|
|
31
|
+
}
|
|
32
|
+
export interface PostcheckCommand {
|
|
33
|
+
name: string;
|
|
34
|
+
check: string;
|
|
35
|
+
fix?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface PostcheckOptions {
|
|
38
|
+
projectRoot: string;
|
|
39
|
+
requirementId: string;
|
|
40
|
+
/** Run each configured command's `fix` before its `check`. */
|
|
41
|
+
fix?: boolean;
|
|
42
|
+
/** Base ref for the CHANGELOG check; defaults to the recorded run base, then the merge-base with the default branch. */
|
|
43
|
+
base?: string;
|
|
44
|
+
/** Inject command execution (tests). Returns the exit code. */
|
|
45
|
+
run?: (command: string, cwd: string) => {
|
|
46
|
+
code: number;
|
|
47
|
+
output: string;
|
|
48
|
+
};
|
|
49
|
+
/** Inject git diff lookups (tests). */
|
|
50
|
+
changedFiles?: (base: string) => string[];
|
|
51
|
+
/** Inject the base CHANGELOG content (tests). */
|
|
52
|
+
baseFile?: (base: string, relPath: string) => string | null;
|
|
53
|
+
}
|
|
54
|
+
/** The requirement's block of the spec file: from its id marker to the next requirement-level marker. */
|
|
55
|
+
export declare function requirementBlock(source: string, requirementId: string): string | null;
|
|
56
|
+
/** Acceptance criterion ids declared under the requirement. */
|
|
57
|
+
export declare function criterionIds(block: string, requirementId: string): string[];
|
|
58
|
+
/** Bullets of a `<name>:` link block inside the requirement block. */
|
|
59
|
+
export declare function linkBullets(block: string, blockName: 'verifies' | 'implementations'): string[];
|
|
60
|
+
/** REQ-PIPETRUST-005.A1/A2 — `verifies:` bullets are real named test symbols and cover every criterion. */
|
|
61
|
+
export declare function checkVerifiesCoverage(projectRoot: string, specSource: string, requirementId: string): PostcheckFinding[];
|
|
62
|
+
/** The `## [Unreleased]` section of a CHANGELOG, or '' when absent. */
|
|
63
|
+
export declare function unreleasedSection(changelog: string): string;
|
|
64
|
+
/** Base ref: explicit, else the run base the readiness step recorded, else merge-base with the default branch. */
|
|
65
|
+
export declare function resolveBaseRef(projectRoot: string, explicit?: string): string | null;
|
|
66
|
+
/** REQ-PIPETRUST-005.A3 — source changes need a CHANGELOG [Unreleased] change. */
|
|
67
|
+
export declare function checkChangelog(opts: PostcheckOptions): PostcheckFinding[];
|
|
68
|
+
/** `postcheck.commands` from specship.config.json (empty when absent or malformed). */
|
|
69
|
+
export declare function readPostcheckCommands(projectRoot: string): PostcheckCommand[];
|
|
70
|
+
/** REQ-PIPETRUST-005.A4 — configured check commands, fix first when asked. */
|
|
71
|
+
export declare function checkConfiguredCommands(opts: PostcheckOptions, ran: string[]): PostcheckFinding[];
|
|
72
|
+
/**
|
|
73
|
+
* REQ-THRU-010 — when `enforce.mutation` is configured, the requirement's
|
|
74
|
+
* implementation files must clear the mutant kill-rate threshold in the
|
|
75
|
+
* project's mutation report. Without configuration the check does not exist;
|
|
76
|
+
* an unreadable report is reported as skipped (never a pass, never a failure),
|
|
77
|
+
* matching the enforcement gate (REQ-QGATE-002.A3).
|
|
78
|
+
*/
|
|
79
|
+
export declare function checkMutation(opts: PostcheckOptions, specFile: string | null, ran: string[]): PostcheckFinding[];
|
|
80
|
+
/** Locate the spec file declaring `requirementId` under specs/. */
|
|
81
|
+
export declare function findSpecFile(projectRoot: string, requirementId: string): string | null;
|
|
82
|
+
export declare function runPostcheck(opts: PostcheckOptions): PostcheckResult;
|
|
83
|
+
/** Marker-led rendering; POSTCHECK=passed|failed is the workflow contract. */
|
|
84
|
+
export declare function formatPostcheck(result: PostcheckResult): string;
|
|
85
|
+
//# sourceMappingURL=postcheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postcheck.d.ts","sourceRoot":"","sources":["../../src/spec-quality/postcheck.ts"],"names":[],"mappings":"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"}
|