@planu/cli 5.7.5 → 5.7.7
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 +88 -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-review-panel-trigger.sh +2 -3
- 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 +8 -12
- 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/evidence-skeletons.js +3 -1
- 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 +43 -5
- 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 +32 -12
- 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.d.ts +3 -3
- package/dist/engine/spec-grounding/contract.js +65 -4
- package/dist/engine/spec-grounding/grounding-entry-parser.d.ts +5 -0
- package/dist/engine/spec-grounding/grounding-entry-parser.js +185 -0
- 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/storage/spec-store.js +8 -1
- package/dist/tools/bump-spec-version.js +5 -5
- 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/init-project/skills-multi-teammate-review-writer.js +1 -1
- 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/multi-teammate-review.js +1 -14
- 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-platform-tools/design-stack-tools.js +3 -3
- package/dist/tools/register-sdd-tools.js +0 -23
- package/dist/tools/register-spec-tools/analysis-tools.js +22 -9
- package/dist/tools/register-spec-tools/core-spec-tools.js +115 -70
- 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 +72 -164
- package/dist/tools/tool-registry/group-infra.js +2 -39
- package/dist/tools/tool-registry/group-quality-compliance.js +20 -15
- package/dist/tools/tool-registry-helpers.js +6 -2
- 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 +26 -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
- package/dist/tools/tool-registry/deprecated-stubs.d.ts +0 -19
- package/dist/tools/tool-registry/deprecated-stubs.js +0 -65
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
// engine/evidence-gates/evidence-autofill.ts — SPEC-1356: automated lifecycle evidence pipeline
|
|
2
|
-
//
|
|
3
|
-
// Generates traceability-matrix.json in the handoff evidence store when it is
|
|
4
|
-
// missing, so a `done` transition never fails purely because nobody hand-wrote
|
|
5
|
-
// the traceability artifact.
|
|
6
|
-
//
|
|
7
|
-
// ORDERING CAVEAT (documented per SPEC-1356 T-A3): writing traceability-matrix.json
|
|
8
|
-
// *after* `validate` has already computed and bound durable validation receipts
|
|
9
|
-
// would make those bindings stale relative to the newly-written evidence — the
|
|
10
|
-
// receipt would be certifying a repository state that does not include this file.
|
|
11
|
-
// This module does NOT attempt to re-run or re-bind validation. Instead, callers
|
|
12
|
-
// (see `src/tools/update-status/index.ts`) MUST treat a write from this module as
|
|
13
|
-
// "evidence created, not yet bound": when autofill actually writes the file, the
|
|
14
|
-
// caller must block the `done` transition with a message telling the operator to
|
|
15
|
-
// re-run `validate` once so the freshly-created evidence gets bound, then retry
|
|
16
|
-
// `done`. This is the simpler, safe choice given SPEC-1356 could not fully verify
|
|
17
|
-
// that skipping straight through to the done gates after autofill would keep
|
|
18
|
-
// receipt bindings correct in every code path (reconciliation, forced bypass,
|
|
19
|
-
// legacy harnesses, etc.).
|
|
20
2
|
import { execFile } from 'node:child_process';
|
|
21
3
|
import { existsSync } from 'node:fs';
|
|
22
4
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
@@ -113,6 +95,18 @@ function filterExistingRepoFiles(projectPath, paths) {
|
|
|
113
95
|
}
|
|
114
96
|
});
|
|
115
97
|
}
|
|
98
|
+
/** Prose template + matching regex for the generator's validation evidence, shared by the
|
|
99
|
+
* generator (renders it) and the staleness classifier (recognizes it). */
|
|
100
|
+
export function buildValidationEvidenceText(specId, score) {
|
|
101
|
+
return `Validated by handoffs/${specId}/validation-report.json (score=${score ?? 'n/a'}).`;
|
|
102
|
+
}
|
|
103
|
+
export const VALIDATION_EVIDENCE_PATTERN = /^Validated by handoffs\/.+\/validation-report\.json \(score=.+\)\.$/;
|
|
104
|
+
/** Prose template + matching regex for the generator's reviewer evidence, shared by the
|
|
105
|
+
* generator (renders it) and the staleness classifier (recognizes it). */
|
|
106
|
+
export function buildReviewerEvidenceText(agent, kind, verdict) {
|
|
107
|
+
return `Reviewed by ${agent} (${kind}), verdict=${verdict}.`;
|
|
108
|
+
}
|
|
109
|
+
export const REVIEWER_EVIDENCE_PATTERN = /^Reviewed by .+ \(.+\), verdict=.+\.$/;
|
|
116
110
|
/** Best-effort read + sha256 summary of the persisted validation report, for prose evidence. */
|
|
117
111
|
async function readValidationReportSummary(projectId, specId) {
|
|
118
112
|
const reportPath = join(projectDataDir(projectId), 'handoffs', specId, 'validation-report.json');
|
|
@@ -124,47 +118,103 @@ async function readValidationReportSummary(projectId, specId) {
|
|
|
124
118
|
}
|
|
125
119
|
const { score, reviewer } = parsed.data;
|
|
126
120
|
return {
|
|
127
|
-
validationEvidence:
|
|
128
|
-
reviewerEvidence:
|
|
121
|
+
validationEvidence: buildValidationEvidenceText(specId, score),
|
|
122
|
+
reviewerEvidence: buildReviewerEvidenceText(reviewer.agent, reviewer.kind, reviewer.verdict),
|
|
129
123
|
};
|
|
130
124
|
}
|
|
131
125
|
catch {
|
|
132
126
|
return {};
|
|
133
127
|
}
|
|
134
128
|
}
|
|
129
|
+
function isAutofillShapedMatrix(candidate) {
|
|
130
|
+
if (typeof candidate !== 'object' ||
|
|
131
|
+
candidate === null ||
|
|
132
|
+
!Array.isArray(candidate.rows)) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
const rows = candidate.rows;
|
|
136
|
+
if (rows.length === 0) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return rows.every((row) => row.validationEvidence !== undefined &&
|
|
140
|
+
row.reviewerEvidence !== undefined &&
|
|
141
|
+
VALIDATION_EVIDENCE_PATTERN.test(row.validationEvidence) &&
|
|
142
|
+
REVIEWER_EVIDENCE_PATTERN.test(row.reviewerEvidence));
|
|
143
|
+
}
|
|
144
|
+
const LEGACY_ROW_ALLOWED_KEYS = new Set(['acceptanceCriterion', 'testEvidence', 'changedFiles']);
|
|
145
|
+
/** A pre-evidence-schema matrix: every row uses only the generator's original
|
|
146
|
+
* keys, with neither validationEvidence nor reviewerEvidence present. Safe to
|
|
147
|
+
* regenerate the same way a stale autofill-shaped matrix is. */
|
|
148
|
+
function isLegacyGeneratorShapedMatrix(candidate) {
|
|
149
|
+
if (typeof candidate !== 'object' ||
|
|
150
|
+
candidate === null ||
|
|
151
|
+
!Array.isArray(candidate.rows)) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
const rows = candidate.rows;
|
|
155
|
+
if (rows.length === 0) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return rows.every((row) => Object.keys(row).every((key) => LEGACY_ROW_ALLOWED_KEYS.has(key)));
|
|
159
|
+
}
|
|
160
|
+
/** Reads the on-disk matrix, if any, and classifies whether it is safe to
|
|
161
|
+
* regenerate. A hand-written, mixed, or unparseable matrix is never touched. */
|
|
162
|
+
async function readRegeneratableMatrix(targetPath) {
|
|
163
|
+
if (!existsSync(targetPath)) {
|
|
164
|
+
return { kind: 'absent' };
|
|
165
|
+
}
|
|
166
|
+
const raw = await readFile(targetPath, 'utf-8');
|
|
167
|
+
try {
|
|
168
|
+
const parsed = JSON.parse(raw);
|
|
169
|
+
return isAutofillShapedMatrix(parsed) || isLegacyGeneratorShapedMatrix(parsed)
|
|
170
|
+
? { kind: 'autofill-shaped', raw }
|
|
171
|
+
: { kind: 'manual' };
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return { kind: 'manual' };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function resolveChangedFiles(args) {
|
|
178
|
+
const exec = args.exec ?? defaultExec;
|
|
179
|
+
const baseBranch = args.baseBranch ?? 'main';
|
|
180
|
+
const gitEntries = args.projectPath
|
|
181
|
+
? await computeChangedFilesFromGit(args.projectPath, exec, baseBranch)
|
|
182
|
+
: [];
|
|
183
|
+
const gitChangedFiles = excludeBookkeepingChanges(gitEntries, args.ownSpecMdPath);
|
|
184
|
+
const ownership = extractCanonicalFileOwnership(args.specBody);
|
|
185
|
+
const ownershipFiles = [...ownership.toCreate, ...ownership.toModify, ...ownership.toTest];
|
|
186
|
+
const rawChangedFiles = gitChangedFiles.length > 0 ? gitChangedFiles : ownershipFiles;
|
|
187
|
+
return [
|
|
188
|
+
...new Set(args.projectPath
|
|
189
|
+
? filterExistingRepoFiles(args.projectPath, rawChangedFiles)
|
|
190
|
+
: rawChangedFiles),
|
|
191
|
+
];
|
|
192
|
+
}
|
|
135
193
|
/**
|
|
136
|
-
* Generate `traceability-matrix.json` in the handoff evidence store
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* transition.
|
|
194
|
+
* Generate `traceability-matrix.json` in the handoff evidence store, or regenerate
|
|
195
|
+
* it in place when it is autofill-shaped but stale relative to the current
|
|
196
|
+
* validation report. Never touches a hand-written or mixed matrix — those are a
|
|
197
|
+
* permanent no-op. Tolerates every failure by returning a typed non-throwing
|
|
198
|
+
* result; callers must never let this crash a lifecycle transition.
|
|
141
199
|
*/
|
|
142
200
|
export async function autofillTraceabilityMatrix(args) {
|
|
143
201
|
const targetPath = handoffEvidencePath(args.projectId, args.specId, 'traceability-matrix.json');
|
|
144
202
|
try {
|
|
145
|
-
if (existsSync(targetPath)) {
|
|
146
|
-
return { written: false, reason: 'already-exists' };
|
|
147
|
-
}
|
|
148
203
|
const criteria = extractAcceptanceCriteriaTexts(args.specBody);
|
|
149
204
|
if (criteria.length === 0) {
|
|
150
205
|
return { written: false, reason: 'no-criteria' };
|
|
151
206
|
}
|
|
207
|
+
const existingLookup = await readRegeneratableMatrix(targetPath);
|
|
208
|
+
if (existingLookup.kind === 'manual') {
|
|
209
|
+
return { written: false, reason: 'already-exists-manual' };
|
|
210
|
+
}
|
|
211
|
+
const existingRaw = existingLookup.kind === 'autofill-shaped' ? existingLookup.raw : undefined;
|
|
152
212
|
const scenarios = parseFrontmatterScenarios(args.specBody);
|
|
153
|
-
const
|
|
154
|
-
const baseBranch = args.baseBranch ?? 'main';
|
|
155
|
-
const gitEntries = args.projectPath
|
|
156
|
-
? await computeChangedFilesFromGit(args.projectPath, exec, baseBranch)
|
|
157
|
-
: [];
|
|
158
|
-
const gitChangedFiles = excludeBookkeepingChanges(gitEntries, args.ownSpecMdPath);
|
|
159
|
-
const ownership = extractCanonicalFileOwnership(args.specBody);
|
|
160
|
-
const ownershipFiles = [...ownership.toCreate, ...ownership.toModify, ...ownership.toTest];
|
|
161
|
-
const rawChangedFiles = gitChangedFiles.length > 0 ? gitChangedFiles : ownershipFiles;
|
|
162
|
-
const changedFiles = [
|
|
163
|
-
...new Set(args.projectPath
|
|
164
|
-
? filterExistingRepoFiles(args.projectPath, rawChangedFiles)
|
|
165
|
-
: rawChangedFiles),
|
|
166
|
-
];
|
|
213
|
+
const changedFiles = await resolveChangedFiles(args);
|
|
167
214
|
const { validationEvidence, reviewerEvidence } = await readValidationReportSummary(args.projectId, args.specId);
|
|
215
|
+
if (existingRaw !== undefined && validationEvidence === undefined) {
|
|
216
|
+
return { written: false, reason: 'already-exists-current' };
|
|
217
|
+
}
|
|
168
218
|
const rows = criteria.map((acceptanceCriterion, index) => {
|
|
169
219
|
const testPaths = scenarios[index]?.tests?.map((test) => test.path) ?? [];
|
|
170
220
|
return {
|
|
@@ -176,7 +226,20 @@ export async function autofillTraceabilityMatrix(args) {
|
|
|
176
226
|
};
|
|
177
227
|
});
|
|
178
228
|
const payload = { version: 1, rows };
|
|
179
|
-
|
|
229
|
+
const payloadJson = JSON.stringify(payload, null, 2);
|
|
230
|
+
if (existingRaw !== undefined) {
|
|
231
|
+
if (existingRaw === payloadJson) {
|
|
232
|
+
return { written: false, reason: 'already-exists-current' };
|
|
233
|
+
}
|
|
234
|
+
await atomicWriteFile(targetPath, payloadJson);
|
|
235
|
+
return {
|
|
236
|
+
written: true,
|
|
237
|
+
path: targetPath,
|
|
238
|
+
rowCount: rows.length,
|
|
239
|
+
reason: 'regenerated-stale',
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
await atomicWriteFile(targetPath, payloadJson);
|
|
180
243
|
return { written: true, path: targetPath, rowCount: rows.length };
|
|
181
244
|
}
|
|
182
245
|
catch (error) {
|
|
@@ -32,7 +32,9 @@ function outOfScopeFromBody(specBody) {
|
|
|
32
32
|
}
|
|
33
33
|
/** Build a strict-schema discovery.json skeleton grounded in the spec's own grounding metadata. */
|
|
34
34
|
export function generateDiscoverySkeleton(args) {
|
|
35
|
-
const records = args.body
|
|
35
|
+
const records = args.body
|
|
36
|
+
? parseCriterionGroundingRecords(specFrontmatter(args.body)).records
|
|
37
|
+
: [];
|
|
36
38
|
const rules = records.length > 0 ? [...new Set(records.map((record) => record.text))] : [DEFAULT_RULE];
|
|
37
39
|
const examples = records.length > 0
|
|
38
40
|
? records.map((record) => ({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, join } from 'node:path';
|
|
2
|
-
import { DISCOVERY_TEMPLATE_OVERRIDES, DiscoverySchema, evidenceArtifactCollectionDigest, evidenceArtifactDigest, } from './artifact-reader.js';
|
|
2
|
+
import { DISCOVERY_TEMPLATE_OVERRIDES, DiscoverySchema, artifactHint, evidenceArtifactCollectionDigest, evidenceArtifactDigest, } from './artifact-reader.js';
|
|
3
3
|
import { renderSchemaSkeleton } from '../handoff-artifacts/schema-skeleton.js';
|
|
4
4
|
import { hasAnyAffirmedMatch, stripMetaAnalysisText, stripNonContractText, } from '../text-signal-boundaries.js';
|
|
5
5
|
import { createCriterionIdentity } from '../criterion-identity.js';
|
|
@@ -218,7 +218,7 @@ export function checkDoneEvidenceGate(spec, criteria, artifacts) {
|
|
|
218
218
|
if (!matrix) {
|
|
219
219
|
issues.push({
|
|
220
220
|
code: 'traceability_missing',
|
|
221
|
-
message:
|
|
221
|
+
message: `Traceability matrix evidence is required before done. Add traceability-matrix.json covering each acceptance criterion. ${artifactHint('Traceability matrix evidence')}`,
|
|
222
222
|
});
|
|
223
223
|
}
|
|
224
224
|
else {
|
|
@@ -238,7 +238,7 @@ export function checkDoneEvidenceGate(spec, criteria, artifacts) {
|
|
|
238
238
|
const rowDetails = incompleteRows.map(describeIncompleteRow);
|
|
239
239
|
issues.push({
|
|
240
240
|
code: 'traceability_incomplete_rows',
|
|
241
|
-
message: `Traceability rows must include evidence, changed files, validation evidence, and reviewer evidence. Incomplete rows: ${rowDetails.join('; ')}
|
|
241
|
+
message: `Traceability rows must include evidence, changed files, validation evidence, and reviewer evidence. Incomplete rows: ${rowDetails.join('; ')}. Check traceability-matrix.json in this spec's handoff evidence store: a generator-shaped matrix regenerates automatically the next time validate runs and the done transition is retried, but a hand-written matrix must be edited in place.`,
|
|
242
242
|
});
|
|
243
243
|
}
|
|
244
244
|
}
|
|
@@ -251,9 +251,12 @@ export function checkDoneEvidenceGate(spec, criteria, artifacts) {
|
|
|
251
251
|
.map((artifact) => artifact.kind);
|
|
252
252
|
const missingKinds = requiredKinds.filter((kind) => !passedKinds.has(kind));
|
|
253
253
|
if (missingKinds.length > 0) {
|
|
254
|
+
const hints = missingKinds
|
|
255
|
+
.map((kind) => artifactHint(`contract-validation-${kind}.json`))
|
|
256
|
+
.join(' ');
|
|
254
257
|
issues.push({
|
|
255
258
|
code: 'contract_validation_missing',
|
|
256
|
-
message: `Contract validation evidence is required for: ${missingKinds.join(', ')}`,
|
|
259
|
+
message: `Contract validation evidence is required for: ${missingKinds.join(', ')}. ${hints}`,
|
|
257
260
|
});
|
|
258
261
|
}
|
|
259
262
|
if (failedKinds.length > 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { stat } from 'node:fs/promises';
|
|
2
1
|
import { isAbsolute, join, normalize } from 'node:path';
|
|
2
|
+
import { pathExistsByStat as pathExists } from '../../core/shared/fs.js';
|
|
3
3
|
import { createCriterionIdentity, criterionIdentitiesMatch } from '../criterion-identity.js';
|
|
4
4
|
export function criterionKey(criterion) {
|
|
5
5
|
return createCriterionIdentity(criterion).stableKey;
|
|
@@ -9,6 +9,10 @@ function truncateAtEvidenceMarker(value) {
|
|
|
9
9
|
const markerIndex = value.search(EVIDENCE_MARKER);
|
|
10
10
|
return markerIndex === -1 ? value : value.slice(0, markerIndex);
|
|
11
11
|
}
|
|
12
|
+
const LINE_CITATION_SUFFIX = /:\d+(?:-\d+)?$/;
|
|
13
|
+
function pathForExistenceCheck(value) {
|
|
14
|
+
return value.replace(LINE_CITATION_SUFFIX, '');
|
|
15
|
+
}
|
|
12
16
|
export async function buildSpecEvidenceIndex(args) {
|
|
13
17
|
const diagnostics = [...args.artifacts.invalidArtifacts];
|
|
14
18
|
const matrixRows = args.artifacts.traceabilityMatrix?.rows ?? [];
|
|
@@ -129,12 +133,25 @@ async function pathRecord(kind, value, source, args, fieldName) {
|
|
|
129
133
|
if (!args.projectPath) {
|
|
130
134
|
return record(kind, value, 'missing', source, evidencePathReason(fieldName, 'projectPath unavailable'));
|
|
131
135
|
}
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
.catch(() => false);
|
|
136
|
+
const literalExists = await pathExists(safe.path);
|
|
137
|
+
const exists = literalExists || (await fallbackExists(value, fieldName, args.projectPath));
|
|
135
138
|
const missingReason = evidencePathReason(fieldName, `Missing path: ${value}`);
|
|
136
139
|
return record(kind, value, exists ? 'valid' : missingPathStatus(fieldName), source, exists ? undefined : missingReason);
|
|
137
140
|
}
|
|
141
|
+
async function fallbackExists(value, fieldName, projectPath) {
|
|
142
|
+
if (fieldName === 'changedFiles') {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
const strippedValue = pathForExistenceCheck(value);
|
|
146
|
+
if (strippedValue === value || strippedValue.length === 0) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const strippedSafe = resolveSafeProjectPath(strippedValue, projectPath, fieldName);
|
|
150
|
+
if (!strippedSafe.ok) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
return pathExists(strippedSafe.path);
|
|
154
|
+
}
|
|
138
155
|
function missingPathStatus(fieldName) {
|
|
139
156
|
return fieldName === 'changedFiles' ? 'missing' : 'stale';
|
|
140
157
|
}
|
|
@@ -249,7 +249,7 @@ function isQueueRelevant(job) {
|
|
|
249
249
|
return true;
|
|
250
250
|
}
|
|
251
251
|
const kind = job.payload.kind;
|
|
252
|
-
return kind === undefined || kind === 'validate';
|
|
252
|
+
return kind === undefined || kind === 'validate' || kind === 'package-handoff';
|
|
253
253
|
}
|
|
254
254
|
function remainingBudgetMs(job, now = Date.now()) {
|
|
255
255
|
const payload = job.payload && typeof job.payload === 'object'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DurableJobRuntime } from './job-runtime.js';
|
|
2
|
+
import type { DurableJobRecord, PackageHandoffDurableJobPayload, PackageHandoffStageResult } from '../../types/durable-job.js';
|
|
3
|
+
export declare function isPackageHandoffJob(value: unknown): boolean;
|
|
4
|
+
export declare function parsePackageHandoffPayload(value: unknown): PackageHandoffDurableJobPayload | undefined;
|
|
5
|
+
/** Bounds one stage with AbortSignal.timeout + Promise.race; the stage's own promise is orphaned on timeout. */
|
|
6
|
+
export declare function runStageWithDeadline<T>(stage: string, budgetMs: number, fn: (signal: AbortSignal) => Promise<T>): Promise<PackageHandoffStageResult<T>>;
|
|
7
|
+
/** Executes accepted package-handoff jobs directly against the packaging engine, never through MCP. */
|
|
8
|
+
export declare class PackageHandoffJobExecutor {
|
|
9
|
+
private readonly runtime;
|
|
10
|
+
private readonly workerId;
|
|
11
|
+
constructor(runtime: DurableJobRuntime, workerId?: string);
|
|
12
|
+
runOnce(projectId: string, workspaceId?: string): Promise<readonly DurableJobRecord[]>;
|
|
13
|
+
runUntilIdle(projectId: string, workspaceId?: string): Promise<void>;
|
|
14
|
+
private candidates;
|
|
15
|
+
private execute;
|
|
16
|
+
private runJob;
|
|
17
|
+
}
|
|
18
|
+
/** Schedules background execution after the submit response can be flushed. */
|
|
19
|
+
export declare function schedulePackageHandoffJobExecution(projectId: string, workspaceId?: string): void;
|
|
20
|
+
/** Re-enqueues accepted/checkpointed package-handoff work discovered after a process restart. */
|
|
21
|
+
export declare function recoverPackageHandoffJobsAtStartup(): void;
|
|
22
|
+
//# sourceMappingURL=package-handoff-job-executor.d.ts.map
|