sdd-mcp-server 5.3.0 → 5.5.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/CHANGELOG.md +49 -0
- package/README.md +85 -13
- package/agents/security-auditor.md +2 -2
- package/dist/__tests__/helpers/message-clarity.d.ts +15 -0
- package/dist/__tests__/helpers/message-clarity.js +26 -0
- package/dist/__tests__/helpers/message-clarity.js.map +1 -0
- package/dist/__tests__/helpers/message-extractor.d.ts +22 -0
- package/dist/__tests__/helpers/message-extractor.js +135 -0
- package/dist/__tests__/helpers/message-extractor.js.map +1 -0
- package/dist/__tests__/unit/jev/helpers.d.ts +84 -0
- package/dist/__tests__/unit/jev/helpers.js +188 -0
- package/dist/__tests__/unit/jev/helpers.js.map +1 -0
- package/dist/adapters/cli/SDDToolAdapter.d.ts +5 -1
- package/dist/adapters/cli/SDDToolAdapter.js +22 -3
- package/dist/adapters/cli/SDDToolAdapter.js.map +1 -1
- package/dist/application/services/JevAdvisoryService.d.ts +53 -0
- package/dist/application/services/JevAdvisoryService.js +310 -0
- package/dist/application/services/JevAdvisoryService.js.map +1 -0
- package/dist/application/services/JevBaseRef.d.ts +21 -0
- package/dist/application/services/JevBaseRef.js +46 -0
- package/dist/application/services/JevBaseRef.js.map +1 -0
- package/dist/application/services/JevChangeManifest.d.ts +33 -0
- package/dist/application/services/JevChangeManifest.js +208 -0
- package/dist/application/services/JevChangeManifest.js.map +1 -0
- package/dist/application/services/JevCheckTypes.d.ts +74 -0
- package/dist/application/services/JevCheckTypes.js +2 -0
- package/dist/application/services/JevCheckTypes.js.map +1 -0
- package/dist/application/services/JevCodeExcerpts.d.ts +23 -0
- package/dist/application/services/JevCodeExcerpts.js +102 -0
- package/dist/application/services/JevCodeExcerpts.js.map +1 -0
- package/dist/application/services/JevCommitLookup.d.ts +17 -0
- package/dist/application/services/JevCommitLookup.js +72 -0
- package/dist/application/services/JevCommitLookup.js.map +1 -0
- package/dist/application/services/JevExchange.d.ts +48 -0
- package/dist/application/services/JevExchange.js +119 -0
- package/dist/application/services/JevExchange.js.map +1 -0
- package/dist/application/services/JevQuestionCatalog.d.ts +28 -0
- package/dist/application/services/JevQuestionCatalog.js +194 -0
- package/dist/application/services/JevQuestionCatalog.js.map +1 -0
- package/dist/application/services/JevRedaction.d.ts +12 -0
- package/dist/application/services/JevRedaction.js +56 -0
- package/dist/application/services/JevRedaction.js.map +1 -0
- package/dist/application/services/JevReport.d.ts +41 -0
- package/dist/application/services/JevReport.js +101 -0
- package/dist/application/services/JevReport.js.map +1 -0
- package/dist/application/services/JevResponseValidator.d.ts +34 -0
- package/dist/application/services/JevResponseValidator.js +98 -0
- package/dist/application/services/JevResponseValidator.js.map +1 -0
- package/dist/application/services/JevResults.d.ts +3 -0
- package/dist/application/services/JevResults.js +7 -0
- package/dist/application/services/JevResults.js.map +1 -0
- package/dist/application/services/JevReviewCheck.d.ts +20 -0
- package/dist/application/services/JevReviewCheck.js +120 -0
- package/dist/application/services/JevReviewCheck.js.map +1 -0
- package/dist/application/services/JevSettings.d.ts +18 -0
- package/dist/application/services/JevSettings.js +72 -0
- package/dist/application/services/JevSettings.js.map +1 -0
- package/dist/application/services/JevSidecarStore.d.ts +68 -0
- package/dist/application/services/JevSidecarStore.js +201 -0
- package/dist/application/services/JevSidecarStore.js.map +1 -0
- package/dist/application/services/JevSpecState.d.ts +38 -0
- package/dist/application/services/JevSpecState.js +109 -0
- package/dist/application/services/JevSpecState.js.map +1 -0
- package/dist/application/services/JevTestcaseState.d.ts +22 -0
- package/dist/application/services/JevTestcaseState.js +85 -0
- package/dist/application/services/JevTestcaseState.js.map +1 -0
- package/dist/application/services/RedEvidenceClassifier.d.ts +10 -0
- package/dist/application/services/RedEvidenceClassifier.js +32 -0
- package/dist/application/services/RedEvidenceClassifier.js.map +1 -0
- package/dist/application/services/WorkflowEngineService.d.ts +28 -0
- package/dist/application/services/WorkflowEngineService.js +111 -63
- package/dist/application/services/WorkflowEngineService.js.map +1 -1
- package/dist/application/services/WorkflowValidationService.d.ts +16 -0
- package/dist/application/services/WorkflowValidationService.js +39 -34
- package/dist/application/services/WorkflowValidationService.js.map +1 -1
- package/dist/cli/install-skills.js +0 -0
- package/dist/cli/install-target.d.ts +8 -6
- package/dist/cli/install-target.js +11 -3
- package/dist/cli/install-target.js.map +1 -1
- package/dist/cli/tool-support/root-guidance.js +1 -0
- package/dist/cli/tool-support/root-guidance.js.map +1 -1
- package/dist/cli/tool-support/target-installer.d.ts +1 -0
- package/dist/cli/tool-support/target-installer.js +9 -5
- package/dist/cli/tool-support/target-installer.js.map +1 -1
- package/dist/domain/ports.d.ts +34 -0
- package/dist/domain/ports.js +11 -1
- package/dist/domain/ports.js.map +1 -1
- package/dist/infrastructure/adapters/FetchJevHttpAdapter.d.ts +4 -0
- package/dist/infrastructure/adapters/FetchJevHttpAdapter.js +73 -0
- package/dist/infrastructure/adapters/FetchJevHttpAdapter.js.map +1 -0
- package/dist/infrastructure/adapters/NodeGitAdapter.d.ts +5 -0
- package/dist/infrastructure/adapters/NodeGitAdapter.js +31 -0
- package/dist/infrastructure/adapters/NodeGitAdapter.js.map +1 -0
- package/dist/infrastructure/di/container.js +10 -0
- package/dist/infrastructure/di/container.js.map +1 -1
- package/dist/infrastructure/di/types.d.ts +4 -0
- package/dist/infrastructure/di/types.js +4 -0
- package/dist/infrastructure/di/types.js.map +1 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.d.ts +1 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.js +72 -1
- package/dist/infrastructure/mcp/sddToolDefinitions.js.map +1 -1
- package/dist/shared/version.d.ts +1 -1
- package/dist/shared/version.js +1 -1
- package/package.json +1 -1
- package/skills/output-clarity-ladder/SKILL.md +43 -0
- package/skills/output-clarity-ladder/references/ja.md +30 -0
- package/skills/output-clarity-ladder/references/zh-TW.md +30 -0
- package/skills/sdd-commit/SKILL.md +4 -3
- package/skills/sdd-design/SKILL.md +1 -1
- package/skills/sdd-implement/SKILL.md +4 -4
- package/skills/sdd-requirements/SKILL.md +6 -6
- package/skills/sdd-review/SKILL.md +3 -2
- package/skills/sdd-security-check/SKILL.md +3 -2
- package/skills/sdd-tasks/REFERENCE.md +4 -0
- package/skills/sdd-tasks/SKILL.md +8 -6
- package/templates/CLAUDE.md +1 -1
- package/templates/codex-AGENTS.md +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { redactState, redactThenClip } from './JevRedaction.js';
|
|
2
|
+
import { buildDesignQuestions, buildRedQuestions, buildRequirementsQuestions } from './JevQuestionCatalog.js';
|
|
3
|
+
import { metadata, parseList, parseSections, uniqueSections, visibleLines } from './WorkflowValidationService.js';
|
|
4
|
+
/** Serialized `state` cap for spec-text requests. */
|
|
5
|
+
export const MAX_STATE_BYTES = 32_768;
|
|
6
|
+
const FIELD_LIMIT = 1500;
|
|
7
|
+
const REQUIREMENT_HEADING = /^###\s+((?:FR|NFR)-\d+):\s*(.+?)\s*$/;
|
|
8
|
+
const DECISION_HEADING = /^###\s+(D-\d+):\s*(.+?)\s*$/;
|
|
9
|
+
function clip(value, limit, counts) {
|
|
10
|
+
const result = redactThenClip(value, limit);
|
|
11
|
+
counts.n += result.count;
|
|
12
|
+
return result.text;
|
|
13
|
+
}
|
|
14
|
+
const sizeOf = (state) => Buffer.byteLength(JSON.stringify(state), 'utf8');
|
|
15
|
+
/** Redacts every item, then keeps whole items in order until the next one would exceed the cap. */
|
|
16
|
+
export function fitState(kind, items, context, maxBytes = MAX_STATE_BYTES) {
|
|
17
|
+
const redactedContext = context && redactState(context);
|
|
18
|
+
const kept = [];
|
|
19
|
+
const keptIds = [];
|
|
20
|
+
const omittedIds = [];
|
|
21
|
+
let redactions = redactedContext?.count ?? 0;
|
|
22
|
+
let full = false;
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
const redacted = redactState(item);
|
|
25
|
+
const candidate = { kind, items: [...kept, redacted.state], ...(redactedContext && { context: redactedContext.state }) };
|
|
26
|
+
if (full || sizeOf(candidate) > maxBytes) {
|
|
27
|
+
full = true;
|
|
28
|
+
omittedIds.push(item.id);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
kept.push(redacted.state);
|
|
32
|
+
keptIds.push(item.id);
|
|
33
|
+
redactions += redacted.count;
|
|
34
|
+
}
|
|
35
|
+
const state = { kind, items: kept, ...(redactedContext && { context: redactedContext.state }) };
|
|
36
|
+
return { state, keptIds, omittedIds, truncated: omittedIds.length > 0, redactions };
|
|
37
|
+
}
|
|
38
|
+
const sectionsOf = (content, heading) => uniqueSections(parseSections(visibleLines(content), heading).sections);
|
|
39
|
+
function toBuilt(fit, questions, itemIds, secondaryIds, extraRedactions = 0) {
|
|
40
|
+
return { state: fit.state, questions, itemIds, secondaryIds, omittedIds: fit.omittedIds, truncated: fit.truncated, redactions: fit.redactions + extraRedactions };
|
|
41
|
+
}
|
|
42
|
+
export function buildRequirementsExchange(snapshot) {
|
|
43
|
+
const counts = { n: 0 };
|
|
44
|
+
const bound = (value, limit = FIELD_LIMIT) => clip(value, limit, counts);
|
|
45
|
+
const items = sectionsOf(snapshot.content, REQUIREMENT_HEADING).map((section) => ({
|
|
46
|
+
id: section.id,
|
|
47
|
+
title: bound(section.title),
|
|
48
|
+
objective: bound(metadata(section, 'Objective')),
|
|
49
|
+
ears: bound(metadata(section, 'EARS Specification')),
|
|
50
|
+
acceptance: bound(metadata(section, 'Acceptance Criteria')),
|
|
51
|
+
}));
|
|
52
|
+
if (items.length === 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
const fit = fitState('requirements', items, { goal: bound(snapshot.description) });
|
|
55
|
+
if (fit.keptIds.length === 0)
|
|
56
|
+
return undefined;
|
|
57
|
+
return toBuilt(fit, buildRequirementsQuestions(fit.keptIds), fit.keptIds, [], counts.n);
|
|
58
|
+
}
|
|
59
|
+
/** RED evidence is the whole state: command, summary, test name, and the task's acceptance criteria. No source code. */
|
|
60
|
+
export function buildRedExchange(taskNumber, testName, evidence, acceptanceCriteria) {
|
|
61
|
+
const counts = { n: 0 };
|
|
62
|
+
const bound = (value, limit = FIELD_LIMIT) => clip(value, limit, counts);
|
|
63
|
+
const item = {
|
|
64
|
+
id: 'red',
|
|
65
|
+
taskNumber: bound(taskNumber, 50),
|
|
66
|
+
testName: bound(testName, 300),
|
|
67
|
+
command: bound(evidence.command, 500),
|
|
68
|
+
summary: bound(evidence.summary, 2000),
|
|
69
|
+
acceptance: bound(acceptanceCriteria),
|
|
70
|
+
};
|
|
71
|
+
const fit = fitState('red', [item], undefined);
|
|
72
|
+
return fit.keptIds.length === 0 ? undefined : toBuilt(fit, buildRedQuestions(), fit.keptIds, [], counts.n);
|
|
73
|
+
}
|
|
74
|
+
export function buildDesignExchange(snapshot) {
|
|
75
|
+
const counts = { n: 0 };
|
|
76
|
+
const bound = (value, limit = FIELD_LIMIT) => clip(value, limit, counts);
|
|
77
|
+
const requirements = sectionsOf(snapshot.requirementsContent ?? '', REQUIREMENT_HEADING);
|
|
78
|
+
const requirementText = new Map(requirements.map((section) => [section.id, {
|
|
79
|
+
id: section.id, ears: bound(metadata(section, 'EARS Specification')), objective: bound(metadata(section, 'Objective')),
|
|
80
|
+
}]));
|
|
81
|
+
const decisions = sectionsOf(snapshot.content, DECISION_HEADING).map((section) => ({
|
|
82
|
+
id: section.id,
|
|
83
|
+
title: bound(section.title),
|
|
84
|
+
covers: parseList(metadata(section, 'Covers')),
|
|
85
|
+
decision: bound(metadata(section, 'Decision')),
|
|
86
|
+
failureBehavior: bound(metadata(section, 'Failure behavior')),
|
|
87
|
+
verification: bound(metadata(section, 'Verification')),
|
|
88
|
+
}));
|
|
89
|
+
if (decisions.length === 0)
|
|
90
|
+
return undefined;
|
|
91
|
+
const decisionItems = decisions.map((decision) => ({
|
|
92
|
+
...decision,
|
|
93
|
+
coveredRequirements: decision.covers.flatMap((id) => requirementText.get(id) ?? []),
|
|
94
|
+
}));
|
|
95
|
+
const nfrItems = requirements.filter((section) => section.id.startsWith('NFR-')).map((section) => ({
|
|
96
|
+
id: section.id,
|
|
97
|
+
title: bound(section.title),
|
|
98
|
+
ears: bound(metadata(section, 'EARS Specification')),
|
|
99
|
+
objective: bound(metadata(section, 'Objective')),
|
|
100
|
+
coveredBy: decisions.filter((decision) => decision.covers.includes(section.id)).map(({ id, decision }) => ({ id, decision })),
|
|
101
|
+
}));
|
|
102
|
+
const fit = fitState('design', [...decisionItems, ...nfrItems], undefined);
|
|
103
|
+
const decisionIds = fit.keptIds.filter((id) => id.startsWith('D-'));
|
|
104
|
+
const nfrIds = fit.keptIds.filter((id) => id.startsWith('NFR-'));
|
|
105
|
+
if (decisionIds.length === 0)
|
|
106
|
+
return undefined;
|
|
107
|
+
return toBuilt(fit, buildDesignQuestions(decisionIds, nfrIds), decisionIds, nfrIds, counts.n);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=JevSpecState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JevSpecState.js","sourceRoot":"","sources":["../../../src/application/services/JevSpecState.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { JevCase } from './JevCheckTypes.js';
|
|
2
|
+
import { type BuiltExchange } from './JevSpecState.js';
|
|
3
|
+
import type { AdvisorySnapshot } from './WorkflowEngineService.js';
|
|
4
|
+
export interface TestcaseExchange extends BuiltExchange {
|
|
5
|
+
/** Kept tasks with whether their TDD is marked not-applicable. */
|
|
6
|
+
tasks: Array<{
|
|
7
|
+
id: string;
|
|
8
|
+
notApplicable: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
/** Case task ids that do not belong to any TDD-required task in tasks.md, sorted. */
|
|
11
|
+
unknownTasks: string[];
|
|
12
|
+
/** Case task ids of TDD-required tasks dropped by the state cap, sorted. */
|
|
13
|
+
truncatedTasks: string[];
|
|
14
|
+
/** Number of supplied cases that belong to a TDD-required task, before truncation. */
|
|
15
|
+
matchedCases: number;
|
|
16
|
+
/** Number of TDD-required tasks in tasks.md, before truncation. */
|
|
17
|
+
tddTaskCount: number;
|
|
18
|
+
caseListSha256: string;
|
|
19
|
+
}
|
|
20
|
+
/** Canonical, order-sensitive hash of the caller's whole case list. */
|
|
21
|
+
export declare function hashCaseList(cases: readonly JevCase[]): string;
|
|
22
|
+
export declare function buildTestcaseExchange(snapshot: AdvisorySnapshot, cases: readonly JevCase[]): TestcaseExchange | undefined;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { buildTestcaseQuestions } from './JevQuestionCatalog.js';
|
|
3
|
+
import { redactThenClip } from './JevRedaction.js';
|
|
4
|
+
import { fitState } from './JevSpecState.js';
|
|
5
|
+
import { metadata, parseList, parseSections, uniqueSections, visibleLines } from './WorkflowValidationService.js';
|
|
6
|
+
const FIELD_LIMIT = 1500;
|
|
7
|
+
const CASE_FIELD_LIMIT = 500;
|
|
8
|
+
const TASK_HEADING = /^###\s+(\d+(?:\.\d+)+)\s+(.+?)\s*$/;
|
|
9
|
+
const REQUIREMENT_HEADING = /^###\s+((?:FR|NFR)-\d+):\s*(.+?)\s*$/;
|
|
10
|
+
const DECISION_HEADING = /^###\s+(D-\d+):\s*(.+?)\s*$/;
|
|
11
|
+
function clip(value, limit, counts) {
|
|
12
|
+
const result = redactThenClip(value, limit);
|
|
13
|
+
counts.n += result.count;
|
|
14
|
+
return result.text;
|
|
15
|
+
}
|
|
16
|
+
const sectionsOf = (content, heading) => uniqueSections(parseSections(visibleLines(content), heading).sections);
|
|
17
|
+
/** Canonical, order-sensitive hash of the caller's whole case list. */
|
|
18
|
+
export function hashCaseList(cases) {
|
|
19
|
+
const canonical = cases.map((entry) => [entry.taskId, entry.kind, entry.given, entry.when, entry.then, entry.covers]);
|
|
20
|
+
return createHash('sha256').update(JSON.stringify(canonical)).digest('hex');
|
|
21
|
+
}
|
|
22
|
+
const clipCase = (entry, counts) => ({
|
|
23
|
+
kind: entry.kind,
|
|
24
|
+
given: clip(entry.given, CASE_FIELD_LIMIT, counts),
|
|
25
|
+
when: clip(entry.when, CASE_FIELD_LIMIT, counts),
|
|
26
|
+
then: clip(entry.then, CASE_FIELD_LIMIT, counts),
|
|
27
|
+
covers: clip(entry.covers, CASE_FIELD_LIMIT, counts),
|
|
28
|
+
});
|
|
29
|
+
export function buildTestcaseExchange(snapshot, cases) {
|
|
30
|
+
const counts = { n: 0 };
|
|
31
|
+
const bound = (value, limit = FIELD_LIMIT) => clip(value, limit, counts);
|
|
32
|
+
const requirements = new Map(sectionsOf(snapshot.requirementsContent ?? '', REQUIREMENT_HEADING)
|
|
33
|
+
.map((section) => [section.id, bound(metadata(section, 'EARS Specification'))]));
|
|
34
|
+
const decisions = new Map(sectionsOf(snapshot.designContent ?? '', DECISION_HEADING)
|
|
35
|
+
.map((section) => [section.id, bound(metadata(section, 'Decision'), 600)]));
|
|
36
|
+
const tasks = sectionsOf(snapshot.content, TASK_HEADING).map((section) => {
|
|
37
|
+
const tdd = metadata(section, 'TDD') ?? '';
|
|
38
|
+
const covers = parseList(metadata(section, 'Covers'));
|
|
39
|
+
return {
|
|
40
|
+
section,
|
|
41
|
+
notApplicable: tdd.startsWith('not-applicable'),
|
|
42
|
+
reason: bound(tdd.replace(/^not-applicable\s*—\s*/, ''), 500),
|
|
43
|
+
covers,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
if (tasks.length === 0)
|
|
47
|
+
return undefined;
|
|
48
|
+
const items = tasks.map(({ section, notApplicable, reason, covers }) => {
|
|
49
|
+
const base = { id: section.id, title: bound(section.title, 300), acceptance: bound(metadata(section, 'Acceptance criteria')) };
|
|
50
|
+
if (notApplicable)
|
|
51
|
+
return { ...base, tddNotApplicable: true, reason };
|
|
52
|
+
return {
|
|
53
|
+
...base,
|
|
54
|
+
covers,
|
|
55
|
+
coveredRequirements: covers.flatMap((id) => (requirements.has(id) ? [{ id, ears: requirements.get(id) }] : [])),
|
|
56
|
+
coveredDecisions: covers.flatMap((id) => (decisions.has(id) ? [{ id, decision: decisions.get(id) }] : [])),
|
|
57
|
+
cases: cases.filter((entry) => entry.taskId === section.id).map((entry) => clipCase(entry, counts)),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
const fit = fitState('testcases', items, undefined);
|
|
61
|
+
if (fit.keptIds.length === 0)
|
|
62
|
+
return undefined;
|
|
63
|
+
const kept = tasks.filter(({ section }) => fit.keptIds.includes(section.id));
|
|
64
|
+
const tddIds = new Set(tasks.filter(({ notApplicable }) => !notApplicable).map(({ section }) => section.id));
|
|
65
|
+
const caseTaskIds = [...new Set(cases.map((entry) => entry.taskId))].sort();
|
|
66
|
+
const unknownTasks = caseTaskIds.filter((id) => !tddIds.has(id));
|
|
67
|
+
const truncatedTasks = caseTaskIds.filter((id) => tddIds.has(id) && fit.omittedIds.includes(id));
|
|
68
|
+
const keptTasks = kept.map(({ section, notApplicable }) => ({ id: section.id, notApplicable }));
|
|
69
|
+
return {
|
|
70
|
+
state: fit.state,
|
|
71
|
+
questions: buildTestcaseQuestions(keptTasks),
|
|
72
|
+
itemIds: fit.keptIds,
|
|
73
|
+
secondaryIds: [],
|
|
74
|
+
omittedIds: fit.omittedIds,
|
|
75
|
+
truncated: fit.truncated,
|
|
76
|
+
redactions: fit.redactions + counts.n,
|
|
77
|
+
tasks: keptTasks,
|
|
78
|
+
unknownTasks,
|
|
79
|
+
truncatedTasks,
|
|
80
|
+
matchedCases: cases.filter((entry) => tddIds.has(entry.taskId)).length,
|
|
81
|
+
tddTaskCount: tddIds.size,
|
|
82
|
+
caseListSha256: hashCaseList(cases),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=JevTestcaseState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JevTestcaseState.js","sourceRoot":"","sources":["../../../src/application/services/JevTestcaseState.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type RedClassification = 'setup' | 'assertion' | 'ambiguous';
|
|
2
|
+
export interface RedClassificationResult {
|
|
3
|
+
classification: RedClassification;
|
|
4
|
+
matchedPattern?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Pure, free classification of RED evidence. Setup wins when both kinds match,
|
|
8
|
+
* because a broken setup cannot prove missing behavior.
|
|
9
|
+
*/
|
|
10
|
+
export declare function classifyRedEvidence(command: string, summary: string): RedClassificationResult;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const SETUP_PATTERNS = [
|
|
2
|
+
{ name: 'module-not-found', regex: /Cannot find module|Module not found|ERR_MODULE_NOT_FOUND/ },
|
|
3
|
+
{ name: 'syntax-error', regex: /SyntaxError/ },
|
|
4
|
+
{ name: 'typescript', regex: /\bTS\d{4}\b/ },
|
|
5
|
+
{ name: 'enoent', regex: /\bENOENT\b/ },
|
|
6
|
+
{ name: 'reference-error', regex: /ReferenceError|\bis not defined\b/ },
|
|
7
|
+
{ name: 'command-not-found', regex: /command not found/ },
|
|
8
|
+
];
|
|
9
|
+
const ASSERTION_PATTERNS = [
|
|
10
|
+
{ name: 'assertion-error', regex: /AssertionError/ },
|
|
11
|
+
{ name: 'expected-received', regex: /Expected:[\s\S]*Received:/ },
|
|
12
|
+
{ name: 'expect-call', regex: /\bexpect\(/ },
|
|
13
|
+
{ name: 'assert-fail', regex: /\bassert(?:ion)?\b.*fail/i },
|
|
14
|
+
];
|
|
15
|
+
function firstMatch(patterns, text) {
|
|
16
|
+
return patterns.find(({ regex }) => regex.test(text))?.name;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Pure, free classification of RED evidence. Setup wins when both kinds match,
|
|
20
|
+
* because a broken setup cannot prove missing behavior.
|
|
21
|
+
*/
|
|
22
|
+
export function classifyRedEvidence(command, summary) {
|
|
23
|
+
const text = `${command}\n${summary}`;
|
|
24
|
+
const setup = firstMatch(SETUP_PATTERNS, text);
|
|
25
|
+
if (setup)
|
|
26
|
+
return { classification: 'setup', matchedPattern: setup };
|
|
27
|
+
const assertion = firstMatch(ASSERTION_PATTERNS, text);
|
|
28
|
+
if (assertion)
|
|
29
|
+
return { classification: 'assertion', matchedPattern: assertion };
|
|
30
|
+
return { classification: 'ambiguous' };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=RedEvidenceClassifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RedEvidenceClassifier.js","sourceRoot":"","sources":["../../../src/application/services/RedEvidenceClassifier.ts"],"names":[],"mappings":"AAYA,MAAM,cAAc,GAA4B;IAC9C,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,0DAA0D,EAAE;IAC/F,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE;IAC9C,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE;IAC5C,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE;IACvC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,mCAAmC,EAAE;IACvE,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,EAAE;CAC1D,CAAC;AAEF,MAAM,kBAAkB,GAA4B;IAClD,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,gBAAgB,EAAE;IACpD,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,2BAA2B,EAAE;IACjE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE;IAC5C,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,2BAA2B,EAAE;CAC5D,CAAC;AAEF,SAAS,UAAU,CAAC,QAAiC,EAAE,IAAY;IACjE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC;AAC9D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,OAAe;IAClE,MAAM,IAAI,GAAG,GAAG,OAAO,KAAK,OAAO,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAC/C,IAAI,KAAK;QAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;IACvD,IAAI,SAAS;QAAE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;IACjF,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -121,6 +121,27 @@ export interface DurableFeatureStatus {
|
|
|
121
121
|
blocked: number;
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
|
+
/** Read-only view for advisory checks; carries no handle that can mutate workflow state. */
|
|
125
|
+
export interface AdvisorySnapshot {
|
|
126
|
+
readonly featureName: string;
|
|
127
|
+
readonly description: string;
|
|
128
|
+
readonly phase: ApprovablePhase;
|
|
129
|
+
readonly revision: number;
|
|
130
|
+
readonly recordedSha256?: string;
|
|
131
|
+
readonly observedSha256?: string;
|
|
132
|
+
readonly validationStatus: PhaseRecord['validation']['status'];
|
|
133
|
+
readonly content: string;
|
|
134
|
+
readonly requirementsContent?: string;
|
|
135
|
+
readonly designContent?: string;
|
|
136
|
+
readonly implementation?: {
|
|
137
|
+
readonly revision: number;
|
|
138
|
+
readonly tasks: Readonly<Record<string, {
|
|
139
|
+
readonly status: TaskStatus;
|
|
140
|
+
readonly tddRequired: boolean;
|
|
141
|
+
readonly acceptanceCriteria: string;
|
|
142
|
+
}>>;
|
|
143
|
+
};
|
|
144
|
+
}
|
|
124
145
|
export interface RecordTaskProgressRequest extends FeatureRequest {
|
|
125
146
|
readonly taskNumber: string;
|
|
126
147
|
readonly action: 'start' | 'record-red' | 'record-green' | 'complete' | 'block';
|
|
@@ -171,6 +192,13 @@ export declare class WorkflowEngineService {
|
|
|
171
192
|
listFeatureStatuses(request: {
|
|
172
193
|
projectRoot: string;
|
|
173
194
|
}): Promise<DurableFeatureStatus[]>;
|
|
195
|
+
/**
|
|
196
|
+
* Read-only view for advisory checks. Never commits state, publishes a handoff, or creates
|
|
197
|
+
* files for an unknown feature (the lock helper would create the feature directory).
|
|
198
|
+
*/
|
|
199
|
+
readAdvisorySnapshot(request: FeatureRequest & {
|
|
200
|
+
readonly phase: ApprovablePhase;
|
|
201
|
+
}): Promise<AdvisorySnapshot>;
|
|
174
202
|
loadProject(request: FeatureRequest): Promise<Project>;
|
|
175
203
|
validateDesignArtifact(request: FeatureRequest): Promise<DesignValidationResult>;
|
|
176
204
|
private withLockedFeature;
|