specky-sdd 3.10.2 → 3.11.1
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/.apm/agents/specky-design-architect.agent.md +8 -28
- package/.apm/agents/specky-implementer.agent.md +15 -27
- package/.apm/agents/specky-onboarding.agent.md +8 -49
- package/.apm/agents/specky-orchestrator.agent.md +12 -37
- package/.apm/agents/specky-quality-reviewer.agent.md +9 -30
- package/.apm/agents/specky-release-engineer.agent.md +8 -30
- package/.apm/agents/specky-requirements-engineer.agent.md +1 -1
- package/.apm/agents/specky-research-analyst.agent.md +1 -1
- package/.apm/agents/specky-sdd-clarify.agent.md +1 -1
- package/.apm/agents/specky-sdd-init.agent.md +8 -19
- package/.apm/agents/specky-spec-engineer.agent.md +8 -30
- package/.apm/agents/specky-task-planner.agent.md +7 -26
- package/.apm/agents/specky-test-verifier.agent.md +7 -17
- package/.apm/hooks/scripts/specky-artifact-validator.sh +14 -10
- package/.apm/hooks/scripts/specky-auto-checkpoint.sh +9 -4
- package/.apm/hooks/scripts/specky-branch-validator.sh +29 -116
- package/.apm/hooks/scripts/specky-cognitive-debt-alert.sh +6 -3
- package/.apm/hooks/scripts/specky-contract-context.bash +24 -0
- package/.apm/hooks/scripts/specky-contract-context.mjs +219 -0
- package/.apm/hooks/scripts/specky-drift-monitor.sh +6 -3
- package/.apm/hooks/scripts/specky-ears-validator.sh +5 -2
- package/.apm/hooks/scripts/specky-lgtm-gate.sh +12 -10
- package/.apm/hooks/scripts/specky-metrics-dashboard.sh +5 -2
- package/.apm/hooks/scripts/specky-phase-gate.sh +18 -9
- package/.apm/hooks/scripts/specky-pipeline-guard.sh +19 -26
- package/.apm/hooks/scripts/specky-release-gate.sh +38 -46
- package/.apm/hooks/scripts/specky-security-scan.sh +53 -36
- package/.apm/hooks/scripts/specky-session-banner.sh +22 -74
- package/.apm/hooks/scripts/specky-spec-quality.sh +7 -4
- package/.apm/hooks/scripts/specky-spec-sync.sh +12 -11
- package/.apm/hooks/scripts/specky-task-tracer.sh +5 -2
- package/.apm/hooks/specky-run.sh +3 -0
- package/.apm/instructions/claude-instructions.instructions.md +3 -3
- package/.apm/instructions/copilot-instructions.instructions.md +3 -3
- package/.apm/instructions/cursor-instructions.instructions.md +3 -3
- package/.apm/instructions/opencode-instructions.instructions.md +2 -2
- package/.apm/prompts/specky-brownfield.prompt.md +8 -5
- package/.apm/prompts/specky-greenfield.prompt.md +7 -5
- package/.apm/prompts/specky-implement.prompt.md +1 -1
- package/.apm/prompts/specky-orchestrate.prompt.md +1 -1
- package/.apm/skills/specky-design-architect/SKILL.md +39 -16
- package/.apm/skills/specky-implementer/SKILL.md +46 -158
- package/.apm/skills/specky-onboarding/SKILL.md +50 -156
- package/.apm/skills/specky-orchestrator/SKILL.md +28 -26
- package/.apm/skills/specky-quality-reviewer/SKILL.md +38 -23
- package/.apm/skills/specky-release-engineer/SKILL.md +38 -270
- package/.apm/skills/specky-research-analyst/SKILL.md +1 -1
- package/.apm/skills/specky-sdd-init/SKILL.md +61 -23
- package/.apm/skills/specky-sdd-pipeline/SKILL.md +36 -216
- package/.apm/skills/specky-spec-engineer/SKILL.md +40 -17
- package/.apm/skills/specky-task-planner/SKILL.md +31 -12
- package/.apm/skills/specky-test-verifier/SKILL.md +23 -202
- package/CHANGELOG.md +39 -0
- package/README.md +49 -47
- package/SECURITY.md +5 -1
- package/apm-policy.yml +2 -0
- package/apm.lock.yaml +55 -53
- package/apm.yml +3 -3
- package/config.yml +32 -53
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +106 -20
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +105 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrate-contracts.d.ts +15 -0
- package/dist/cli/commands/migrate-contracts.d.ts.map +1 -0
- package/dist/cli/commands/migrate-contracts.js +311 -0
- package/dist/cli/commands/migrate-contracts.js.map +1 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +63 -48
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/index.js +82 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lib/apm/policy.d.ts +1 -0
- package/dist/cli/lib/apm/policy.d.ts.map +1 -1
- package/dist/cli/lib/apm/policy.js +15 -0
- package/dist/cli/lib/apm/policy.js.map +1 -1
- package/dist/cli/lib/asset-copier.d.ts +1 -0
- package/dist/cli/lib/asset-copier.d.ts.map +1 -1
- package/dist/cli/lib/asset-copier.js +4 -4
- package/dist/cli/lib/asset-copier.js.map +1 -1
- package/dist/cli/lib/cursor-plugin-writer.d.ts.map +1 -1
- package/dist/cli/lib/cursor-plugin-writer.js +34 -9
- package/dist/cli/lib/cursor-plugin-writer.js.map +1 -1
- package/dist/cli/lib/harness/compilers/claude.js +3 -3
- package/dist/cli/lib/harness/compilers/claude.js.map +1 -1
- package/dist/cli/lib/harness/compilers/common.d.ts +10 -1
- package/dist/cli/lib/harness/compilers/common.d.ts.map +1 -1
- package/dist/cli/lib/harness/compilers/common.js +71 -8
- package/dist/cli/lib/harness/compilers/common.js.map +1 -1
- package/dist/cli/lib/harness/compilers/copilot.js +3 -3
- package/dist/cli/lib/harness/compilers/copilot.js.map +1 -1
- package/dist/cli/lib/harness/compilers/cursor.js +3 -3
- package/dist/cli/lib/harness/compilers/cursor.js.map +1 -1
- package/dist/cli/lib/harness/compilers/opencode.js +3 -3
- package/dist/cli/lib/harness/compilers/opencode.js.map +1 -1
- package/dist/cli/lib/harness/tool-map.d.ts +8 -1
- package/dist/cli/lib/harness/tool-map.d.ts.map +1 -1
- package/dist/cli/lib/harness/tool-map.js +74 -0
- package/dist/cli/lib/harness/tool-map.js.map +1 -1
- package/dist/cli/lib/harness/types.d.ts +11 -1
- package/dist/cli/lib/harness/types.d.ts.map +1 -1
- package/dist/cli/lib/mcp-writer.d.ts +2 -1
- package/dist/cli/lib/mcp-writer.d.ts.map +1 -1
- package/dist/cli/lib/mcp-writer.js +13 -5
- package/dist/cli/lib/mcp-writer.js.map +1 -1
- package/dist/cli/lib/settings-merger.d.ts +15 -0
- package/dist/cli/lib/settings-merger.d.ts.map +1 -1
- package/dist/cli/lib/settings-merger.js +66 -1
- package/dist/cli/lib/settings-merger.js.map +1 -1
- package/dist/config.d.ts +110 -42
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +129 -140
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/contracts/pipeline-profiles.d.ts +61 -0
- package/dist/contracts/pipeline-profiles.d.ts.map +1 -0
- package/dist/contracts/pipeline-profiles.js +303 -0
- package/dist/contracts/pipeline-profiles.js.map +1 -0
- package/dist/contracts/tool-contracts.d.ts +449 -0
- package/dist/contracts/tool-contracts.d.ts.map +1 -0
- package/dist/contracts/tool-contracts.js +83 -0
- package/dist/contracts/tool-contracts.js.map +1 -0
- package/dist/contracts/use-case.d.ts +736 -0
- package/dist/contracts/use-case.d.ts.map +1 -0
- package/dist/contracts/use-case.js +352 -0
- package/dist/contracts/use-case.js.map +1 -0
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/schemas/common.d.ts +3 -3
- package/dist/schemas/common.d.ts.map +1 -1
- package/dist/schemas/common.js +1 -4
- package/dist/schemas/common.js.map +1 -1
- package/dist/schemas/context.d.ts +2 -2
- package/dist/schemas/environment.d.ts +6 -17
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js +1 -28
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/feature-content.d.ts +116 -0
- package/dist/schemas/feature-content.d.ts.map +1 -0
- package/dist/schemas/feature-content.js +65 -0
- package/dist/schemas/feature-content.js.map +1 -0
- package/dist/schemas/infrastructure.d.ts +6 -28
- package/dist/schemas/infrastructure.d.ts.map +1 -1
- package/dist/schemas/infrastructure.js +1 -30
- package/dist/schemas/infrastructure.js.map +1 -1
- package/dist/schemas/input.d.ts +412 -8
- package/dist/schemas/input.d.ts.map +1 -1
- package/dist/schemas/input.js +6 -7
- package/dist/schemas/input.js.map +1 -1
- package/dist/schemas/integration.d.ts +22 -28
- package/dist/schemas/integration.d.ts.map +1 -1
- package/dist/schemas/integration.js +11 -47
- package/dist/schemas/integration.js.map +1 -1
- package/dist/schemas/metrics.d.ts +3 -2
- package/dist/schemas/metrics.d.ts.map +1 -1
- package/dist/schemas/metrics.js +2 -1
- package/dist/schemas/metrics.js.map +1 -1
- package/dist/schemas/pbt.d.ts +2 -7
- package/dist/schemas/pbt.d.ts.map +1 -1
- package/dist/schemas/pbt.js +1 -9
- package/dist/schemas/pbt.js.map +1 -1
- package/dist/schemas/pipeline.d.ts +282 -35
- package/dist/schemas/pipeline.d.ts.map +1 -1
- package/dist/schemas/pipeline.js +29 -15
- package/dist/schemas/pipeline.js.map +1 -1
- package/dist/schemas/quality.d.ts +15 -19
- package/dist/schemas/quality.d.ts.map +1 -1
- package/dist/schemas/quality.js +6 -3
- package/dist/schemas/quality.js.map +1 -1
- package/dist/schemas/routing.d.ts +2 -2
- package/dist/schemas/routing.d.ts.map +1 -1
- package/dist/schemas/routing.js +2 -4
- package/dist/schemas/routing.js.map +1 -1
- package/dist/schemas/testing.d.ts +4 -13
- package/dist/schemas/testing.d.ts.map +1 -1
- package/dist/schemas/testing.js +2 -10
- package/dist/schemas/testing.js.map +1 -1
- package/dist/schemas/transcript.d.ts +648 -10
- package/dist/schemas/transcript.d.ts.map +1 -1
- package/dist/schemas/transcript.js +25 -12
- package/dist/schemas/transcript.js.map +1 -1
- package/dist/schemas/utility.d.ts +28 -15
- package/dist/schemas/utility.d.ts.map +1 -1
- package/dist/schemas/utility.js +23 -14
- package/dist/schemas/utility.js.map +1 -1
- package/dist/schemas/visualization.d.ts +66 -15
- package/dist/schemas/visualization.d.ts.map +1 -1
- package/dist/schemas/visualization.js +36 -12
- package/dist/schemas/visualization.js.map +1 -1
- package/dist/services/codebase-scanner.d.ts +1 -1
- package/dist/services/codebase-scanner.d.ts.map +1 -1
- package/dist/services/codebase-scanner.js +2 -2
- package/dist/services/codebase-scanner.js.map +1 -1
- package/dist/services/compliance-engine.d.ts +3 -2
- package/dist/services/compliance-engine.d.ts.map +1 -1
- package/dist/services/compliance-engine.js +34 -21
- package/dist/services/compliance-engine.js.map +1 -1
- package/dist/services/diagram-generator.d.ts +4 -96
- package/dist/services/diagram-generator.d.ts.map +1 -1
- package/dist/services/diagram-generator.js +18 -1051
- package/dist/services/diagram-generator.js.map +1 -1
- package/dist/services/doc-generator.d.ts +19 -52
- package/dist/services/doc-generator.d.ts.map +1 -1
- package/dist/services/doc-generator.js +389 -524
- package/dist/services/doc-generator.js.map +1 -1
- package/dist/services/execution-context.d.ts +32 -0
- package/dist/services/execution-context.d.ts.map +1 -0
- package/dist/services/execution-context.js +120 -0
- package/dist/services/execution-context.js.map +1 -0
- package/dist/services/file-manager.d.ts +16 -4
- package/dist/services/file-manager.d.ts.map +1 -1
- package/dist/services/file-manager.js +81 -5
- package/dist/services/file-manager.js.map +1 -1
- package/dist/services/iac-generator.d.ts +9 -10
- package/dist/services/iac-generator.d.ts.map +1 -1
- package/dist/services/iac-generator.js +36 -99
- package/dist/services/iac-generator.js.map +1 -1
- package/dist/services/methodology.js +1 -1
- package/dist/services/methodology.js.map +1 -1
- package/dist/services/metrics-generator.d.ts +1 -1
- package/dist/services/metrics-generator.d.ts.map +1 -1
- package/dist/services/metrics-generator.js +2 -2
- package/dist/services/metrics-generator.js.map +1 -1
- package/dist/services/pbt-generator.d.ts +10 -31
- package/dist/services/pbt-generator.d.ts.map +1 -1
- package/dist/services/pbt-generator.js +43 -515
- package/dist/services/pbt-generator.js.map +1 -1
- package/dist/services/state-machine.d.ts +22 -16
- package/dist/services/state-machine.d.ts.map +1 -1
- package/dist/services/state-machine.js +176 -179
- package/dist/services/state-machine.js.map +1 -1
- package/dist/services/template-engine.d.ts +11 -5
- package/dist/services/template-engine.d.ts.map +1 -1
- package/dist/services/template-engine.js +48 -21
- package/dist/services/template-engine.js.map +1 -1
- package/dist/services/test-generator.d.ts +11 -7
- package/dist/services/test-generator.d.ts.map +1 -1
- package/dist/services/test-generator.js +45 -54
- package/dist/services/test-generator.js.map +1 -1
- package/dist/templates/design.md +6 -0
- package/dist/tools/analysis.d.ts.map +1 -1
- package/dist/tools/analysis.js +8 -9
- package/dist/tools/analysis.js.map +1 -1
- package/dist/tools/checkpoint.d.ts.map +1 -1
- package/dist/tools/checkpoint.js +43 -46
- package/dist/tools/checkpoint.js.map +1 -1
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +5 -7
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/documentation.d.ts +1 -4
- package/dist/tools/documentation.d.ts.map +1 -1
- package/dist/tools/documentation.js +112 -193
- package/dist/tools/documentation.js.map +1 -1
- package/dist/tools/environment.d.ts +1 -1
- package/dist/tools/environment.d.ts.map +1 -1
- package/dist/tools/environment.js +58 -99
- package/dist/tools/environment.js.map +1 -1
- package/dist/tools/infrastructure.d.ts.map +1 -1
- package/dist/tools/infrastructure.js +36 -38
- package/dist/tools/infrastructure.js.map +1 -1
- package/dist/tools/input.d.ts.map +1 -1
- package/dist/tools/input.js +18 -11
- package/dist/tools/input.js.map +1 -1
- package/dist/tools/integration.d.ts.map +1 -1
- package/dist/tools/integration.js +64 -65
- package/dist/tools/integration.js.map +1 -1
- package/dist/tools/metrics.d.ts.map +1 -1
- package/dist/tools/metrics.js +18 -30
- package/dist/tools/metrics.js.map +1 -1
- package/dist/tools/pbt.d.ts.map +1 -1
- package/dist/tools/pbt.js +10 -12
- package/dist/tools/pbt.js.map +1 -1
- package/dist/tools/pipeline.d.ts.map +1 -1
- package/dist/tools/pipeline.js +156 -274
- package/dist/tools/pipeline.js.map +1 -1
- package/dist/tools/quality.d.ts.map +1 -1
- package/dist/tools/quality.js +58 -45
- package/dist/tools/quality.js.map +1 -1
- package/dist/tools/response-builder.d.ts.map +1 -1
- package/dist/tools/response-builder.js +12 -10
- package/dist/tools/response-builder.js.map +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/tools/testing.js +28 -29
- package/dist/tools/testing.js.map +1 -1
- package/dist/tools/tool-enforcement.d.ts +2 -0
- package/dist/tools/tool-enforcement.d.ts.map +1 -1
- package/dist/tools/tool-enforcement.js +84 -59
- package/dist/tools/tool-enforcement.js.map +1 -1
- package/dist/tools/transcript.d.ts.map +1 -1
- package/dist/tools/transcript.js +304 -374
- package/dist/tools/transcript.js.map +1 -1
- package/dist/tools/turnkey.d.ts +1 -31
- package/dist/tools/turnkey.d.ts.map +1 -1
- package/dist/tools/turnkey.js +104 -488
- package/dist/tools/turnkey.js.map +1 -1
- package/dist/tools/utility.d.ts.map +1 -1
- package/dist/tools/utility.js +115 -134
- package/dist/tools/utility.js.map +1 -1
- package/dist/tools/visualization.d.ts +1 -25
- package/dist/tools/visualization.d.ts.map +1 -1
- package/dist/tools/visualization.js +187 -562
- package/dist/tools/visualization.js.map +1 -1
- package/dist/types.d.ts +21 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/artifact-metadata.d.ts +8 -0
- package/dist/utils/artifact-metadata.d.ts.map +1 -0
- package/dist/utils/artifact-metadata.js +10 -0
- package/dist/utils/artifact-metadata.js.map +1 -0
- package/dist/utils/requirement-partition.d.ts +8 -0
- package/dist/utils/requirement-partition.d.ts.map +1 -0
- package/dist/utils/requirement-partition.js +15 -0
- package/dist/utils/requirement-partition.js.map +1 -0
- package/package.json +11 -2
- package/templates/design.md +6 -0
- package/dist/utils/design-stubs.d.ts +0 -32
- package/dist/utils/design-stubs.d.ts.map +0 -1
- package/dist/utils/design-stubs.js +0 -103
- package/dist/utils/design-stubs.js.map +0 -1
package/dist/tools/transcript.js
CHANGED
|
@@ -12,11 +12,70 @@ import { importTranscriptInputSchema, autoPipelineInputSchema, batchTranscriptsI
|
|
|
12
12
|
import { enrichResponse } from "./response-builder.js";
|
|
13
13
|
import { FeaturePackageGenerator } from "../services/feature-package-generator.js";
|
|
14
14
|
import { AnalysisEngine } from "../services/analysis-engine.js";
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
import { requireExecutionContext } from "../services/execution-context.js";
|
|
16
|
+
import { renderWorkloadDesign } from "../contracts/pipeline-profiles.js";
|
|
17
|
+
import { artifactMetadata } from "../utils/artifact-metadata.js";
|
|
18
|
+
function transcriptDiscoveryContext(analysis) {
|
|
19
|
+
const lines = [
|
|
20
|
+
"## Discovery Context",
|
|
21
|
+
"",
|
|
22
|
+
`- **Participants:** ${analysis.participants.join(", ") || "Not identified in source"}`,
|
|
23
|
+
`- **Topics:** ${analysis.topics.map((topic) => topic.name).join(", ") || "No named topics extracted"}`,
|
|
24
|
+
`- **Decisions captured:** ${analysis.decisions.length}`,
|
|
25
|
+
`- **Constraints captured:** ${analysis.constraints_mentioned.length}`,
|
|
26
|
+
`- **Open questions:** ${analysis.open_questions.length}`,
|
|
27
|
+
"",
|
|
28
|
+
"This context was extracted from the referenced transcript and must be reviewed against the source before approval.",
|
|
29
|
+
"",
|
|
30
|
+
"---",
|
|
31
|
+
"",
|
|
32
|
+
];
|
|
33
|
+
return lines.join("\n");
|
|
34
|
+
}
|
|
35
|
+
function validateExplicitFeatureContent(analysis, requirements, tasks, earsValidator) {
|
|
36
|
+
const source = analysis.full_text.toLowerCase();
|
|
37
|
+
const requirementIds = new Set();
|
|
38
|
+
for (const requirement of requirements) {
|
|
39
|
+
if (requirementIds.has(requirement.id))
|
|
40
|
+
throw new Error(`Duplicate requirement ID ${requirement.id}.`);
|
|
41
|
+
requirementIds.add(requirement.id);
|
|
42
|
+
if (!source.includes(requirement.source_quote.toLowerCase())) {
|
|
43
|
+
throw new Error(`Requirement ${requirement.id} source_quote is not present in the transcript.`);
|
|
44
|
+
}
|
|
45
|
+
const validation = earsValidator.validate(requirement.text);
|
|
46
|
+
if (!validation.valid) {
|
|
47
|
+
throw new Error(`Requirement ${requirement.id} is not valid EARS: ${(validation.issues ?? []).join("; ")}.`);
|
|
48
|
+
}
|
|
49
|
+
if (requirement.acceptance_criteria.length === 0) {
|
|
50
|
+
throw new Error(`Requirement ${requirement.id} requires acceptance criteria.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const taskIds = new Set(tasks.map((task) => task.id));
|
|
54
|
+
for (const task of tasks) {
|
|
55
|
+
const unknownRequirements = task.traces_to.filter((id) => !requirementIds.has(id));
|
|
56
|
+
const unknownDependencies = task.dependencies.filter((id) => !taskIds.has(id));
|
|
57
|
+
if (unknownRequirements.length > 0) {
|
|
58
|
+
throw new Error(`Task ${task.id} traces to unknown requirements: ${unknownRequirements.join(", ")}.`);
|
|
59
|
+
}
|
|
60
|
+
if (unknownDependencies.length > 0) {
|
|
61
|
+
throw new Error(`Task ${task.id} depends on unknown tasks: ${unknownDependencies.join(", ")}.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function replaceDesignEvidence(content, diagrams, adrs) {
|
|
66
|
+
const diagramContent = diagrams.map((diagram) => `### ${diagram.title}\n\n\`\`\`mermaid\n${diagram.code}\n\`\`\``).join("\n\n---\n\n");
|
|
67
|
+
const adrContent = adrs.map((adr, index) => [
|
|
68
|
+
`### ADR-${String(index + 1).padStart(3, "0")}: ${adr.title}`,
|
|
69
|
+
"",
|
|
70
|
+
`**Decision:** ${adr.decision}`,
|
|
71
|
+
"",
|
|
72
|
+
`**Rationale:** ${adr.rationale}`,
|
|
73
|
+
"",
|
|
74
|
+
`**Consequences:** ${adr.consequences}`,
|
|
75
|
+
].join("\n")).join("\n\n---\n\n");
|
|
76
|
+
return content
|
|
77
|
+
.replace(/## 5\. System Diagrams\n\n[\s\S]*?(?=\n---\n\n## 6)/, `## 5. System Diagrams\n\n${diagramContent}`)
|
|
78
|
+
.replace(/## 10\. Architecture Decision Records\n\n[\s\S]*?(?=\n---\n\n## 11)/, `## 10. Architecture Decision Records\n\n${adrContent}`);
|
|
20
79
|
}
|
|
21
80
|
export function registerTranscriptTools(server, fileManager, stateMachine, templateEngine, earsValidator, transcriptParser) {
|
|
22
81
|
const featurePackageGenerator = new FeaturePackageGenerator(fileManager);
|
|
@@ -97,7 +156,7 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
97
156
|
// ─── sdd_auto_pipeline ───
|
|
98
157
|
server.registerTool("sdd_auto_pipeline", {
|
|
99
158
|
title: "Auto Pipeline from Transcript",
|
|
100
|
-
description: "
|
|
159
|
+
description: "Validates explicit Constitution, source-quoted EARS requirements, complete architecture, tasks, and gates against one transcript before atomically orchestrating the full contracted artifact set.",
|
|
101
160
|
inputSchema: autoPipelineInputSchema,
|
|
102
161
|
annotations: {
|
|
103
162
|
readOnlyHint: false,
|
|
@@ -105,7 +164,7 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
105
164
|
idempotentHint: false,
|
|
106
165
|
openWorldHint: false,
|
|
107
166
|
},
|
|
108
|
-
}, async ({ file_path, raw_text, project_name, format, spec_dir,
|
|
167
|
+
}, async ({ file_path, raw_text, project_name, feature_number, constitution, requirements, architecture, tasks, pre_impl_gates, format, spec_dir, force }) => {
|
|
109
168
|
try {
|
|
110
169
|
// ── Step 1: Parse transcript ──
|
|
111
170
|
console.error(`[specky] Auto-pipeline: parsing transcript...`);
|
|
@@ -120,47 +179,60 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
120
179
|
else {
|
|
121
180
|
throw new Error("Provide either file_path or raw_text.\n→ Fix: Set file_path to transcript location, or paste text in raw_text.");
|
|
122
181
|
}
|
|
123
|
-
const featureDir = join(spec_dir,
|
|
182
|
+
const featureDir = join(spec_dir, `${feature_number}-${project_name}`);
|
|
183
|
+
const contract = requireExecutionContext("sdd_auto_pipeline").requestedContract;
|
|
184
|
+
if (contract.execution_mode !== "full") {
|
|
185
|
+
throw new Error(`sdd_auto_pipeline requires a full execution-mode contract; received ${contract.id}.`);
|
|
186
|
+
}
|
|
187
|
+
validateExplicitFeatureContent(analysis, requirements, tasks, earsValidator);
|
|
188
|
+
const workloadDesign = renderWorkloadDesign(contract.workload, architecture.workload_design);
|
|
189
|
+
if (contract.workload === "api" && architecture.api_contracts.length === 0) {
|
|
190
|
+
throw new Error("API transcript orchestration requires at least one explicit API contract.");
|
|
191
|
+
}
|
|
192
|
+
const existing = (await fileManager.listFeatures(spec_dir)).find((feature) => feature.number === feature_number);
|
|
193
|
+
if (existing)
|
|
194
|
+
throw new Error(`Feature number ${feature_number} is already assigned to ${existing.directory}.`);
|
|
124
195
|
const filesCreated = [];
|
|
125
196
|
// ── Step 2: Create CONSTITUTION.md ──
|
|
126
197
|
console.error(`[specky] Auto-pipeline: writing CONSTITUTION.md...`);
|
|
127
198
|
await fileManager.ensureSpecDir(spec_dir);
|
|
128
|
-
const autoConstraints = analysis.constraints_mentioned.length > 0
|
|
129
|
-
? analysis.constraints_mentioned
|
|
130
|
-
: ["Derived from meeting transcript — review for accuracy"];
|
|
131
|
-
const autoPrinciples = principles || [
|
|
132
|
-
"Requirements traced to meeting decisions",
|
|
133
|
-
"EARS notation for all requirements",
|
|
134
|
-
"Traceability from transcript to implementation",
|
|
135
|
-
];
|
|
136
199
|
const constitutionContent = await templateEngine.renderWithFrontmatter("constitution", {
|
|
200
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_auto_pipeline", status: "Active" }),
|
|
137
201
|
title: `${project_name} — Constitution`,
|
|
138
|
-
feature_id:
|
|
202
|
+
feature_id: `${feature_number}-${project_name}`,
|
|
139
203
|
project_name,
|
|
140
|
-
author:
|
|
141
|
-
principles:
|
|
142
|
-
constraints:
|
|
143
|
-
description:
|
|
144
|
-
license:
|
|
145
|
-
scope_in:
|
|
146
|
-
scope_out:
|
|
204
|
+
author: constitution.author,
|
|
205
|
+
principles: constitution.principles,
|
|
206
|
+
constraints: constitution.constraints,
|
|
207
|
+
description: constitution.description,
|
|
208
|
+
license: constitution.license,
|
|
209
|
+
scope_in: constitution.scope_in,
|
|
210
|
+
scope_out: constitution.scope_out,
|
|
147
211
|
});
|
|
148
212
|
await fileManager.writeSpecFile(featureDir, "CONSTITUTION.md", constitutionContent, force);
|
|
149
213
|
filesCreated.push("CONSTITUTION.md");
|
|
150
214
|
// Initialize state and advance through phases properly
|
|
151
|
-
const state = stateMachine.
|
|
152
|
-
|
|
215
|
+
const state = stateMachine.createFeatureState({
|
|
216
|
+
projectName: project_name,
|
|
217
|
+
feature: { number: feature_number, name: project_name, directory: featureDir },
|
|
218
|
+
contract,
|
|
219
|
+
});
|
|
153
220
|
state.phases[Phase.Init] = {
|
|
154
221
|
status: "completed",
|
|
155
222
|
started_at: new Date().toISOString(),
|
|
156
223
|
completed_at: new Date().toISOString(),
|
|
157
224
|
};
|
|
158
|
-
await stateMachine.saveState(
|
|
225
|
+
await stateMachine.saveState(featureDir, state);
|
|
159
226
|
// Advance Init → Discover
|
|
160
|
-
await stateMachine.advancePhase(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
227
|
+
await stateMachine.advancePhase(featureDir);
|
|
228
|
+
const earsRequirements = requirements.map((requirement) => ({
|
|
229
|
+
id: requirement.id,
|
|
230
|
+
title: requirement.title,
|
|
231
|
+
pattern: requirement.ears_pattern,
|
|
232
|
+
text: requirement.text,
|
|
233
|
+
acceptance_criteria: requirement.acceptance_criteria,
|
|
234
|
+
source: requirement.source_quote,
|
|
235
|
+
}));
|
|
164
236
|
// ── Step 4: Write SPECIFICATION.md ──
|
|
165
237
|
console.error(`[specky] Auto-pipeline: writing SPECIFICATION.md...`);
|
|
166
238
|
const reqSections = earsRequirements
|
|
@@ -184,9 +256,11 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
184
256
|
.map((req) => `| ${req.id} | ${req.text.slice(0, 60)}... | Acceptance test |`)
|
|
185
257
|
.join("\n");
|
|
186
258
|
const specContent = await templateEngine.renderWithFrontmatter("specification", {
|
|
259
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_auto_pipeline", status: "Draft" }),
|
|
187
260
|
title: `${project_name} — Specification`,
|
|
188
|
-
feature_id:
|
|
261
|
+
feature_id: `${feature_number}-${project_name}`,
|
|
189
262
|
project_name,
|
|
263
|
+
discovery_context: transcriptDiscoveryContext(analysis),
|
|
190
264
|
requirements_core: reqSections,
|
|
191
265
|
requirements_functional: "",
|
|
192
266
|
requirements_nonfunctional: "",
|
|
@@ -199,57 +273,66 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
199
273
|
await fileManager.writeSpecFile(featureDir, "SPECIFICATION.md", specContent, force);
|
|
200
274
|
filesCreated.push("SPECIFICATION.md");
|
|
201
275
|
// Advance Discover → Specify → Clarify
|
|
202
|
-
await stateMachine.recordPhaseComplete(
|
|
203
|
-
await stateMachine.advancePhase(
|
|
204
|
-
await stateMachine.recordPhaseComplete(
|
|
205
|
-
await stateMachine.advancePhase(
|
|
206
|
-
await stateMachine.recordPhaseComplete(
|
|
276
|
+
await stateMachine.recordPhaseComplete(featureDir, Phase.Discover);
|
|
277
|
+
await stateMachine.advancePhase(featureDir); // Discover → Specify
|
|
278
|
+
await stateMachine.recordPhaseComplete(featureDir, Phase.Specify);
|
|
279
|
+
await stateMachine.advancePhase(featureDir); // Specify → Clarify
|
|
280
|
+
await stateMachine.recordPhaseComplete(featureDir, Phase.Clarify);
|
|
207
281
|
// ── Step 5: Write DESIGN.md ──
|
|
208
282
|
console.error(`[specky] Auto-pipeline: writing DESIGN.md...`);
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
283
|
+
const apiContent = architecture.api_contracts.length > 0
|
|
284
|
+
? architecture.api_contracts.map((api) => [
|
|
285
|
+
`### ${api.method} ${api.endpoint}`,
|
|
286
|
+
"",
|
|
287
|
+
api.description,
|
|
288
|
+
"",
|
|
289
|
+
`**Request:** ${api.request}`,
|
|
290
|
+
"",
|
|
291
|
+
`**Response:** ${api.response}`,
|
|
292
|
+
].join("\n")).join("\n\n---\n\n")
|
|
293
|
+
: "No network API is exposed by this workload contract.";
|
|
294
|
+
let designContent = await templateEngine.renderWithFrontmatter("design", {
|
|
295
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_auto_pipeline", status: "Draft" }),
|
|
212
296
|
title: `${project_name} — Design`,
|
|
213
|
-
feature_id:
|
|
297
|
+
feature_id: `${feature_number}-${project_name}`,
|
|
214
298
|
project_name,
|
|
215
|
-
architecture_overview:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
api_contracts:
|
|
221
|
-
data_models:
|
|
222
|
-
error_handling:
|
|
223
|
-
component_design:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
299
|
+
architecture_overview: architecture.architecture_overview,
|
|
300
|
+
system_context: architecture.system_context,
|
|
301
|
+
container_architecture: architecture.container_architecture,
|
|
302
|
+
diagrams: architecture.mermaid_diagrams.map((diagram) => diagram.title),
|
|
303
|
+
adrs: architecture.adrs.map((adr) => adr.title),
|
|
304
|
+
api_contracts: apiContent,
|
|
305
|
+
data_models: architecture.data_models,
|
|
306
|
+
error_handling: architecture.error_handling,
|
|
307
|
+
component_design: architecture.component_design,
|
|
308
|
+
code_level_design: architecture.code_level_design,
|
|
309
|
+
security_architecture: architecture.security_architecture,
|
|
310
|
+
infrastructure: architecture.infrastructure,
|
|
311
|
+
cross_cutting: architecture.cross_cutting,
|
|
312
|
+
workload_design: workloadDesign,
|
|
313
|
+
requirement_references: requirements.map((requirement) => `- ${requirement.id}`).join("\n"),
|
|
228
314
|
});
|
|
315
|
+
designContent = replaceDesignEvidence(designContent, architecture.mermaid_diagrams, architecture.adrs);
|
|
229
316
|
await fileManager.writeSpecFile(featureDir, "DESIGN.md", designContent, force);
|
|
230
317
|
filesCreated.push("DESIGN.md");
|
|
231
318
|
// Advance Clarify → Design
|
|
232
|
-
await stateMachine.advancePhase(
|
|
233
|
-
await stateMachine.recordPhaseComplete(
|
|
319
|
+
await stateMachine.advancePhase(featureDir);
|
|
320
|
+
await stateMachine.recordPhaseComplete(featureDir, Phase.Design);
|
|
234
321
|
// ── Step 6: Write TASKS.md ──
|
|
235
322
|
console.error(`[specky] Auto-pipeline: writing TASKS.md...`);
|
|
236
|
-
const tasks = generateTasks(earsRequirements, analysis);
|
|
237
323
|
const taskTable = tasks
|
|
238
|
-
.map((t) => `| ${t.id} | ${t.title} | ${t.parallel ? "[P]" : ""} | ${t.effort} | ${t.
|
|
324
|
+
.map((t) => `| ${t.id} | ${t.title} | ${t.parallel ? "[P]" : ""} | ${t.effort} | ${t.dependencies.join(", ") || "—"} | ${t.traces_to.join(", ")} |`)
|
|
239
325
|
.join("\n");
|
|
240
|
-
const gates =
|
|
241
|
-
"**Gate 1:** CONSTITUTION.md approved — project charter and scope confirmed",
|
|
242
|
-
"**Gate 2:** SPECIFICATION.md reviewed — all EARS requirements have acceptance criteria",
|
|
243
|
-
"**Gate 3:** DESIGN.md reviewed — architecture and decisions documented",
|
|
244
|
-
];
|
|
326
|
+
const gates = pre_impl_gates.map((gate) => `**Gate ${gate.id}:** ${gate.check} (${gate.constitution_article})`);
|
|
245
327
|
const tasksContent = await templateEngine.renderWithFrontmatter("tasks", {
|
|
328
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_auto_pipeline", status: "Draft" }),
|
|
246
329
|
title: `${project_name} — Tasks`,
|
|
247
|
-
feature_id:
|
|
330
|
+
feature_id: `${feature_number}-${project_name}`,
|
|
248
331
|
project_name,
|
|
249
332
|
gates,
|
|
250
333
|
task_table: taskTable,
|
|
251
|
-
dependency_graph: tasks.map((t) => `${t.id}: ${t.title}`).join("\n"),
|
|
252
|
-
effort_summary: "",
|
|
334
|
+
dependency_graph: tasks.map((t) => `${t.id}: ${t.title} -> [${t.dependencies.join(", ")}]`).join("\n"),
|
|
335
|
+
effort_summary: tasks.map((t) => `${t.id}: ${t.effort}`).join("; "),
|
|
253
336
|
total_tasks: String(tasks.length),
|
|
254
337
|
parallel_tasks: String(tasks.filter((t) => t.parallel).length),
|
|
255
338
|
total_effort: `${tasks.length} tasks`,
|
|
@@ -257,8 +340,8 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
257
340
|
await fileManager.writeSpecFile(featureDir, "TASKS.md", tasksContent, force);
|
|
258
341
|
filesCreated.push("TASKS.md");
|
|
259
342
|
// Advance Design → Tasks
|
|
260
|
-
await stateMachine.advancePhase(
|
|
261
|
-
await stateMachine.recordPhaseComplete(
|
|
343
|
+
await stateMachine.advancePhase(featureDir);
|
|
344
|
+
await stateMachine.recordPhaseComplete(featureDir, Phase.Tasks);
|
|
262
345
|
// ── Step 7: Write ANALYSIS.md ──
|
|
263
346
|
// Real quality gate over the artifacts we just wrote — the SAME
|
|
264
347
|
// AnalysisEngine sdd_run_analysis and sdd_batch_transcripts use, with
|
|
@@ -274,9 +357,16 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
274
357
|
designContent,
|
|
275
358
|
tasksContent,
|
|
276
359
|
});
|
|
360
|
+
let reportedGaps = gate.gaps;
|
|
361
|
+
if (reportedGaps.length === 0) {
|
|
362
|
+
reportedGaps = analysis.open_questions.length > 0
|
|
363
|
+
? analysis.open_questions.map((question) => `Open question from meeting: ${question}`)
|
|
364
|
+
: ["No gaps — all requirements mapped through design and tasks"];
|
|
365
|
+
}
|
|
277
366
|
const analysisContent = await templateEngine.renderWithFrontmatter("analysis", {
|
|
367
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_auto_pipeline", status: gate.decision }),
|
|
278
368
|
title: `${project_name} — Analysis`,
|
|
279
|
-
feature_id:
|
|
369
|
+
feature_id: `${feature_number}-${project_name}`,
|
|
280
370
|
project_name,
|
|
281
371
|
gate_decision: gate.decision,
|
|
282
372
|
coverage_percent: String(gate.coveragePercent),
|
|
@@ -284,19 +374,12 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
284
374
|
design_coverage: `${gate.designCoverage}%`,
|
|
285
375
|
task_coverage: `${gate.taskCoverage}%`,
|
|
286
376
|
test_coverage: "Pending implementation",
|
|
287
|
-
gaps:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
"Review auto-generated EARS requirements for accuracy",
|
|
294
|
-
...gate.gaps.map((g) => `Remediate: ${g}`),
|
|
295
|
-
"Validate architecture decisions with team",
|
|
296
|
-
"Add detailed API contracts to DESIGN.md",
|
|
297
|
-
"Prioritize tasks based on team capacity",
|
|
298
|
-
...analysis.action_items.map((a) => `Action item: ${a}`),
|
|
299
|
-
],
|
|
377
|
+
gaps: reportedGaps,
|
|
378
|
+
recommendations: gate.gaps.length > 0
|
|
379
|
+
? gate.gaps.map((gap) => `Remediate: ${gap}`)
|
|
380
|
+
: analysis.action_items.length > 0
|
|
381
|
+
? analysis.action_items.map((action) => `Source action: ${action}`)
|
|
382
|
+
: ["No remediation is required by the current traceability analysis."],
|
|
300
383
|
ears_compliance: `${gate.earsCoverage}%`,
|
|
301
384
|
ears_status: gate.earsCoverage === 100 ? "✅" : "❌",
|
|
302
385
|
coverage_status: gate.coveragePercent >= 90 ? "✅" : "❌",
|
|
@@ -311,24 +394,24 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
311
394
|
filesCreated.push("TRANSCRIPT.md");
|
|
312
395
|
const featurePackage = await featurePackageGenerator.ensureFeaturePackage({
|
|
313
396
|
featureDir,
|
|
314
|
-
featureNumber:
|
|
397
|
+
featureNumber: feature_number,
|
|
315
398
|
featureName: project_name,
|
|
316
399
|
specContent,
|
|
317
400
|
sourceTool: "sdd_auto_pipeline",
|
|
318
401
|
});
|
|
319
402
|
filesCreated.push(...featurePackage.created);
|
|
320
403
|
// ── Step 9: Finalize state — advance Tasks → Analyze and set gate decision ──
|
|
321
|
-
await stateMachine.advancePhase(
|
|
322
|
-
await stateMachine.recordPhaseComplete(
|
|
404
|
+
await stateMachine.advancePhase(featureDir); // Tasks → Analyze
|
|
405
|
+
await stateMachine.recordPhaseComplete(featureDir, Phase.Analyze);
|
|
323
406
|
// Persist the COMPUTED gate decision — the state must carry the same
|
|
324
407
|
// engine verdict written into ANALYSIS.md, not an asserted APPROVE.
|
|
325
408
|
const gateDecidedAt = new Date().toISOString();
|
|
326
|
-
await stateMachine.mutateState(
|
|
409
|
+
await stateMachine.mutateState(featureDir, (state) => {
|
|
327
410
|
state.gate_decision = {
|
|
328
411
|
decision: gate.decision,
|
|
329
412
|
reasons: [
|
|
330
413
|
...gate.reasons,
|
|
331
|
-
`${earsRequirements.length} EARS requirements
|
|
414
|
+
`${earsRequirements.length} source-backed EARS requirements validated against the transcript.`,
|
|
332
415
|
`${analysis.decisions.length} decisions captured.`,
|
|
333
416
|
`${analysis.topics.length} topics covered.`,
|
|
334
417
|
],
|
|
@@ -350,7 +433,7 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
350
433
|
topics_extracted: analysis.topics.length,
|
|
351
434
|
decisions_captured: analysis.decisions.length,
|
|
352
435
|
action_items: analysis.action_items.length,
|
|
353
|
-
|
|
436
|
+
requirements_validated: earsRequirements.length,
|
|
354
437
|
open_questions: analysis.open_questions.length,
|
|
355
438
|
},
|
|
356
439
|
gate_decision: {
|
|
@@ -364,14 +447,14 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
364
447
|
...(gate.decision === "APPROVE"
|
|
365
448
|
? []
|
|
366
449
|
: [`0. Gate is ${gate.decision} (${gate.coveragePercent}% coverage) — remediate the gaps in ANALYSIS.md, then re-run sdd_run_analysis`]),
|
|
367
|
-
"1. Review SPECIFICATION.md
|
|
368
|
-
"2. Review DESIGN.md
|
|
369
|
-
"3. Review TASKS.md
|
|
450
|
+
"1. Review SPECIFICATION.md source quotes and acceptance criteria",
|
|
451
|
+
"2. Review DESIGN.md diagrams, ADRs, and workload-specific contract",
|
|
452
|
+
"3. Review TASKS.md dependency and requirement traceability",
|
|
370
453
|
"4. Review TRANSCRIPT.md — original meeting content preserved",
|
|
371
454
|
"5. Start implementation following TASKS.md breakdown",
|
|
372
455
|
].join("\n"),
|
|
373
456
|
};
|
|
374
|
-
const enriched = await enrichResponse("sdd_auto_pipeline", result, stateMachine,
|
|
457
|
+
const enriched = await enrichResponse("sdd_auto_pipeline", result, stateMachine, featureDir);
|
|
375
458
|
return {
|
|
376
459
|
content: [
|
|
377
460
|
{ type: "text", text: truncate(JSON.stringify(enriched, null, 2)) },
|
|
@@ -393,7 +476,7 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
393
476
|
// ─── sdd_batch_transcripts ───
|
|
394
477
|
server.registerTool("sdd_batch_transcripts", {
|
|
395
478
|
title: "Batch Process Transcript Folder",
|
|
396
|
-
description: "
|
|
479
|
+
description: "Validates a one-to-one explicit feature manifest for every transcript file, including source quotes, architecture, tasks, gates, and feature numbers, before writing batch feature packages.",
|
|
397
480
|
inputSchema: batchTranscriptsInputSchema,
|
|
398
481
|
annotations: {
|
|
399
482
|
readOnlyHint: false,
|
|
@@ -401,8 +484,12 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
401
484
|
idempotentHint: false,
|
|
402
485
|
openWorldHint: false,
|
|
403
486
|
},
|
|
404
|
-
}, async ({ transcripts_dir, spec_dir, force }) => {
|
|
487
|
+
}, async ({ transcripts_dir, spec_dir, features: featureInputs, force }) => {
|
|
405
488
|
try {
|
|
489
|
+
const contract = requireExecutionContext("sdd_batch_transcripts").requestedContract;
|
|
490
|
+
if (contract.execution_mode !== "full") {
|
|
491
|
+
throw new Error(`sdd_batch_transcripts requires a full execution-mode contract; received ${contract.id}.`);
|
|
492
|
+
}
|
|
406
493
|
console.error(`[specky] Batch: scanning ${transcripts_dir} for transcripts...`);
|
|
407
494
|
// Find all transcript files
|
|
408
495
|
const extensions = [".vtt", ".srt", ".txt", ".md"];
|
|
@@ -422,52 +509,67 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
422
509
|
],
|
|
423
510
|
};
|
|
424
511
|
}
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
512
|
+
const filesByName = new Map(files.map((filePath) => [basename(filePath), filePath]));
|
|
513
|
+
const configuredNames = new Set(featureInputs.map((feature) => feature.file_name));
|
|
514
|
+
const missingConfigurations = [...filesByName.keys()].filter((name) => !configuredNames.has(name));
|
|
515
|
+
const missingFiles = featureInputs
|
|
516
|
+
.map((feature) => feature.file_name)
|
|
517
|
+
.filter((name) => !filesByName.has(name));
|
|
518
|
+
if (missingConfigurations.length > 0 || missingFiles.length > 0) {
|
|
519
|
+
throw new Error(`Batch manifest mismatch. Unconfigured files: ${missingConfigurations.join(", ") || "none"}. ` +
|
|
520
|
+
`Missing files: ${missingFiles.join(", ") || "none"}.`);
|
|
521
|
+
}
|
|
429
522
|
const existingFeatures = await fileManager.listFeatures(spec_dir);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
523
|
+
const existingNumbers = new Set(existingFeatures.map((feature) => feature.number));
|
|
524
|
+
const configuredNumbers = featureInputs.map((feature) => feature.feature_number);
|
|
525
|
+
const duplicateNumbers = configuredNumbers.filter((number, index) => configuredNumbers.indexOf(number) !== index);
|
|
526
|
+
const collisions = configuredNumbers.filter((number) => existingNumbers.has(number));
|
|
527
|
+
if (duplicateNumbers.length > 0 || collisions.length > 0) {
|
|
528
|
+
throw new Error(`Batch feature number conflict. Duplicates: ${[...new Set(duplicateNumbers)].join(", ") || "none"}. ` +
|
|
529
|
+
`Existing: ${collisions.join(", ") || "none"}.`);
|
|
433
530
|
}
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
531
|
+
const prepared = await Promise.all(featureInputs.map(async (featureInput) => {
|
|
532
|
+
const filePath = filesByName.get(featureInput.file_name);
|
|
533
|
+
const analysis = await transcriptParser.parseFile(filePath);
|
|
534
|
+
validateExplicitFeatureContent(analysis, featureInput.requirements, featureInput.tasks, earsValidator);
|
|
535
|
+
const workloadDesign = renderWorkloadDesign(contract.workload, featureInput.architecture.workload_design);
|
|
536
|
+
if (contract.workload === "api" && featureInput.architecture.api_contracts.length === 0) {
|
|
537
|
+
throw new Error(`Batch feature ${featureInput.feature_number} requires an explicit API contract.`);
|
|
538
|
+
}
|
|
539
|
+
return { featureInput, filePath, analysis, workloadDesign };
|
|
540
|
+
}));
|
|
541
|
+
console.error(`[specky] Batch: validated ${prepared.length} transcript feature manifest(s). Processing...`);
|
|
542
|
+
const results = [];
|
|
543
|
+
for (const { featureInput, analysis, workloadDesign } of prepared) {
|
|
544
|
+
const fileName = featureInput.file_name;
|
|
545
|
+
const projectName = featureInput.project_name;
|
|
546
|
+
const featureNum = featureInput.feature_number;
|
|
444
547
|
const featureDir = join(spec_dir, `${featureNum}-${projectName}`);
|
|
445
548
|
try {
|
|
446
|
-
console.error(`[specky] Batch
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
549
|
+
console.error(`[specky] Batch feature ${featureNum}: processing "${fileName}"...`);
|
|
550
|
+
const earsRequirements = featureInput.requirements.map((requirement) => ({
|
|
551
|
+
id: requirement.id,
|
|
552
|
+
title: requirement.title,
|
|
553
|
+
pattern: requirement.ears_pattern,
|
|
554
|
+
text: requirement.text,
|
|
555
|
+
acceptance_criteria: requirement.acceptance_criteria,
|
|
556
|
+
source: requirement.source_quote,
|
|
557
|
+
}));
|
|
451
558
|
// ── Write all spec files ──
|
|
452
559
|
await fileManager.ensureSpecDir(spec_dir);
|
|
453
560
|
// CONSTITUTION.md
|
|
454
561
|
const constitutionContent = await templateEngine.renderWithFrontmatter("constitution", {
|
|
562
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_batch_transcripts", status: "Active" }),
|
|
455
563
|
title: `${projectName} — Constitution`,
|
|
456
564
|
feature_id: `${featureNum}-${projectName}`,
|
|
457
565
|
project_name: projectName,
|
|
458
|
-
author:
|
|
459
|
-
principles:
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
? analysis.constraints_mentioned
|
|
466
|
-
: ["Derived from meeting transcript"],
|
|
467
|
-
description: `Auto-generated from ${fileName}. ${analysis.topics.length} topics, ${analysis.participants.length} participants.`,
|
|
468
|
-
license: "MIT",
|
|
469
|
-
scope_in: analysis.topics.map((t) => t.name).join(", ") || "Meeting topics",
|
|
470
|
-
scope_out: "Items not discussed",
|
|
566
|
+
author: featureInput.constitution.author,
|
|
567
|
+
principles: featureInput.constitution.principles,
|
|
568
|
+
constraints: featureInput.constitution.constraints,
|
|
569
|
+
description: featureInput.constitution.description,
|
|
570
|
+
license: featureInput.constitution.license,
|
|
571
|
+
scope_in: featureInput.constitution.scope_in,
|
|
572
|
+
scope_out: featureInput.constitution.scope_out,
|
|
471
573
|
});
|
|
472
574
|
await fileManager.writeSpecFile(featureDir, "CONSTITUTION.md", constitutionContent, force);
|
|
473
575
|
// SPECIFICATION.md
|
|
@@ -479,9 +581,11 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
479
581
|
"", `**Source:** ${req.source}`, "", "---", "",
|
|
480
582
|
].join("\n")).join("\n");
|
|
481
583
|
const specContent = await templateEngine.renderWithFrontmatter("specification", {
|
|
584
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_batch_transcripts", status: "Draft" }),
|
|
482
585
|
title: `${projectName} — Specification`,
|
|
483
586
|
feature_id: `${featureNum}-${projectName}`,
|
|
484
587
|
project_name: projectName,
|
|
588
|
+
discovery_context: transcriptDiscoveryContext(analysis),
|
|
485
589
|
requirements_core: reqSections,
|
|
486
590
|
requirements_functional: "",
|
|
487
591
|
requirements_nonfunctional: "",
|
|
@@ -492,41 +596,53 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
492
596
|
uniqueness_score: `${earsRequirements.length}/${earsRequirements.length}`,
|
|
493
597
|
});
|
|
494
598
|
await fileManager.writeSpecFile(featureDir, "SPECIFICATION.md", specContent, force);
|
|
495
|
-
|
|
496
|
-
const
|
|
497
|
-
|
|
599
|
+
const architecture = featureInput.architecture;
|
|
600
|
+
const apiContent = architecture.api_contracts.length > 0
|
|
601
|
+
? architecture.api_contracts.map((api) => [
|
|
602
|
+
`### ${api.method} ${api.endpoint}`,
|
|
603
|
+
"",
|
|
604
|
+
api.description,
|
|
605
|
+
"",
|
|
606
|
+
`**Request:** ${api.request}`,
|
|
607
|
+
"",
|
|
608
|
+
`**Response:** ${api.response}`,
|
|
609
|
+
].join("\n")).join("\n\n---\n\n")
|
|
610
|
+
: "No network API is exposed by this workload contract.";
|
|
611
|
+
let designContent = await templateEngine.renderWithFrontmatter("design", {
|
|
612
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_batch_transcripts", status: "Draft" }),
|
|
498
613
|
title: `${projectName} — Design`,
|
|
499
614
|
feature_id: `${featureNum}-${projectName}`,
|
|
500
615
|
project_name: projectName,
|
|
501
|
-
architecture_overview:
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
api_contracts:
|
|
507
|
-
data_models:
|
|
508
|
-
error_handling:
|
|
509
|
-
component_design:
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
616
|
+
architecture_overview: architecture.architecture_overview,
|
|
617
|
+
system_context: architecture.system_context,
|
|
618
|
+
container_architecture: architecture.container_architecture,
|
|
619
|
+
diagrams: architecture.mermaid_diagrams.map((diagram) => diagram.title),
|
|
620
|
+
adrs: architecture.adrs.map((adr) => adr.title),
|
|
621
|
+
api_contracts: apiContent,
|
|
622
|
+
data_models: architecture.data_models,
|
|
623
|
+
error_handling: architecture.error_handling,
|
|
624
|
+
component_design: architecture.component_design,
|
|
625
|
+
code_level_design: architecture.code_level_design,
|
|
626
|
+
security_architecture: architecture.security_architecture,
|
|
627
|
+
infrastructure: architecture.infrastructure,
|
|
628
|
+
cross_cutting: architecture.cross_cutting,
|
|
629
|
+
workload_design: workloadDesign,
|
|
630
|
+
requirement_references: featureInput.requirements
|
|
631
|
+
.map((requirement) => `- ${requirement.id}`)
|
|
632
|
+
.join("\n"),
|
|
514
633
|
});
|
|
634
|
+
designContent = replaceDesignEvidence(designContent, architecture.mermaid_diagrams, architecture.adrs);
|
|
515
635
|
await fileManager.writeSpecFile(featureDir, "DESIGN.md", designContent, force);
|
|
516
|
-
|
|
517
|
-
const tasks = generateTasks(earsRequirements, analysis);
|
|
636
|
+
const tasks = featureInput.tasks;
|
|
518
637
|
const tasksContent = await templateEngine.renderWithFrontmatter("tasks", {
|
|
638
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_batch_transcripts", status: "Draft" }),
|
|
519
639
|
title: `${projectName} — Tasks`,
|
|
520
640
|
feature_id: `${featureNum}-${projectName}`,
|
|
521
641
|
project_name: projectName,
|
|
522
|
-
gates:
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
],
|
|
527
|
-
task_table: tasks.map((t) => `| ${t.id} | ${t.title} | ${t.parallel ? "[P]" : ""} | ${t.effort} | ${t.depends} | ${t.traces} |`).join("\n"),
|
|
528
|
-
dependency_graph: tasks.map((t) => `${t.id}: ${t.title}`).join("\n"),
|
|
529
|
-
effort_summary: "",
|
|
642
|
+
gates: featureInput.pre_impl_gates.map((gate) => `**Gate ${gate.id}:** ${gate.check} (${gate.constitution_article})`),
|
|
643
|
+
task_table: tasks.map((t) => `| ${t.id} | ${t.title} | ${t.parallel ? "[P]" : ""} | ${t.effort} | ${t.dependencies.join(", ") || "—"} | ${t.traces_to.join(", ")} |`).join("\n"),
|
|
644
|
+
dependency_graph: tasks.map((t) => `${t.id}: ${t.title} -> [${t.dependencies.join(", ")}]`).join("\n"),
|
|
645
|
+
effort_summary: tasks.map((t) => `${t.id}: ${t.effort}`).join("; "),
|
|
530
646
|
total_tasks: String(tasks.length),
|
|
531
647
|
parallel_tasks: String(tasks.filter((t) => t.parallel).length),
|
|
532
648
|
total_effort: `${tasks.length} tasks`,
|
|
@@ -546,6 +662,7 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
546
662
|
tasksContent,
|
|
547
663
|
});
|
|
548
664
|
const analysisContent = await templateEngine.renderWithFrontmatter("analysis", {
|
|
665
|
+
...artifactMetadata({ version: "1.0.0", author: "sdd_batch_transcripts", status: gate.decision }),
|
|
549
666
|
title: `${projectName} — Analysis`,
|
|
550
667
|
feature_id: `${featureNum}-${projectName}`,
|
|
551
668
|
project_name: projectName,
|
|
@@ -555,19 +672,17 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
555
672
|
design_coverage: `${gate.designCoverage}%`,
|
|
556
673
|
task_coverage: `${gate.taskCoverage}%`,
|
|
557
674
|
test_coverage: "Pending",
|
|
558
|
-
gaps: gate.gaps.
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
...analysis.action_items.slice(0, 3).map((a) => `Action: ${a}`),
|
|
565
|
-
],
|
|
675
|
+
gaps: batchAnalysisGaps(gate.gaps, analysis.open_questions),
|
|
676
|
+
recommendations: gate.gaps.length > 0
|
|
677
|
+
? gate.gaps.map((gap) => `Remediate: ${gap}`)
|
|
678
|
+
: analysis.action_items.length > 0
|
|
679
|
+
? analysis.action_items.map((action) => `Source action: ${action}`)
|
|
680
|
+
: ["No remediation is required by the current traceability analysis."],
|
|
566
681
|
ears_compliance: `${gate.earsCoverage}%`,
|
|
567
|
-
ears_status: gate.earsCoverage === 100
|
|
568
|
-
coverage_status: gate.coveragePercent >= 90
|
|
682
|
+
ears_status: evidenceStatus(gate.earsCoverage === 100),
|
|
683
|
+
coverage_status: evidenceStatus(gate.coveragePercent >= 90),
|
|
569
684
|
orphan_count: String(gate.orphanCount),
|
|
570
|
-
orphan_status: gate.orphanCount === 0
|
|
685
|
+
orphan_status: evidenceStatus(gate.orphanCount === 0),
|
|
571
686
|
});
|
|
572
687
|
await fileManager.writeSpecFile(featureDir, "ANALYSIS.md", analysisContent, force);
|
|
573
688
|
// TRANSCRIPT.md
|
|
@@ -580,9 +695,11 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
580
695
|
specContent,
|
|
581
696
|
sourceTool: "sdd_batch_transcripts",
|
|
582
697
|
});
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
698
|
+
const state = stateMachine.createFeatureState({
|
|
699
|
+
projectName,
|
|
700
|
+
feature: { number: featureNum, name: projectName, directory: featureDir },
|
|
701
|
+
contract,
|
|
702
|
+
});
|
|
586
703
|
state.current_phase = Phase.Analyze;
|
|
587
704
|
for (const phase of [Phase.Init, Phase.Discover, Phase.Specify, Phase.Clarify, Phase.Design, Phase.Tasks, Phase.Analyze]) {
|
|
588
705
|
state.phases[phase] = {
|
|
@@ -604,8 +721,7 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
604
721
|
gaps: gate.gaps,
|
|
605
722
|
decided_at: new Date().toISOString(),
|
|
606
723
|
};
|
|
607
|
-
|
|
608
|
-
await fileManager.writeSpecFile(featureDir, ".sdd-state.json", JSON.stringify(state, null, 2), true);
|
|
724
|
+
await stateMachine.saveState(featureDir, state);
|
|
609
725
|
results.push({
|
|
610
726
|
file: fileName,
|
|
611
727
|
project_name: projectName,
|
|
@@ -614,7 +730,6 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
614
730
|
requirements: earsRequirements.length,
|
|
615
731
|
package_artifacts: featurePackage.created.length,
|
|
616
732
|
});
|
|
617
|
-
featureSeq++;
|
|
618
733
|
}
|
|
619
734
|
catch (err) {
|
|
620
735
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -627,7 +742,6 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
627
742
|
requirements: 0,
|
|
628
743
|
error: errorMsg,
|
|
629
744
|
});
|
|
630
|
-
featureSeq++;
|
|
631
745
|
}
|
|
632
746
|
}
|
|
633
747
|
const succeeded = results.filter((r) => r.status === "completed");
|
|
@@ -644,17 +758,20 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
644
758
|
spec_directory: spec_dir,
|
|
645
759
|
next_action: [
|
|
646
760
|
`${succeeded.length} feature spec packages created in ${spec_dir}/`,
|
|
647
|
-
"Review each
|
|
761
|
+
"Review each source quote and artifact approval state.",
|
|
648
762
|
"Use sdd_get_status to check any individual feature.",
|
|
649
|
-
failed.length
|
|
650
|
-
? `${failed.length} transcript(s) failed — check error messages above.`
|
|
651
|
-
: "All transcripts processed successfully.",
|
|
763
|
+
batchCompletionMessage(failed.length),
|
|
652
764
|
].join("\n"),
|
|
653
765
|
};
|
|
654
|
-
const enriched = await enrichResponse("sdd_batch_transcripts", batchResult, stateMachine, spec_dir);
|
|
655
766
|
return {
|
|
656
767
|
content: [
|
|
657
|
-
{
|
|
768
|
+
{
|
|
769
|
+
type: "text", text: truncate(JSON.stringify({
|
|
770
|
+
...batchResult,
|
|
771
|
+
contract_id: contract.id,
|
|
772
|
+
contract_fingerprint: contract.fingerprint,
|
|
773
|
+
}, null, 2))
|
|
774
|
+
},
|
|
658
775
|
],
|
|
659
776
|
};
|
|
660
777
|
}
|
|
@@ -671,202 +788,15 @@ export function registerTranscriptTools(server, fileManager, stateMachine, templ
|
|
|
671
788
|
}
|
|
672
789
|
});
|
|
673
790
|
}
|
|
674
|
-
function
|
|
675
|
-
|
|
676
|
-
let seqCore = 1;
|
|
677
|
-
let seqFunc = 1;
|
|
678
|
-
let seqNfr = 1;
|
|
679
|
-
// Generate from extracted requirements
|
|
680
|
-
for (const rawReq of analysis.requirements_raw) {
|
|
681
|
-
const improvement = earsValidator.suggestImprovement(rawReq);
|
|
682
|
-
const id = `REQ-FUNC-${String(seqFunc++).padStart(3, "0")}`;
|
|
683
|
-
reqs.push({
|
|
684
|
-
id,
|
|
685
|
-
title: rawReq.slice(0, 60),
|
|
686
|
-
pattern: improvement.pattern,
|
|
687
|
-
text: improvement.suggestion.includes("[TODO")
|
|
688
|
-
? `The system shall ${rawReq.toLowerCase().replace(/\.$/, "")}.`
|
|
689
|
-
: improvement.suggestion,
|
|
690
|
-
acceptance_criteria: [
|
|
691
|
-
`Verify that the system ${rawReq.toLowerCase().replace(/\.$/, "")}`,
|
|
692
|
-
"Automated test validates this requirement",
|
|
693
|
-
],
|
|
694
|
-
source: `Meeting transcript — raw requirement`,
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
// Generate from decisions
|
|
698
|
-
for (const decision of analysis.decisions) {
|
|
699
|
-
const cleanDecision = decision.replace(/^\[[^\]]+\]\s*/, "");
|
|
700
|
-
const id = `REQ-CORE-${String(seqCore++).padStart(3, "0")}`;
|
|
701
|
-
reqs.push({
|
|
702
|
-
id,
|
|
703
|
-
title: cleanDecision.slice(0, 60),
|
|
704
|
-
pattern: "event_driven",
|
|
705
|
-
text: `When the project is implemented, the system shall ${cleanDecision.toLowerCase().replace(/\.$/, "")}.`,
|
|
706
|
-
acceptance_criteria: [
|
|
707
|
-
`Verify that ${cleanDecision.toLowerCase().replace(/\.$/, "")}`,
|
|
708
|
-
"Team review confirms alignment with meeting decision",
|
|
709
|
-
],
|
|
710
|
-
source: `Meeting decision`,
|
|
711
|
-
});
|
|
712
|
-
}
|
|
713
|
-
// Generate from constraints
|
|
714
|
-
for (const constraint of analysis.constraints_mentioned) {
|
|
715
|
-
const id = `REQ-NFR-${String(seqNfr++).padStart(3, "0")}`;
|
|
716
|
-
reqs.push({
|
|
717
|
-
id,
|
|
718
|
-
title: constraint.slice(0, 60),
|
|
719
|
-
pattern: "ubiquitous",
|
|
720
|
-
text: `The system shall comply with the constraint: ${constraint.toLowerCase().replace(/\.$/, "")}.`,
|
|
721
|
-
acceptance_criteria: [
|
|
722
|
-
`Verify compliance with: ${constraint}`,
|
|
723
|
-
"Architecture review confirms constraint is met",
|
|
724
|
-
],
|
|
725
|
-
source: `Meeting constraint`,
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
// Generate from topics (at least one per topic)
|
|
729
|
-
for (const topic of analysis.topics) {
|
|
730
|
-
const existing = reqs.some((r) => r.source.includes(topic.name) || r.title.toLowerCase().includes(topic.name.toLowerCase()));
|
|
731
|
-
if (!existing && topic.key_points.length > 0) {
|
|
732
|
-
const id = `REQ-FUNC-${String(seqFunc++).padStart(3, "0")}`;
|
|
733
|
-
reqs.push({
|
|
734
|
-
id,
|
|
735
|
-
title: topic.name,
|
|
736
|
-
pattern: "ubiquitous",
|
|
737
|
-
text: `The system shall support ${topic.name.toLowerCase()} as discussed: ${topic.key_points[0].slice(0, 200)}.`,
|
|
738
|
-
acceptance_criteria: [
|
|
739
|
-
`Verify ${topic.name.toLowerCase()} functionality`,
|
|
740
|
-
...topic.key_points.slice(0, 2).map((kp) => `Verify: ${kp.slice(0, 100)}`),
|
|
741
|
-
],
|
|
742
|
-
source: `Meeting topic: ${topic.name}`,
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
// Ensure minimum 5 requirements
|
|
747
|
-
if (reqs.length < 5) {
|
|
748
|
-
const defaults = [
|
|
749
|
-
{
|
|
750
|
-
title: "System Availability",
|
|
751
|
-
text: "The system shall be available during business hours with 99% uptime.",
|
|
752
|
-
criteria: ["Monitor uptime", "Alert on downtime"],
|
|
753
|
-
},
|
|
754
|
-
{
|
|
755
|
-
title: "Error Handling",
|
|
756
|
-
text: "When an error occurs, the system shall log the error and return a user-friendly message.",
|
|
757
|
-
criteria: ["Verify error logging", "Verify user-friendly error messages"],
|
|
758
|
-
},
|
|
759
|
-
{
|
|
760
|
-
title: "Security",
|
|
761
|
-
text: "The system shall authenticate all users before granting access to protected resources.",
|
|
762
|
-
criteria: ["Verify authentication is required", "Verify unauthorized access is rejected"],
|
|
763
|
-
},
|
|
764
|
-
];
|
|
765
|
-
for (const def of defaults) {
|
|
766
|
-
if (reqs.length >= 5)
|
|
767
|
-
break;
|
|
768
|
-
const id = `REQ-NFR-${String(seqNfr++).padStart(3, "0")}`;
|
|
769
|
-
reqs.push({
|
|
770
|
-
id,
|
|
771
|
-
title: def.title,
|
|
772
|
-
pattern: reqs.length % 2 === 0 ? "ubiquitous" : "event_driven",
|
|
773
|
-
text: def.text,
|
|
774
|
-
acceptance_criteria: def.criteria,
|
|
775
|
-
source: "Auto-generated baseline requirement",
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
return reqs;
|
|
791
|
+
function batchAnalysisGaps(gaps, openQuestions) {
|
|
792
|
+
return gaps.length > 0 ? gaps : openQuestions.map((question) => `Open: ${question}`);
|
|
780
793
|
}
|
|
781
|
-
function
|
|
782
|
-
|
|
783
|
-
`Architecture auto-generated from meeting transcript with ${analysis.participants.length} participants.`,
|
|
784
|
-
"",
|
|
785
|
-
"## Key Components (from meeting topics)",
|
|
786
|
-
"",
|
|
787
|
-
];
|
|
788
|
-
for (const topic of analysis.topics) {
|
|
789
|
-
lines.push(`### ${topic.name}`);
|
|
790
|
-
lines.push("");
|
|
791
|
-
lines.push(topic.summary || "Component discussed in meeting.");
|
|
792
|
-
lines.push("");
|
|
793
|
-
if (topic.key_points.length > 0) {
|
|
794
|
-
lines.push("**Key points:**");
|
|
795
|
-
for (const kp of topic.key_points.slice(0, 3)) {
|
|
796
|
-
lines.push(`- ${kp}`);
|
|
797
|
-
}
|
|
798
|
-
lines.push("");
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
if (analysis.decisions.length > 0) {
|
|
802
|
-
lines.push("## Architecture Decisions");
|
|
803
|
-
lines.push("");
|
|
804
|
-
for (const d of analysis.decisions) {
|
|
805
|
-
lines.push(`- ${d}`);
|
|
806
|
-
}
|
|
807
|
-
lines.push("");
|
|
808
|
-
}
|
|
809
|
-
lines.push(`## Requirements Coverage`);
|
|
810
|
-
lines.push("");
|
|
811
|
-
lines.push(`Total requirements: ${requirements.length}`);
|
|
812
|
-
lines.push(`Topics covered: ${analysis.topics.length}`);
|
|
813
|
-
return lines.join("\n");
|
|
794
|
+
function evidenceStatus(passed) {
|
|
795
|
+
return passed ? "✅" : "❌";
|
|
814
796
|
}
|
|
815
|
-
function
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
tasks.push({
|
|
820
|
-
id: `T-${String(seq++).padStart(3, "0")}`,
|
|
821
|
-
title: "Project setup and scaffolding",
|
|
822
|
-
parallel: false,
|
|
823
|
-
effort: "M",
|
|
824
|
-
depends: "—",
|
|
825
|
-
traces: "REQ-CORE-001",
|
|
826
|
-
});
|
|
827
|
-
// Tasks from requirements
|
|
828
|
-
for (const req of requirements) {
|
|
829
|
-
tasks.push({
|
|
830
|
-
id: `T-${String(seq++).padStart(3, "0")}`,
|
|
831
|
-
title: `Implement: ${req.title.slice(0, 60)}`,
|
|
832
|
-
parallel: seq > 3, // First 2 tasks sequential, rest parallel
|
|
833
|
-
effort: req.acceptance_criteria.length > 2 ? "L" : "M",
|
|
834
|
-
depends: seq <= 3 ? `T-${String(seq - 2).padStart(3, "0")}` : "T-001",
|
|
835
|
-
traces: req.id,
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
// Tasks from action items
|
|
839
|
-
for (const action of analysis.action_items.slice(0, 5)) {
|
|
840
|
-
const cleanAction = action.replace(/^\[[^\]]+\]\s*/, "");
|
|
841
|
-
tasks.push({
|
|
842
|
-
id: `T-${String(seq++).padStart(3, "0")}`,
|
|
843
|
-
title: `Action: ${cleanAction.slice(0, 60)}`,
|
|
844
|
-
parallel: true,
|
|
845
|
-
effort: "S",
|
|
846
|
-
depends: "T-001",
|
|
847
|
-
traces: "—",
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
// Final tasks
|
|
851
|
-
tasks.push({
|
|
852
|
-
id: `T-${String(seq++).padStart(3, "0")}`,
|
|
853
|
-
title: "Integration testing",
|
|
854
|
-
parallel: false,
|
|
855
|
-
effort: "L",
|
|
856
|
-
depends: tasks
|
|
857
|
-
.slice(-3)
|
|
858
|
-
.map((t) => t.id)
|
|
859
|
-
.join(", "),
|
|
860
|
-
traces: "All REQ",
|
|
861
|
-
});
|
|
862
|
-
tasks.push({
|
|
863
|
-
id: `T-${String(seq++).padStart(3, "0")}`,
|
|
864
|
-
title: "Documentation and deployment",
|
|
865
|
-
parallel: false,
|
|
866
|
-
effort: "M",
|
|
867
|
-
depends: `T-${String(seq - 2).padStart(3, "0")}`,
|
|
868
|
-
traces: "All REQ",
|
|
869
|
-
});
|
|
870
|
-
return tasks;
|
|
797
|
+
function batchCompletionMessage(failedCount) {
|
|
798
|
+
return failedCount > 0
|
|
799
|
+
? `${failedCount} transcript feature(s) failed; no unvalidated source content was synthesized.`
|
|
800
|
+
: "All explicitly configured transcript features were processed successfully.";
|
|
871
801
|
}
|
|
872
802
|
//# sourceMappingURL=transcript.js.map
|