@planu/cli 5.7.5 → 5.7.6
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 +75 -0
- package/README.md +3 -3
- package/dist/.planu-build.json +1 -1
- package/dist/cli/commands/package-handoff.js +30 -5
- package/dist/config/compliance-profiles.json +36 -3
- package/dist/config/environment-schema.json +21 -0
- package/dist/config/hook-templates/planu-session-safeguard.sh +22 -15
- package/dist/config/official-sdd-tools.d.ts +1 -1
- package/dist/config/official-sdd-tools.js +11 -24
- package/dist/config/registries/hosts/codex.json +14 -30
- package/dist/config/registries/hosts/opencode.json +1 -6
- package/dist/config/server-instructions.js +0 -21
- package/dist/config/skill-templates/planu-context-assets.md +3 -6
- package/dist/config/skill-templates/planu-implement.md +2 -2
- package/dist/config/skill-templates/planu-multi-teammate-review.md +1 -1
- package/dist/config/skill-templates/planu-release.md +2 -7
- package/dist/config/skill-templates/planu-validate.md +3 -3
- package/dist/config/spec-templates/crud-rest-api/template.json +5 -1
- package/dist/config/spec-templates/file-upload-s3/template.json +5 -1
- package/dist/config/spec-templates/stripe-payments/template.json +5 -1
- package/dist/config/spec-templates/webhook-system/template.json +5 -1
- package/dist/config/subagent-templates/planu-challenger.md +4 -9
- package/dist/config/subagent-templates/planu-spec-implementer.md +2 -4
- package/dist/config/subagent-templates/planu-validator.md +5 -8
- package/dist/config/tool-groups.json +6 -144
- package/dist/engine/ambiguity-scorer.js +40 -1
- package/dist/engine/autopilot/bootstrap.js +1 -1
- package/dist/engine/code-impact-analyzer.js +2 -6
- package/dist/engine/context-orchestrator/index.js +2 -2
- package/dist/engine/convention-scanner/codebase-scanner.js +2 -2
- package/dist/engine/dogfooding/runtime-gap-detector.js +2 -2
- package/dist/engine/evidence-gates/artifact-reader.d.ts +1 -0
- package/dist/engine/evidence-gates/artifact-reader.js +1 -1
- package/dist/engine/evidence-gates/evidence-autofill.d.ts +13 -5
- package/dist/engine/evidence-gates/evidence-autofill.js +106 -43
- package/dist/engine/evidence-gates/lifecycle-gate.js +7 -4
- package/dist/engine/evidence-index/index-builder.js +21 -4
- package/dist/engine/execution/job-runtime.js +1 -1
- package/dist/engine/execution/package-handoff-job-executor.d.ts +22 -0
- package/dist/engine/execution/package-handoff-job-executor.js +490 -0
- package/dist/engine/execution/validate-job-executor.js +20 -8
- package/dist/engine/git/exec-git.d.ts +4 -0
- package/dist/engine/git/exec-git.js +14 -0
- package/dist/engine/handoff-artifacts/implementation-review-reader.js +15 -2
- package/dist/engine/handoff-artifacts/schemas.d.ts +2 -0
- package/dist/engine/handoff-artifacts/schemas.js +1 -0
- package/dist/engine/handoff-format.d.ts +36 -0
- package/dist/engine/handoff-format.js +314 -0
- package/dist/engine/handoff-packager.d.ts +5 -3
- package/dist/engine/handoff-packager.js +89 -32
- package/dist/engine/host-tool-filter.js +20 -49
- package/dist/engine/human-summary.js +14 -3
- package/dist/engine/implementation-contract/common.d.ts +8 -3
- package/dist/engine/implementation-contract/common.js +9 -6
- package/dist/engine/implementation-contract/evaluator.js +4 -6
- package/dist/engine/implementation-contract/renderer.js +18 -23
- package/dist/engine/project-health-checker.js +2 -2
- package/dist/engine/readiness-checker.js +27 -0
- package/dist/engine/reconcile/apply-changes.js +4 -0
- package/dist/engine/reconcile/propagate-mirrors.d.ts +3 -0
- package/dist/engine/reconcile/propagate-mirrors.js +311 -0
- package/dist/engine/self-healing/healer.js +12 -5
- package/dist/engine/self-healing/strategies/lint-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/lint-fix.js +4 -1
- package/dist/engine/self-healing/strategies/test-fix.d.ts +4 -3
- package/dist/engine/self-healing/strategies/test-fix.js +32 -14
- package/dist/engine/self-healing/strategies/typescript-fix.d.ts +1 -1
- package/dist/engine/self-healing/strategies/typescript-fix.js +28 -10
- package/dist/engine/session/checkpoint-writer.js +8 -4
- package/dist/engine/session/session-tracker.d.ts +5 -16
- package/dist/engine/session/session-tracker.js +29 -26
- package/dist/engine/session-state/writer.js +3 -11
- package/dist/engine/skill-generator/workflow-skill-generator.d.ts +4 -2
- package/dist/engine/skill-generator/workflow-skill-generator.js +113 -38
- package/dist/engine/spec-format/bdd-parser.d.ts +1 -11
- package/dist/engine/spec-format/bdd-parser.js +40 -11
- package/dist/engine/spec-format/lean-spec-generator.js +30 -10
- package/dist/engine/spec-format/retired-scaffold.d.ts +3 -0
- package/dist/engine/spec-format/retired-scaffold.js +5 -0
- package/dist/engine/spec-format/technical-md-populator.d.ts +1 -0
- package/dist/engine/spec-format/technical-md-populator.js +27 -0
- package/dist/engine/spec-format/unified-spec-builder.d.ts +1 -0
- package/dist/engine/spec-format/unified-spec-builder.js +30 -20
- package/dist/engine/spec-grounding/contract.js +115 -1
- package/dist/engine/spec-quality/generic-output-gate.js +37 -3
- package/dist/engine/technical-enricher/index.js +4 -1
- package/dist/engine/type-safety-gate.js +3 -11
- package/dist/engine/universal-rules/catalog.js +0 -2
- package/dist/engine/universal-rules/rules/planu-workflow.js +1 -1
- package/dist/engine/validation/durable-validation.d.ts +3 -2
- package/dist/engine/validation/durable-validation.js +106 -40
- package/dist/engine/validation/validation-freshness.d.ts +3 -2
- package/dist/engine/validation/validation-freshness.js +22 -16
- package/dist/engine/validation/validation-worktree.js +34 -0
- package/dist/hosts/claude-code/ux/mcp-prompts.js +8 -8
- package/dist/index.js +3 -17
- package/dist/resources/process.js +38 -65
- package/dist/storage/session-state-store.js +2 -0
- package/dist/tools/challenge-spec/scenarios-utils.js +2 -2
- package/dist/tools/configure-checkpoint-policy.js +3 -3
- package/dist/tools/create-spec-helpers.js +2 -5
- package/dist/tools/create-spec.js +21 -4
- package/dist/tools/facilitate.js +3 -6
- package/dist/tools/git/branch-ops.js +16 -4
- package/dist/tools/github-release-handler.js +1 -20
- package/dist/tools/init-project/agents-md-writer.js +6 -6
- package/dist/tools/init-project/claude-md-generator.d.ts +0 -1
- package/dist/tools/init-project/claude-md-generator.js +0 -35
- package/dist/tools/init-project/handler.js +7 -5
- package/dist/tools/init-project/per-client-files-writer.js +5 -5
- package/dist/tools/init-project/planu-workflow-generator.js +2 -13
- package/dist/tools/init-project/portable-index-reconciler.d.ts +2 -1
- package/dist/tools/init-project/portable-index-reconciler.js +7 -2
- package/dist/tools/init-project/rules-generator.d.ts +0 -11
- package/dist/tools/init-project/rules-generator.js +4 -85
- package/dist/tools/init-project/scaffold-writer.d.ts +0 -1
- package/dist/tools/init-project/scaffold-writer.js +1 -12
- package/dist/tools/jobs/handlers.d.ts +0 -6
- package/dist/tools/jobs/handlers.js +13 -40
- package/dist/tools/list-specs.js +6 -21
- package/dist/tools/package-handoff.d.ts +3 -2
- package/dist/tools/package-handoff.js +153 -427
- package/dist/tools/reconcile-session-safeguard-hook.js +22 -15
- package/dist/tools/register-sdd-tools.js +0 -23
- package/dist/tools/register-spec-tools/core-spec-tools.js +18 -27
- package/dist/tools/schemas/index.d.ts +1 -1
- package/dist/tools/schemas/index.js +1 -1
- package/dist/tools/schemas/output-schemas.d.ts +1 -18
- package/dist/tools/schemas/output-schemas.js +1 -11
- package/dist/tools/schemas/package-handoff-output-schema.d.ts +40 -0
- package/dist/tools/schemas/package-handoff-output-schema.js +68 -0
- package/dist/tools/schemas/validate-output-schema.d.ts +1 -2
- package/dist/tools/schemas/validate-output-schema.js +3 -2
- package/dist/tools/session-checkpoint.js +2 -2
- package/dist/tools/skill-registry/index.d.ts +0 -1
- package/dist/tools/skill-registry/index.js +0 -1
- package/dist/tools/sync-spec-state-handler.d.ts +2 -2
- package/dist/tools/sync-spec-state-handler.js +23 -3
- package/dist/tools/tool-registry/core-tools.js +1 -140
- package/dist/tools/tool-registry/group-infra.js +2 -39
- package/dist/tools/tool-registry/group-quality-compliance.js +9 -1
- package/dist/tools/update-status/batch.js +17 -0
- package/dist/tools/update-status/dod-gates.js +14 -1
- package/dist/tools/update-status/done-receipt-verifier.d.ts +22 -0
- package/dist/tools/update-status/done-receipt-verifier.js +56 -26
- package/dist/tools/update-status/evidence-gate.js +10 -21
- package/dist/tools/update-status/file-sync.d.ts +1 -0
- package/dist/tools/update-status/file-sync.js +6 -0
- package/dist/tools/update-status/index.d.ts +6 -0
- package/dist/tools/update-status/index.js +39 -44
- package/dist/tools/update-status-actions.js +2 -7
- package/dist/tools/validate.js +34 -28
- package/dist/transports/oauth-validator.js +12 -1
- package/dist/transports/transport-factory.d.ts +2 -1
- package/dist/transports/transport-factory.js +19 -0
- package/dist/types/common/primitives.d.ts +2 -0
- package/dist/types/durable-job.d.ts +45 -0
- package/dist/types/durable-validation.d.ts +1 -1
- package/dist/types/evidence-autofill.d.ts +13 -1
- package/dist/types/handoff-artifacts.d.ts +2 -1
- package/dist/types/readiness.d.ts +8 -1
- package/dist/types/reconcile.d.ts +6 -0
- package/dist/types/skill-registry.d.ts +26 -1
- package/dist/types/spec/core.d.ts +5 -0
- package/dist/types/spec/inputs.d.ts +2 -2
- package/dist/types/spec-format.d.ts +11 -1
- package/dist/types/spec-grounding.d.ts +12 -0
- package/dist/types/transport.d.ts +1 -0
- package/dist/types/validation-receipt.d.ts +27 -0
- package/package.json +3 -2
- package/planu-plugin.json +13 -26
- package/src/i18n/messages/en.json +1 -1
- package/src/i18n/messages/es.json +1 -1
- package/src/i18n/messages/pt.json +1 -1
- package/dist/engine/skill-generator/conventions-hasher.d.ts +0 -7
- package/dist/engine/skill-generator/conventions-hasher.js +0 -24
- package/dist/engine/universal-rules/rules/agent-teams.d.ts +0 -3
- package/dist/engine/universal-rules/rules/agent-teams.js +0 -63
- package/dist/tools/clarify-requirements/multiple-choice.d.ts +0 -30
- package/dist/tools/clarify-requirements/multiple-choice.js +0 -306
- package/dist/tools/clarify-requirements/questions-context.d.ts +0 -9
- package/dist/tools/clarify-requirements/questions-context.js +0 -74
- package/dist/tools/clarify-requirements/questions.d.ts +0 -16
- package/dist/tools/clarify-requirements/questions.js +0 -96
- package/dist/tools/clarify-requirements.d.ts +0 -4
- package/dist/tools/clarify-requirements.js +0 -314
- package/dist/tools/code-graph-handler.d.ts +0 -6
- package/dist/tools/code-graph-handler.js +0 -72
- package/dist/tools/create-rule.d.ts +0 -4
- package/dist/tools/create-rule.js +0 -107
- package/dist/tools/feedback-handler.d.ts +0 -7
- package/dist/tools/feedback-handler.js +0 -150
- package/dist/tools/semantic-search-handler.d.ts +0 -7
- package/dist/tools/semantic-search-handler.js +0 -71
- package/dist/tools/skill-registry/search.d.ts +0 -11
- package/dist/tools/skill-registry/search.js +0 -159
|
@@ -7,63 +7,34 @@ import { fileURLToPath } from 'node:url';
|
|
|
7
7
|
// Core AGENTS.md tool reference — curated list of key Planu tools
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
export const CORE_AGENTS_MD_TOOLS = [
|
|
10
|
+
{ name: 'init_project', description: 'Load or bootstrap Planu project context' },
|
|
11
|
+
{ name: 'facilitate', description: 'Universal entry point for SDD workflow' },
|
|
10
12
|
{ name: 'create_spec', description: 'Create a new spec from a description' },
|
|
11
13
|
{ name: 'list_specs', description: 'List all specs and their status' },
|
|
12
14
|
{ name: 'update_status', description: 'Move a spec through workflow stages' },
|
|
15
|
+
{
|
|
16
|
+
name: 'update_status_batch',
|
|
17
|
+
description: 'Batch update many specs to the same status in one MCP execution',
|
|
18
|
+
},
|
|
13
19
|
{ name: 'validate', description: 'Check implementation against spec criteria' },
|
|
14
20
|
{ name: 'challenge_spec', description: 'Stress-test a spec for gaps before implementation' },
|
|
15
21
|
{ name: 'check_readiness', description: 'Verify spec has enough detail to implement' },
|
|
16
|
-
{ name: 'planu_status', description: 'Quick project health snapshot' },
|
|
17
|
-
{ name: 'facilitate', description: 'Universal entry point for SDD workflow' },
|
|
18
|
-
{ name: 'plan_mode', description: 'Preview implementation plan before writing code' },
|
|
19
|
-
{ name: 'generate_steering_file', description: 'Generate host-aware steering files' },
|
|
20
|
-
{ name: 'search_specs', description: 'Search specs with fuzzy text matching' },
|
|
21
|
-
{ name: 'spec_quality_score', description: 'Compute quality score for a spec' },
|
|
22
|
-
{ name: 'analyze_spec_size', description: 'Analyze if a spec should be split' },
|
|
23
|
-
{ name: 'decompose_spec', description: 'Decompose a spec into agent tasks' },
|
|
24
|
-
{ name: 'estimate', description: 'Estimate time and cost for a spec' },
|
|
25
|
-
{ name: 'session_checkpoint', description: 'Save session context' },
|
|
26
|
-
{ name: 'project_overview', description: 'Single-call project state query' },
|
|
27
|
-
{ name: 'scan_project', description: 'Scan project and generate specs' },
|
|
28
|
-
{ name: 'validate_workflow', description: 'Validate a workflow definition' },
|
|
29
|
-
{ name: 'tdd_scaffold', description: 'Generate TDD test stubs from spec criteria' },
|
|
30
|
-
{ name: 'coverage_gap_analyzer', description: 'Analyze untested acceptance criteria' },
|
|
31
|
-
{ name: 'security_scan', description: 'Scan dependencies for vulnerabilities' },
|
|
32
|
-
{ name: 'bootstrap_skills', description: 'Auto-install skills for detected stack' },
|
|
33
|
-
{ name: 'manage_drift', description: 'Detect and resolve spec drift' },
|
|
34
|
-
{ name: 'sync_spec_state', description: 'Sync spec status between frontmatter and store' },
|
|
35
|
-
{ name: 'health_check_all', description: 'Run health check on all specs' },
|
|
36
|
-
{ name: 'tokens', description: 'Token usage dashboard' },
|
|
37
|
-
{ name: 'configure_workers', description: 'Configure background analysis workers' },
|
|
38
22
|
{
|
|
39
|
-
name: '
|
|
40
|
-
description:
|
|
23
|
+
name: 'bump_spec_version',
|
|
24
|
+
description: "Bump the SemVer spec_version of a spec's frontmatter",
|
|
25
|
+
},
|
|
26
|
+
{ name: 'reconcile_spec', description: 'Reconcile implementation drift back into the spec' },
|
|
27
|
+
{
|
|
28
|
+
name: 'migrate_legacy_spec',
|
|
29
|
+
description: 'Persist a synthesized spec.md and retire legacy files',
|
|
41
30
|
},
|
|
42
|
-
{ name: '
|
|
43
|
-
{ name: '
|
|
44
|
-
{ name: '
|
|
45
|
-
{ name: '
|
|
46
|
-
{ name: '
|
|
47
|
-
{ name: '
|
|
48
|
-
{ name: '
|
|
49
|
-
{ name: 'render_spec_for_provider', description: 'Render spec for target LLM provider' },
|
|
50
|
-
{ name: 'convert_to_bdd', description: 'Convert spec criteria to BDD format' },
|
|
51
|
-
{ name: 'generate_e2e_tests', description: 'Generate E2E tests from spec criteria' },
|
|
52
|
-
{ name: 'run_spec_lint', description: 'Run custom lint rules against specs' },
|
|
53
|
-
{ name: 'suggest_criteria', description: 'Suggest missing best-practice criteria' },
|
|
54
|
-
{ name: 'check_compliance', description: 'Unified compliance tool' },
|
|
55
|
-
{ name: 'create_release', description: 'Create a GitHub Release' },
|
|
56
|
-
{ name: 'generate_changelog', description: 'Generate changelog from merged PRs' },
|
|
57
|
-
{ name: 'create_pr_from_spec', description: 'Create a PR from a spec' },
|
|
58
|
-
{ name: 'onboarding_progress', description: 'View onboarding progress' },
|
|
59
|
-
{ name: 'list_templates', description: 'Browse spec template catalog' },
|
|
60
|
-
{ name: 'generate_docs_site', description: 'Generate static HTML docs site' },
|
|
61
|
-
{ name: 'plan_team_distribution', description: 'Distribute specs across agents' },
|
|
62
|
-
{ name: 'optimize_spec_scheduling', description: 'Minimize agent idle time' },
|
|
63
|
-
{ name: 'context_window_status', description: 'Estimate token consumption' },
|
|
64
|
-
{ name: 'archive_spec_context', description: 'Archive a spec to free tokens' },
|
|
65
|
-
{ name: 'workspace_overview', description: 'Get overview of all projects' },
|
|
66
|
-
{ name: 'search_all_projects', description: 'Search specs across all projects' },
|
|
31
|
+
{ name: 'package_handoff', description: 'Generate an implementation handoff package for a spec' },
|
|
32
|
+
{ name: 'planu_status', description: 'Quick project health snapshot' },
|
|
33
|
+
{ name: 'session_checkpoint', description: 'Save session context' },
|
|
34
|
+
{ name: 'estimate', description: 'Estimate time and cost for a spec' },
|
|
35
|
+
{ name: 'request_changes', description: 'Request changes on a spec, moving it back to draft' },
|
|
36
|
+
{ name: 'get_job', description: 'Read the durable state of a local Planu job' },
|
|
37
|
+
{ name: 'restart_job', description: 'Restart a cancelled or failed durable job' },
|
|
67
38
|
];
|
|
68
39
|
// ---------------------------------------------------------------------------
|
|
69
40
|
// Interactive detection heuristics
|
|
@@ -89,20 +89,31 @@ export function buildListSpecsSummary(total, byStatus) {
|
|
|
89
89
|
return `No plans found in this project yet. Use create_spec to add your first one.`;
|
|
90
90
|
}
|
|
91
91
|
const get = (key) => byStatus[key] ?? 0;
|
|
92
|
-
const inProgress = get('implementing')
|
|
92
|
+
const inProgress = get('implementing');
|
|
93
|
+
const approved = get('approved');
|
|
93
94
|
const inReview = get('review');
|
|
95
|
+
const approvedClause = approved > 0 ? `${String(approved)} approved ready to implement` : '';
|
|
94
96
|
if (inReview > 0 && inProgress > 0) {
|
|
95
97
|
const reviewNoun = inReview === 1 ? 'plan' : 'plans';
|
|
96
|
-
|
|
98
|
+
const suffix = approvedClause ? ` and ${approvedClause}` : '';
|
|
99
|
+
return `You have ${String(inProgress)} in progress and ${String(inReview)} ${reviewNoun} ready to review${suffix}. Start with the ones under review.`;
|
|
97
100
|
}
|
|
98
101
|
if (inReview > 0) {
|
|
99
102
|
const noun = inReview === 1 ? 'plan' : 'plans';
|
|
100
|
-
|
|
103
|
+
const suffix = approvedClause ? ` You also have ${approvedClause}.` : '';
|
|
104
|
+
return `You have ${String(inReview)} ${noun} ready to review. Approve or request changes to move forward.${suffix}`;
|
|
105
|
+
}
|
|
106
|
+
if (inProgress > 0 && approved > 0) {
|
|
107
|
+
return `You have ${String(inProgress)} in progress and ${approvedClause}. Run validate when implementation is done.`;
|
|
101
108
|
}
|
|
102
109
|
if (inProgress > 0) {
|
|
103
110
|
const noun = inProgress === 1 ? 'plan' : 'plans';
|
|
104
111
|
return `You have ${String(inProgress)} ${noun} in progress. Run validate when implementation is done.`;
|
|
105
112
|
}
|
|
113
|
+
if (approved > 0) {
|
|
114
|
+
const noun = approved === 1 ? 'plan' : 'plans';
|
|
115
|
+
return `You have ${String(approved)} ${noun} approved ready to implement. Run validate when implementation is done.`;
|
|
116
|
+
}
|
|
106
117
|
return `You have ${String(total)} plans in this project. Approve a plan to start implementing.`;
|
|
107
118
|
}
|
|
108
119
|
/** estimate — time and cost estimated for a spec */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const IMPLEMENTATION_CONTRACT_SECTION = "Implementation Contract";
|
|
2
2
|
/** Subsections every rendered contract must include — omitting one is a defect. */
|
|
3
|
-
export declare const IMPLEMENTATION_CONTRACT_REQUIRED_SUBSECTIONS: readonly ['User Outcome'
|
|
3
|
+
export declare const IMPLEMENTATION_CONTRACT_REQUIRED_SUBSECTIONS: readonly ['User Outcome'];
|
|
4
4
|
/**
|
|
5
5
|
* SPEC-1406: subsections that carry no grounded content are omitted from the rendered
|
|
6
6
|
* body rather than filled with restated-criterion filler. Their absence is not an
|
|
@@ -8,10 +8,15 @@ export declare const IMPLEMENTATION_CONTRACT_REQUIRED_SUBSECTIONS: readonly ['Us
|
|
|
8
8
|
* already-persisted specs that still carry these sections (with real, non-empty
|
|
9
9
|
* content) transitionable: `evaluateImplementationContract` only requires them when
|
|
10
10
|
* the rendered body chose to include the heading.
|
|
11
|
+
*
|
|
12
|
+
* SPEC-1435: extends the same omission precedent to File-Level Work Plan,
|
|
13
|
+
* Acceptance-To-Verification Map, and Verification Commands — the renderer used to
|
|
14
|
+
* fill these with a constant "no evidence was grounded" sentence that its own quality
|
|
15
|
+
* gates could not see. Now the renderer omits the subsection instead.
|
|
11
16
|
*/
|
|
12
|
-
export declare const IMPLEMENTATION_CONTRACT_OPTIONAL_SUBSECTIONS: readonly ['Behavior Contract', 'Edge Cases And Failure Modes', 'Non-Goals And Forbidden Approaches'];
|
|
17
|
+
export declare const IMPLEMENTATION_CONTRACT_OPTIONAL_SUBSECTIONS: readonly ['Behavior Contract', 'Edge Cases And Failure Modes', 'Non-Goals And Forbidden Approaches', 'File-Level Work Plan', 'Acceptance-To-Verification Map', 'Verification Commands'];
|
|
13
18
|
/** Union of required + optional subsections. Kept for callers that only need the full set. */
|
|
14
|
-
export declare const IMPLEMENTATION_CONTRACT_SUBSECTIONS: readonly ["User Outcome", "
|
|
19
|
+
export declare const IMPLEMENTATION_CONTRACT_SUBSECTIONS: readonly ["User Outcome", "Behavior Contract", "Edge Cases And Failure Modes", "Non-Goals And Forbidden Approaches", "File-Level Work Plan", "Acceptance-To-Verification Map", "Verification Commands"];
|
|
15
20
|
export declare function hasImplementationContract(specBody: string): boolean;
|
|
16
21
|
export declare function extractTopLevelSection(body: string, sectionName: string): string;
|
|
17
22
|
//# sourceMappingURL=common.d.ts.map
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { escapeRegex } from '../../core/shared/strings.js';
|
|
2
2
|
export const IMPLEMENTATION_CONTRACT_SECTION = 'Implementation Contract';
|
|
3
3
|
/** Subsections every rendered contract must include — omitting one is a defect. */
|
|
4
|
-
export const IMPLEMENTATION_CONTRACT_REQUIRED_SUBSECTIONS = [
|
|
5
|
-
'User Outcome',
|
|
6
|
-
'File-Level Work Plan',
|
|
7
|
-
'Acceptance-To-Verification Map',
|
|
8
|
-
'Verification Commands',
|
|
9
|
-
];
|
|
4
|
+
export const IMPLEMENTATION_CONTRACT_REQUIRED_SUBSECTIONS = ['User Outcome'];
|
|
10
5
|
/**
|
|
11
6
|
* SPEC-1406: subsections that carry no grounded content are omitted from the rendered
|
|
12
7
|
* body rather than filled with restated-criterion filler. Their absence is not an
|
|
@@ -14,11 +9,19 @@ export const IMPLEMENTATION_CONTRACT_REQUIRED_SUBSECTIONS = [
|
|
|
14
9
|
* already-persisted specs that still carry these sections (with real, non-empty
|
|
15
10
|
* content) transitionable: `evaluateImplementationContract` only requires them when
|
|
16
11
|
* the rendered body chose to include the heading.
|
|
12
|
+
*
|
|
13
|
+
* SPEC-1435: extends the same omission precedent to File-Level Work Plan,
|
|
14
|
+
* Acceptance-To-Verification Map, and Verification Commands — the renderer used to
|
|
15
|
+
* fill these with a constant "no evidence was grounded" sentence that its own quality
|
|
16
|
+
* gates could not see. Now the renderer omits the subsection instead.
|
|
17
17
|
*/
|
|
18
18
|
export const IMPLEMENTATION_CONTRACT_OPTIONAL_SUBSECTIONS = [
|
|
19
19
|
'Behavior Contract',
|
|
20
20
|
'Edge Cases And Failure Modes',
|
|
21
21
|
'Non-Goals And Forbidden Approaches',
|
|
22
|
+
'File-Level Work Plan',
|
|
23
|
+
'Acceptance-To-Verification Map',
|
|
24
|
+
'Verification Commands',
|
|
22
25
|
];
|
|
23
26
|
/** Union of required + optional subsections. Kept for callers that only need the full set. */
|
|
24
27
|
export const IMPLEMENTATION_CONTRACT_SUBSECTIONS = [
|
|
@@ -83,12 +83,10 @@ function evaluateSubsection(section, content) {
|
|
|
83
83
|
}
|
|
84
84
|
const trimmed = content.trim();
|
|
85
85
|
if (trimmed.length === 0) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
91
|
-
}
|
|
86
|
+
issues.push({
|
|
87
|
+
code: 'contract_subsection_empty',
|
|
88
|
+
message: `Implementation Contract subsection "${section}" is empty.`,
|
|
89
|
+
});
|
|
92
90
|
return issues;
|
|
93
91
|
}
|
|
94
92
|
for (const pattern of FILLER_PATTERNS) {
|
|
@@ -3,32 +3,32 @@ export function buildImplementationContractSection(input) {
|
|
|
3
3
|
const criteria = input.criteria;
|
|
4
4
|
const testFiles = input.files.test.map((file) => file.path);
|
|
5
5
|
const verificationCommands = input.verificationCommands;
|
|
6
|
+
const filePlan = renderFilePlan(input.files);
|
|
6
7
|
const verificationRows = renderVerificationRows(criteria, testFiles, verificationCommands);
|
|
7
8
|
const failureModes = renderFailureModes(criteria);
|
|
8
9
|
const nonGoals = renderNonGoals(input.outOfScope);
|
|
10
|
+
const verificationCommandLines = verificationCommands.length > 0
|
|
11
|
+
? verificationCommands.map((command) => `- \`${command}\``)
|
|
12
|
+
: criteria.length > 0
|
|
13
|
+
? []
|
|
14
|
+
: ['- Needs decision: provide a repository-supported verification command.'];
|
|
9
15
|
const lines = [
|
|
10
16
|
`## ${IMPLEMENTATION_CONTRACT_SECTION}`,
|
|
11
17
|
'### User Outcome',
|
|
12
18
|
criteria[0]?.text ??
|
|
13
19
|
'Needs decision: define the exact observable outcome this spec must deliver.',
|
|
14
20
|
'',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
'',
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// SPEC-1406 (DEFECT 5): a subsection with no grounded content is omitted rather than
|
|
22
|
-
// filled with a restated-criterion placeholder — prefer omission over filler.
|
|
21
|
+
// SPEC-1406 (DEFECT 5) / SPEC-1435: a subsection with no grounded content is omitted
|
|
22
|
+
// rather than filled with a restated-criterion placeholder — prefer omission over filler.
|
|
23
|
+
...(filePlan.length > 0 ? ['### File-Level Work Plan', ...filePlan, ''] : []),
|
|
24
|
+
...(verificationRows.length > 0
|
|
25
|
+
? ['### Acceptance-To-Verification Map', ...verificationRows, '']
|
|
26
|
+
: []),
|
|
23
27
|
...(failureModes.length > 0 ? ['### Edge Cases And Failure Modes', ...failureModes, ''] : []),
|
|
24
28
|
...(nonGoals.length > 0 ? ['### Non-Goals And Forbidden Approaches', ...nonGoals, ''] : []),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
: criteria.length > 0
|
|
29
|
-
? ['- No repository-supported verification command was grounded for this candidate.']
|
|
30
|
-
: ['- Needs decision: provide a repository-supported verification command.']),
|
|
31
|
-
'',
|
|
29
|
+
...(verificationCommandLines.length > 0
|
|
30
|
+
? ['### Verification Commands', ...verificationCommandLines, '']
|
|
31
|
+
: []),
|
|
32
32
|
];
|
|
33
33
|
return lines.join('\n');
|
|
34
34
|
}
|
|
@@ -38,7 +38,7 @@ export function appendImplementationContractIfMissing(specBody, input) {
|
|
|
38
38
|
}
|
|
39
39
|
return `${specBody.trimEnd()}\n\n${buildImplementationContractSection(input)}`;
|
|
40
40
|
}
|
|
41
|
-
function renderFilePlan(files
|
|
41
|
+
function renderFilePlan(files) {
|
|
42
42
|
const lines = [];
|
|
43
43
|
for (const [label, entries] of [
|
|
44
44
|
['Create', files.create],
|
|
@@ -49,12 +49,7 @@ function renderFilePlan(files, hasGroundedBehavior) {
|
|
|
49
49
|
lines.push(`- ${label}: \`${entry.path}\` (${entry.status})`);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
return lines;
|
|
54
|
-
}
|
|
55
|
-
return hasGroundedBehavior
|
|
56
|
-
? ['- No file ownership was grounded for this behavior.']
|
|
57
|
-
: ['- Needs decision: identify expected source and test files.'];
|
|
52
|
+
return lines;
|
|
58
53
|
}
|
|
59
54
|
/**
|
|
60
55
|
* SPEC-1406 (DEFECT 1, round 2): renders the Acceptance-To-Verification Map body.
|
|
@@ -80,7 +75,7 @@ function renderVerificationRows(criteria, testFiles, commands) {
|
|
|
80
75
|
return rows;
|
|
81
76
|
}
|
|
82
77
|
if (testFiles.length === 0) {
|
|
83
|
-
return [
|
|
78
|
+
return [];
|
|
84
79
|
}
|
|
85
80
|
return renderSharedVerificationRow(testFiles, commands);
|
|
86
81
|
}
|
|
@@ -2,15 +2,15 @@ import { technologyValue } from './technology-registry.js';
|
|
|
2
2
|
import { fileExists } from '../core/shared/fs.js';
|
|
3
3
|
// @crash-shield-ignore-file — config/cache reader for Planu-controlled JSON; writer is this codebase, shape guaranteed by build/seed.
|
|
4
4
|
// engine/project-health-checker.ts — SPEC-180 + SPEC-192 + SPEC-177: Project health check with convention scanning
|
|
5
|
-
import { execSync } from 'node:child_process';
|
|
6
5
|
import { readFile } from 'node:fs/promises';
|
|
7
6
|
import { join, relative } from 'node:path';
|
|
8
7
|
import { parseConventions, scanConventions } from './convention-scanner/index.js';
|
|
9
8
|
import { fastFindPatterns } from './core-bridge.js';
|
|
9
|
+
import { execGitCapture } from './git/exec-git.js';
|
|
10
10
|
// ── helpers ──────────────────────────────────────────────────────────────────
|
|
11
11
|
function execSafe(cmd, cwd) {
|
|
12
12
|
try {
|
|
13
|
-
return
|
|
13
|
+
return execGitCapture(cmd, cwd);
|
|
14
14
|
}
|
|
15
15
|
catch {
|
|
16
16
|
return '';
|
|
@@ -10,11 +10,24 @@ import { findScenariosWithoutTests, parseFrontmatterScenarios, } from './validat
|
|
|
10
10
|
import { normalizeExecutableEvidence } from './validator/executable-evidence.js';
|
|
11
11
|
import { evaluateImplementationContract } from './implementation-contract/index.js';
|
|
12
12
|
import { extractNormalizedAcceptanceCriteria } from './spec-format/acceptance-criteria.js';
|
|
13
|
+
import { isRetiredScaffoldText } from './spec-format/retired-scaffold.js';
|
|
13
14
|
import { detectCrossSpecPremiseContradictions } from './contradiction-detector.js';
|
|
14
15
|
// ── SPEC-784: Technical section quality constants ─────────────────────────────
|
|
15
16
|
const TECHNICAL_MIN_CHARS = 500;
|
|
16
17
|
// Detects "See technical.md", "See spec.md", or "See `<file>` technical.md" patterns
|
|
17
18
|
const TECHNICAL_PLACEHOLDER_REGEX = /\bSee\s+(?:`?[^`\n]+`?\s+)?(?:technical|spec)\.md/i;
|
|
19
|
+
const BDD_CRITERION_RE = /\bGIVEN\b[\s\S]*\bWHEN\b[\s\S]*\bTHEN\b/i;
|
|
20
|
+
function isExecutableBddLine(line) {
|
|
21
|
+
return !isRetiredScaffoldText(line) && BDD_CRITERION_RE.test(line);
|
|
22
|
+
}
|
|
23
|
+
function checkZeroBddCriteria(criteriaLines) {
|
|
24
|
+
if (criteriaLines.length === 0 || criteriaLines.some(isExecutableBddLine)) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
return [
|
|
28
|
+
'NO_BDD_CRITERIA: no acceptance criterion parses as GIVEN/WHEN/THEN — restate at least one criterion in executable form',
|
|
29
|
+
];
|
|
30
|
+
}
|
|
18
31
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
19
32
|
function scoreHuCompleteness(spec) {
|
|
20
33
|
const blockers = [];
|
|
@@ -73,6 +86,15 @@ function countFrontmatterScenarios(raw) {
|
|
|
73
86
|
// Each scenario entry starts with " - title:" (2-space indent)
|
|
74
87
|
return (block.match(/^\s+-\s+title:/gm) ?? []).length;
|
|
75
88
|
}
|
|
89
|
+
function frontmatterScenarioScavengeWarning(bddScenarioCount, normalizedCriteria) {
|
|
90
|
+
const bodyCriteriaCount = normalizedCriteria.filter((criterion) => criterion.source === 'body').length;
|
|
91
|
+
if (bddScenarioCount === 0 || bddScenarioCount >= bodyCriteriaCount) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return (`frontmatter_scenarios_below_body_criteria: frontmatter declares ${String(bddScenarioCount)} ` +
|
|
95
|
+
`scenarios but body renders ${String(bodyCriteriaCount)} acceptance criteria — scenarios may ` +
|
|
96
|
+
`have been scavenged from prose`);
|
|
97
|
+
}
|
|
76
98
|
async function readFichaContent(spec) {
|
|
77
99
|
return readSpecTechnicalSection(spec);
|
|
78
100
|
}
|
|
@@ -418,6 +440,7 @@ export async function checkSpecReadiness(spec, mode, projectHash) {
|
|
|
418
440
|
...earsBlockers,
|
|
419
441
|
];
|
|
420
442
|
const allWarnings = [...hu.warnings, ...criteria.warnings, ...files.warnings, ...deps.warnings];
|
|
443
|
+
allBlockers.push(...checkZeroBddCriteria(effectiveCriteriaLines));
|
|
421
444
|
if (mode === 'strict' && spec.scope !== 'trivial' && spec.difficulty >= 3) {
|
|
422
445
|
for (const issue of evaluateImplementationContract(huContent, canonicalCriteriaLines)) {
|
|
423
446
|
allBlockers.push(`${issue.code}: ${issue.message}`);
|
|
@@ -432,6 +455,10 @@ export async function checkSpecReadiness(spec, mode, projectHash) {
|
|
|
432
455
|
allBlockers.push(`scenarios_missing_tests: scenario "${title}" has no tests array — add tests entries as a block list (tests: with one "- path" line per test) or an inline array (tests: ["path"]) (SPEC-732)`);
|
|
433
456
|
}
|
|
434
457
|
}
|
|
458
|
+
const scavengeWarning = frontmatterScenarioScavengeWarning(bddScenarioCount, normalizedCriteria);
|
|
459
|
+
if (scavengeWarning) {
|
|
460
|
+
allWarnings.push(scavengeWarning);
|
|
461
|
+
}
|
|
435
462
|
// SPEC-1214: BDD-criteria executable-evidence gate — when a spec has no frontmatter
|
|
436
463
|
// scenarios, its criteria must carry a TEST: marker mapping to an executable test file,
|
|
437
464
|
// or validation will later score 0 with "No executable scenarios declared".
|
|
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { atomicWriteFile } from '../safety/atomic-write-file.js';
|
|
3
3
|
import { SECTIONS_WITHOUT_LITERAL_BODY_TEXT, } from '../../types/index.js';
|
|
4
4
|
import { escapeRegex } from '../../core/shared/strings.js';
|
|
5
|
+
import { propagateMirrors } from './propagate-mirrors.js';
|
|
5
6
|
function findSectionBounds(content, section) {
|
|
6
7
|
const headingRe = new RegExp(`^(#{1,6})\\s+${escapeRegex(section)}\\s*$`, 'mi');
|
|
7
8
|
const match = headingRe.exec(content);
|
|
@@ -54,6 +55,7 @@ export async function applyChangesToSpec(specPath, changes) {
|
|
|
54
55
|
let modified = content;
|
|
55
56
|
let applied = 0;
|
|
56
57
|
const skipped = [];
|
|
58
|
+
const appliedSections = new Set();
|
|
57
59
|
for (const change of approvedTextChanges) {
|
|
58
60
|
const bounds = findSectionBounds(modified, change.section);
|
|
59
61
|
if (bounds === null) {
|
|
@@ -92,10 +94,12 @@ export async function applyChangesToSpec(specPath, changes) {
|
|
|
92
94
|
change.newValue +
|
|
93
95
|
modified.slice(absoluteIdx + change.originalValue.length);
|
|
94
96
|
applied++;
|
|
97
|
+
appliedSections.add(change.section);
|
|
95
98
|
}
|
|
96
99
|
if (applied === 0) {
|
|
97
100
|
return { applied: 0, skipped, fileWritten: false };
|
|
98
101
|
}
|
|
102
|
+
modified = propagateMirrors(modified, appliedSections);
|
|
99
103
|
await atomicWriteFile(specPath, modified);
|
|
100
104
|
return { applied, skipped, fileWritten: true };
|
|
101
105
|
}
|