frontend-harness 0.2.3 → 0.3.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/README.md +27 -25
- package/dist/cli/index.js +8 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/runtime/builtin-skills.js +6 -13
- package/dist/runtime/builtin-skills.js.map +1 -1
- package/dist/runtime/command-taxonomy.d.ts +1 -0
- package/dist/runtime/command-taxonomy.js +6 -2
- package/dist/runtime/command-taxonomy.js.map +1 -1
- package/dist/runtime/common/naming.js +16 -1
- package/dist/runtime/common/naming.js.map +1 -1
- package/dist/runtime/context.js +2 -2
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/graph.js +5 -1
- package/dist/runtime/graph.js.map +1 -1
- package/dist/runtime/knowledge.js +12 -6
- package/dist/runtime/knowledge.js.map +1 -1
- package/dist/runtime/plan/component-resolver.js +88 -31
- package/dist/runtime/plan/component-resolver.js.map +1 -1
- package/dist/runtime/plan/guidance.js +8 -4
- package/dist/runtime/plan/guidance.js.map +1 -1
- package/dist/runtime/plan/proposal.js +4 -0
- package/dist/runtime/plan/proposal.js.map +1 -1
- package/dist/runtime/plan/workflow.js +45 -51
- package/dist/runtime/plan/workflow.js.map +1 -1
- package/dist/runtime/plan.js +101 -9
- package/dist/runtime/plan.js.map +1 -1
- package/dist/runtime/policy-provenance.js +2 -2
- package/dist/runtime/policy-provenance.js.map +1 -1
- package/dist/runtime/protocol-init.js +16 -15
- package/dist/runtime/protocol-init.js.map +1 -1
- package/dist/runtime/repair-packet.js +4 -3
- package/dist/runtime/repair-packet.js.map +1 -1
- package/dist/runtime/scaffold/vue-template.js +1 -1
- package/dist/runtime/scaffold/vue-template.js.map +1 -1
- package/dist/runtime/state-explain.js +2 -1
- package/dist/runtime/state-explain.js.map +1 -1
- package/dist/runtime/state.d.ts +10 -0
- package/dist/runtime/state.js +86 -18
- package/dist/runtime/state.js.map +1 -1
- package/dist/runtime/ui-restoration.d.ts +3 -2
- package/dist/runtime/ui-restoration.js +100 -26
- package/dist/runtime/ui-restoration.js.map +1 -1
- package/dist/runtime/units.js +25 -3
- package/dist/runtime/units.js.map +1 -1
- package/dist/runtime/verify.js +269 -3
- package/dist/runtime/verify.js.map +1 -1
- package/dist/schemas/types.d.ts +2 -0
- package/docs/DIRECTION.md +1 -1
- package/package.json +3 -5
- package/AGENTS.md +0 -60
- package/CLAUDE.md +0 -60
- package/dist/runtime/common/parsing.d.ts +0 -11
- package/dist/runtime/common/parsing.js +0 -30
- package/dist/runtime/common/parsing.js.map +0 -1
- package/dist/runtime/project-discovery.d.ts +0 -17
- package/dist/runtime/project-discovery.js +0 -174
- package/dist/runtime/project-discovery.js.map +0 -1
- package/dist/runtime/skills.d.ts +0 -19
- package/dist/runtime/skills.js +0 -230
- package/dist/runtime/skills.js.map +0 -1
- package/dist/runtime/verification-commands.d.ts +0 -11
- package/dist/runtime/verification-commands.js +0 -93
- package/dist/runtime/verification-commands.js.map +0 -1
- package/dist/storage/json.d.ts +0 -5
- package/dist/storage/json.js +0 -29
- package/dist/storage/json.js.map +0 -1
- package/dist/storage/paths.d.ts +0 -3
- package/dist/storage/paths.js +0 -9
- package/dist/storage/paths.js.map +0 -1
|
@@ -1,18 +1,87 @@
|
|
|
1
1
|
import { lines } from "./common/text.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
const UI_RESTORATION_RULES = [
|
|
3
|
+
{
|
|
4
|
+
section: "Source Material",
|
|
5
|
+
constraint: "Treat imported MCP/Pixso/Figma/Stitch/HTML output as source material, not project-ready code.",
|
|
6
|
+
guidance: "Treat MCP, Pixso, Figma, Stitch, or exported HTML as reference material and rewrite it into project code."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
section: "Source Material",
|
|
10
|
+
constraint: "When both design structure data and screenshots exist, use design structure data for hierarchy, text, dimensions, tokens, and component boundaries; use screenshots for final visual verification.",
|
|
11
|
+
guidance: "Prefer DST or structured design data for hierarchy, text, dimensions, tokens, and component boundaries; use screenshots to verify final rendered appearance, alignment, overflow, and visual state."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
section: "Project Conventions",
|
|
15
|
+
constraint: "Normalize generated UI into project components, naming, state, routing, and styling conventions.",
|
|
16
|
+
guidance: "Preserve the target project's component API, naming, routing, state, style, UI-library, icon, and layout conventions."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
section: "Source Structure",
|
|
20
|
+
constraint: "Split substantial restored screens into page orchestration, typed data fixtures, view configuration, calculations, and focused presentational components.",
|
|
21
|
+
guidance: "Keep page files thin; move types, data fixtures, view configuration, calculations, and repeated sections into feature modules or focused components."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
section: "Source Structure",
|
|
25
|
+
constraint: "Represent mock/domain data with named object fields instead of long positional factory arguments.",
|
|
26
|
+
guidance: "Use named object fixtures for domain rows; avoid long positional factory arguments in generated examples."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
section: "State And Interactions",
|
|
30
|
+
constraint: "Derive progress, summary values, badges, disabled states, and totals from source data or explicit workflow state instead of duplicating static literals.",
|
|
31
|
+
guidance: "Derive summary values, progress text, percentages, bars, badges, and disabled states from the same source state."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
section: "State And Interactions",
|
|
35
|
+
constraint: "Preserve PRD terminology in visible labels and record stable domain wording in source-linked project knowledge when PRD input is provided.",
|
|
36
|
+
guidance: "Preserve PRD terminology in visible labels and capture stable domain wording in project knowledge with source_paths and coverage when PRD input is available."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
section: "State And Interactions",
|
|
40
|
+
constraint: "Implement expected interactions for the affected workflow, or mark unsupported interactions as explicit assumptions in the handoff evidence.",
|
|
41
|
+
guidance: "Implement or explicitly defer expected interactions for the affected workflow, such as filtering, selection, editing, navigation, dialogs, or bulk actions when applicable."
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
section: "Styling",
|
|
45
|
+
constraint: "Use reusable design tokens for colors, spacing, layout metrics, and status semantics instead of one-off page-only constants.",
|
|
46
|
+
guidance: "Use reusable design tokens for colors, spacing, status colors, and layout metrics instead of page-only literals."
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
section: "Required Artifacts",
|
|
50
|
+
constraint: "Keep design input, PRD summary, component boundary notes, and visual verification evidence as durable project artifacts when a design source is provided.",
|
|
51
|
+
guidance: "Keep design input, PRD summary, component boundary notes, and visual verification screenshots or notes as project artifacts when a design source exists."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
section: "Project Conventions",
|
|
55
|
+
constraint: "Replace visible placeholder labels for icons or visual primitives with real project icons, components, or CSS shapes.",
|
|
56
|
+
guidance: "Replace visible placeholder labels for icons or visual primitives with real project icons, components, or CSS shapes."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
section: "Accessibility",
|
|
60
|
+
constraint: "Preserve accessibility semantics for controls, forms, dialogs, tables, keyboard navigation, focus order, and icon-only actions.",
|
|
61
|
+
guidance: "Use semantic HTML and project UI-library accessibility behavior for controls, forms, dialogs, tables, keyboard navigation, focus order, and icon-only actions."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
section: "Localization",
|
|
65
|
+
constraint: "Keep visible copy, dates, numbers, directionality, and terminology compatible with the project's i18n and localization conventions.",
|
|
66
|
+
guidance: "Follow project i18n conventions for visible copy, dates, numbers, directionality, and terminology instead of hard-coding locale-specific formatting."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
section: "Styling",
|
|
70
|
+
constraint: "Use project theme tokens for light/dark mode, status colors, typography, density, and responsive breakpoints instead of one-off visual constants.",
|
|
71
|
+
guidance: "Use project theme tokens for light/dark mode, status colors, typography, density, and responsive breakpoints instead of page-only constants."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
section: "Verification",
|
|
75
|
+
constraint: "Cross-check visible state consistency across selected steps, progress labels, percentages, bars, badges, disabled states, and summary counts.",
|
|
76
|
+
guidance: "Verify visible state consistency across selected steps, progress labels, percentages, bars, badges, disabled states, and summary counts."
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
section: "Verification",
|
|
80
|
+
constraint: "Keep generated build output such as dist/ out of lint and typecheck scopes.",
|
|
81
|
+
guidance: "Keep generated build output such as dist/ out of lint and typecheck scopes."
|
|
82
|
+
}
|
|
15
83
|
];
|
|
84
|
+
export const UI_RESTORATION_CONSTRAINTS = UI_RESTORATION_RULES.map((rule) => rule.constraint);
|
|
16
85
|
export const UI_RESTORATION_VERIFICATION_FOCUS = [
|
|
17
86
|
"typecheck",
|
|
18
87
|
"component behavior",
|
|
@@ -20,19 +89,24 @@ export const UI_RESTORATION_VERIFICATION_FOCUS = [
|
|
|
20
89
|
"responsive rendering",
|
|
21
90
|
"visual regressions when UI source is provided"
|
|
22
91
|
];
|
|
23
|
-
export const UI_RESTORATION_GUIDANCE =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"Preserve PRD terminology in visible labels and capture stable domain wording in project knowledge when PRD input is available.",
|
|
28
|
-
"Implement or explicitly defer expected interactions for the affected workflow, such as filtering, selection, editing, navigation, dialogs, or bulk actions when applicable.",
|
|
29
|
-
"Use reusable design tokens for colors, spacing, status colors, and layout metrics instead of page-only literals.",
|
|
30
|
-
"Keep design input, PRD summary, component boundary notes, and visual verification screenshots or notes as project artifacts when a design source exists.",
|
|
31
|
-
"Replace visible placeholder labels for icons or visual primitives with real project icons, components, or CSS shapes.",
|
|
32
|
-
"Verify visible state consistency across selected steps, progress labels, percentages, bars, badges, disabled states, and summary counts.",
|
|
33
|
-
"Keep generated build output such as dist/ out of lint and typecheck scopes."
|
|
34
|
-
];
|
|
92
|
+
export const UI_RESTORATION_GUIDANCE = UI_RESTORATION_RULES.map((rule) => rule.guidance);
|
|
93
|
+
export function renderUiImplementationSkillBullets() {
|
|
94
|
+
return UI_RESTORATION_GUIDANCE.map((item) => `- ${item}`).join("\n");
|
|
95
|
+
}
|
|
35
96
|
export function renderUiRestorationChecklist() {
|
|
36
|
-
return lines("# UI Restoration Checklist", "", "Use this checklist when converting design files, screenshots, exported HTML, or generated UI into project code.", "",
|
|
97
|
+
return lines("# UI Restoration Checklist", "", "Use this checklist when converting design files, screenshots, exported HTML, or generated UI into project code.", "", ...renderChecklistSections());
|
|
98
|
+
}
|
|
99
|
+
function renderChecklistSections() {
|
|
100
|
+
const output = [];
|
|
101
|
+
for (const section of checklistSections()) {
|
|
102
|
+
output.push(`## ${section}`, "");
|
|
103
|
+
output.push(...UI_RESTORATION_RULES.map((rule) => rule.section === section ? `- ${rule.guidance}` : "").filter(Boolean));
|
|
104
|
+
output.push("");
|
|
105
|
+
}
|
|
106
|
+
output.pop();
|
|
107
|
+
return output;
|
|
108
|
+
}
|
|
109
|
+
function checklistSections() {
|
|
110
|
+
return [...new Set(UI_RESTORATION_RULES.map((rule) => rule.section))];
|
|
37
111
|
}
|
|
38
112
|
//# sourceMappingURL=ui-restoration.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-restoration.js","sourceRoot":"","sources":["../../src/runtime/ui-restoration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,
|
|
1
|
+
{"version":3,"file":"ui-restoration.js","sourceRoot":"","sources":["../../src/runtime/ui-restoration.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/units.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { relativeHarnessPath } from "../storage/paths.js";
|
|
4
|
+
import { projectScriptCommand } from "./command-taxonomy.js";
|
|
5
|
+
import { UI_RESTORATION_VERIFICATION_FOCUS } from "./ui-restoration.js";
|
|
4
6
|
import { discoverVerificationCommands, defaultVerificationCommandNames } from "./verification-commands.js";
|
|
5
7
|
const policyVerificationNames = [
|
|
6
8
|
"targeted test",
|
|
@@ -11,6 +13,7 @@ const policyVerificationNames = [
|
|
|
11
13
|
"documentation review",
|
|
12
14
|
"review findings"
|
|
13
15
|
];
|
|
16
|
+
const policyEvidenceNames = UI_RESTORATION_VERIFICATION_FOCUS.filter((focus) => focus !== "typecheck");
|
|
14
17
|
export function checkExecutionUnits(projectRoot) {
|
|
15
18
|
const artifactPath = relativeHarnessPath("execution-units", "latest.json");
|
|
16
19
|
const fullPath = path.join(projectRoot, artifactPath);
|
|
@@ -21,7 +24,7 @@ export function checkExecutionUnits(projectRoot) {
|
|
|
21
24
|
unitCount: 0,
|
|
22
25
|
independentUnitIds: [],
|
|
23
26
|
errors: [],
|
|
24
|
-
warnings: [
|
|
27
|
+
warnings: [`Run ${projectScriptCommand("frontend-harness plan --json \"<task>\"")} before checking execution units.`]
|
|
25
28
|
};
|
|
26
29
|
}
|
|
27
30
|
const errors = [];
|
|
@@ -33,6 +36,7 @@ export function checkExecutionUnits(projectRoot) {
|
|
|
33
36
|
.filter((command) => command.command)
|
|
34
37
|
.map((command) => command.name)
|
|
35
38
|
]);
|
|
39
|
+
const knownEvidenceNames = new Set(policyEvidenceNames);
|
|
36
40
|
const artifact = readArtifact(fullPath, errors);
|
|
37
41
|
const units = getUnits(artifact, errors);
|
|
38
42
|
const ids = new Set();
|
|
@@ -57,7 +61,7 @@ export function checkExecutionUnits(projectRoot) {
|
|
|
57
61
|
if (!isRecord(unit)) {
|
|
58
62
|
continue;
|
|
59
63
|
}
|
|
60
|
-
validateUnit(unit, index, ids, knownVerificationNames, errors);
|
|
64
|
+
validateUnit(unit, index, ids, knownVerificationNames, knownEvidenceNames, errors);
|
|
61
65
|
}
|
|
62
66
|
validateDependencyCycles(units, errors);
|
|
63
67
|
return {
|
|
@@ -123,10 +127,11 @@ function getUnits(artifact, errors) {
|
|
|
123
127
|
}
|
|
124
128
|
return artifact["units"];
|
|
125
129
|
}
|
|
126
|
-
function validateUnit(unit, index, ids, knownVerificationNames, errors) {
|
|
130
|
+
function validateUnit(unit, index, ids, knownVerificationNames, knownEvidenceNames, errors) {
|
|
127
131
|
validateFile(unit, index, errors);
|
|
128
132
|
validateTask(unit, index, errors);
|
|
129
133
|
validateVerification(unit, index, knownVerificationNames, errors);
|
|
134
|
+
validateVerificationEvidence(unit, index, knownEvidenceNames, errors);
|
|
130
135
|
validateDependsOn(unit, index, ids, errors);
|
|
131
136
|
validateParallelGroup(unit, index, errors);
|
|
132
137
|
}
|
|
@@ -162,6 +167,20 @@ function validateVerification(unit, index, knownVerificationNames, errors) {
|
|
|
162
167
|
}
|
|
163
168
|
}
|
|
164
169
|
}
|
|
170
|
+
function validateVerificationEvidence(unit, index, knownEvidenceNames, errors) {
|
|
171
|
+
if (unit.verificationEvidence === undefined) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (!Array.isArray(unit.verificationEvidence)) {
|
|
175
|
+
errors.push(`units[${index}].verificationEvidence must be an array when present.`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
for (const [evidenceIndex, evidence] of unit.verificationEvidence.entries()) {
|
|
179
|
+
if (typeof evidence !== "string" || !knownEvidenceNames.has(evidence)) {
|
|
180
|
+
errors.push(`units[${index}].verificationEvidence[${evidenceIndex}] must be one of: ${[...knownEvidenceNames].join(", ")}.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
165
184
|
function validateDependsOn(unit, index, ids, errors) {
|
|
166
185
|
if (!Array.isArray(unit.dependsOn)) {
|
|
167
186
|
errors.push(`units[${index}].dependsOn must be an array.`);
|
|
@@ -208,6 +227,9 @@ function isProjectRelativeSourcePath(file) {
|
|
|
208
227
|
if (normalized.startsWith("/") || /^[a-zA-Z]:\//.test(normalized)) {
|
|
209
228
|
return false;
|
|
210
229
|
}
|
|
230
|
+
if (normalized === ".frontend-harness/knowledge" || normalized.startsWith(".frontend-harness/knowledge/")) {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
211
233
|
const firstSegment = normalized.split("/")[0];
|
|
212
234
|
return firstSegment !== ".frontend-harness";
|
|
213
235
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/runtime/units.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,4BAA4B,EAAE,+BAA+B,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/runtime/units.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime/verify.js
CHANGED
|
@@ -4,13 +4,110 @@ import path from "node:path";
|
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { harnessPath } from "../storage/paths.js";
|
|
6
6
|
import { ensureDir, readJson, writeJson, writeText } from "../storage/json.js";
|
|
7
|
-
import { updateVerificationState } from "./state.js";
|
|
7
|
+
import { checkStateContract, loadState, updateVerificationState } from "./state.js";
|
|
8
|
+
import { checkKnowledgeCoverage } from "./knowledge.js";
|
|
8
9
|
import { discoverVerificationCommands } from "./verification-commands.js";
|
|
9
10
|
import { createRepairPacket } from "./repair-packet.js";
|
|
11
|
+
import { projectScriptCommand } from "./command-taxonomy.js";
|
|
10
12
|
const MAX_NODE_E_SCRIPT_LENGTH = 200;
|
|
11
13
|
export function runVerification(projectRoot, options) {
|
|
12
14
|
const commands = resolveCommands(projectRoot, options);
|
|
13
15
|
const startedFromFailed = readPreviousStatus(projectRoot) === "failed";
|
|
16
|
+
const stateContract = checkStateContract(projectRoot);
|
|
17
|
+
if (stateContract.status === "failed") {
|
|
18
|
+
const relativeLogPath = `.frontend-harness/logs/harness-contract-${new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}.log`;
|
|
19
|
+
writeText(path.join(projectRoot, relativeLogPath), [
|
|
20
|
+
"Harness execution contract failed before project verification.",
|
|
21
|
+
"",
|
|
22
|
+
"## Errors",
|
|
23
|
+
...stateContract.errors.map((error) => `- ${error}`),
|
|
24
|
+
"",
|
|
25
|
+
"## Warnings",
|
|
26
|
+
...(stateContract.warnings.length ? stateContract.warnings.map((warning) => `- ${warning}`) : ["- none"]),
|
|
27
|
+
"",
|
|
28
|
+
"## Expected execution-unit files",
|
|
29
|
+
...(stateContract.expectedFiles.length ? stateContract.expectedFiles.map((file) => `- ${file}`) : ["- none"]),
|
|
30
|
+
"",
|
|
31
|
+
"## Recorded changed files",
|
|
32
|
+
...(stateContract.changedFiles.length ? stateContract.changedFiles.map((file) => `- ${file}`) : ["- none"])
|
|
33
|
+
].join("\n"));
|
|
34
|
+
const result = {
|
|
35
|
+
status: "failed",
|
|
36
|
+
results: [
|
|
37
|
+
{
|
|
38
|
+
name: "harness-contract",
|
|
39
|
+
command: "frontend-harness state check --json",
|
|
40
|
+
source: "none",
|
|
41
|
+
status: "failed",
|
|
42
|
+
exitCode: 1,
|
|
43
|
+
durationMs: 0,
|
|
44
|
+
logPath: relativeLogPath
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
retryGuidance: {
|
|
48
|
+
summary: "recorded changed files do not cover the planned execution units",
|
|
49
|
+
failedCommands: ["frontend-harness state check --json"],
|
|
50
|
+
logs: [relativeLogPath],
|
|
51
|
+
nextAgentInstruction: [
|
|
52
|
+
"Read .frontend-harness/plans/latest.json and .frontend-harness/execution-units/latest.json.",
|
|
53
|
+
"Record every planned execution-unit file that was edited.",
|
|
54
|
+
"If the implementation boundary changed, rerun planning before verification.",
|
|
55
|
+
`Then rerun ${projectScriptCommand("frontend-harness verify --json")}.`
|
|
56
|
+
].join(" ")
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
writeResult(projectRoot, result);
|
|
60
|
+
updateVerificationState(projectRoot, "failed", startedFromFailed);
|
|
61
|
+
createRepairPacket(projectRoot, { verification: result });
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
const state = loadState(projectRoot);
|
|
65
|
+
if (state.task?.inputs?.prd) {
|
|
66
|
+
const coverage = checkKnowledgeCoverage(projectRoot);
|
|
67
|
+
if (coverage.status !== "passed") {
|
|
68
|
+
const relativeLogPath = `.frontend-harness/logs/knowledge-coverage-${new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}.log`;
|
|
69
|
+
writeText(path.join(projectRoot, relativeLogPath), [
|
|
70
|
+
"PRD traceability failed before project verification.",
|
|
71
|
+
"",
|
|
72
|
+
`PRD input: ${state.task.inputs.prd}`,
|
|
73
|
+
`coverage status: ${coverage.status}`,
|
|
74
|
+
"",
|
|
75
|
+
"## Errors",
|
|
76
|
+
...(coverage.errors.length ? coverage.errors.map((error) => `- ${error}`) : ["- none"]),
|
|
77
|
+
"",
|
|
78
|
+
"## Warnings",
|
|
79
|
+
...(coverage.warnings.length ? coverage.warnings.map((warning) => `- ${warning}`) : ["- none"])
|
|
80
|
+
].join("\n"));
|
|
81
|
+
const result = {
|
|
82
|
+
status: "failed",
|
|
83
|
+
results: [
|
|
84
|
+
{
|
|
85
|
+
name: "knowledge-coverage",
|
|
86
|
+
command: "frontend-harness knowledge coverage --json",
|
|
87
|
+
source: "none",
|
|
88
|
+
status: "failed",
|
|
89
|
+
exitCode: 1,
|
|
90
|
+
durationMs: 0,
|
|
91
|
+
logPath: relativeLogPath
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
retryGuidance: {
|
|
95
|
+
summary: "PRD-derived knowledge is missing source traceability",
|
|
96
|
+
failedCommands: ["frontend-harness knowledge coverage --json"],
|
|
97
|
+
logs: [relativeLogPath],
|
|
98
|
+
nextAgentInstruction: [
|
|
99
|
+
"Distill PRD acceptance rules into .frontend-harness/knowledge cards.",
|
|
100
|
+
"Each active PRD card must include source_paths and coverage for the referenced PRD section.",
|
|
101
|
+
`Then rerun ${projectScriptCommand("frontend-harness verify --json")}.`
|
|
102
|
+
].join(" ")
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
writeResult(projectRoot, result);
|
|
106
|
+
updateVerificationState(projectRoot, "failed", startedFromFailed);
|
|
107
|
+
createRepairPacket(projectRoot, { verification: result });
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
14
111
|
if (commands.length === 0) {
|
|
15
112
|
const result = {
|
|
16
113
|
status: "not_configured",
|
|
@@ -31,6 +128,50 @@ export function runVerification(projectRoot, options) {
|
|
|
31
128
|
updateVerificationState(projectRoot, "not_configured", startedFromFailed);
|
|
32
129
|
return result;
|
|
33
130
|
}
|
|
131
|
+
const evidenceGate = checkVerificationEvidence(projectRoot);
|
|
132
|
+
if (evidenceGate.status === "failed") {
|
|
133
|
+
const relativeLogPath = `.frontend-harness/logs/evidence-gate-${new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}.log`;
|
|
134
|
+
writeText(path.join(projectRoot, relativeLogPath), [
|
|
135
|
+
"Verification evidence gate failed before project verification.",
|
|
136
|
+
"",
|
|
137
|
+
"## Evidence manifest",
|
|
138
|
+
evidenceGate.manifestPath,
|
|
139
|
+
"",
|
|
140
|
+
"## Required evidence",
|
|
141
|
+
...evidenceGate.requiredEvidence.map((evidence) => `- ${evidence.unitId}: ${evidence.kind}`),
|
|
142
|
+
"",
|
|
143
|
+
"## Errors",
|
|
144
|
+
...evidenceGate.errors.map((error) => `- ${error}`)
|
|
145
|
+
].join("\n"));
|
|
146
|
+
const result = {
|
|
147
|
+
status: "failed",
|
|
148
|
+
results: [
|
|
149
|
+
{
|
|
150
|
+
name: "evidence-gate",
|
|
151
|
+
command: "frontend-harness verify --json",
|
|
152
|
+
source: "none",
|
|
153
|
+
status: "failed",
|
|
154
|
+
exitCode: 1,
|
|
155
|
+
durationMs: 0,
|
|
156
|
+
logPath: relativeLogPath
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
retryGuidance: {
|
|
160
|
+
summary: "required verification evidence artifacts are missing or stale",
|
|
161
|
+
failedCommands: ["frontend-harness verify --json"],
|
|
162
|
+
logs: [relativeLogPath],
|
|
163
|
+
nextAgentInstruction: [
|
|
164
|
+
"Create .frontend-harness/verification/evidence.json with entries for every required unit evidence kind.",
|
|
165
|
+
"Each entry must include unitId, kind, artifactPaths, and a createdAt timestamp, and every artifact path must exist under .frontend-harness/.",
|
|
166
|
+
`Then rerun ${projectScriptCommand("frontend-harness verify --json")}.`
|
|
167
|
+
].join(" ")
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
writeResult(projectRoot, result);
|
|
171
|
+
updateVerificationState(projectRoot, "failed", startedFromFailed);
|
|
172
|
+
createRepairPacket(projectRoot, { verification: result });
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
34
175
|
const results = commands.map(({ name, source, command }) => runCommand(projectRoot, name, source, command));
|
|
35
176
|
const failed = results.filter((result) => result.status === "failed" || result.status === "error");
|
|
36
177
|
const status = failed.length > 0 ? "failed" : "passed";
|
|
@@ -45,8 +186,8 @@ export function runVerification(projectRoot, options) {
|
|
|
45
186
|
nextAgentInstruction: [
|
|
46
187
|
"Read .frontend-harness/verification/latest.json and the failed command logs.",
|
|
47
188
|
"Patch the relevant project files without changing generated harness artifacts.",
|
|
48
|
-
|
|
49
|
-
|
|
189
|
+
`Record newly changed files with ${projectScriptCommand("frontend-harness state record-change <file>")}.`,
|
|
190
|
+
`Rerun ${projectScriptCommand("frontend-harness verify --json")} until verification passes.`
|
|
50
191
|
].join(" ")
|
|
51
192
|
}
|
|
52
193
|
: null
|
|
@@ -58,6 +199,131 @@ export function runVerification(projectRoot, options) {
|
|
|
58
199
|
}
|
|
59
200
|
return result;
|
|
60
201
|
}
|
|
202
|
+
function checkVerificationEvidence(projectRoot) {
|
|
203
|
+
const state = loadState(projectRoot);
|
|
204
|
+
const manifestPath = ".frontend-harness/verification/evidence.json";
|
|
205
|
+
const requiredEvidence = state.units.flatMap((unit) => [...new Set(unit.verificationEvidence ?? [])]
|
|
206
|
+
.map(evidenceArtifactKind)
|
|
207
|
+
.filter((kind) => Boolean(kind))
|
|
208
|
+
.map((kind) => ({ unitId: unit.id, kind })));
|
|
209
|
+
if (requiredEvidence.length === 0) {
|
|
210
|
+
return {
|
|
211
|
+
status: "passed",
|
|
212
|
+
manifestPath,
|
|
213
|
+
requiredEvidence,
|
|
214
|
+
errors: []
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const manifest = readEvidenceManifest(projectRoot, manifestPath);
|
|
218
|
+
if (!manifest.ok) {
|
|
219
|
+
return {
|
|
220
|
+
status: "failed",
|
|
221
|
+
manifestPath,
|
|
222
|
+
requiredEvidence,
|
|
223
|
+
errors: [manifest.error]
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
const entries = manifest.entries;
|
|
227
|
+
const errors = validateEvidenceEntries(projectRoot, requiredEvidence, entries);
|
|
228
|
+
return {
|
|
229
|
+
status: errors.length ? "failed" : "passed",
|
|
230
|
+
manifestPath,
|
|
231
|
+
requiredEvidence,
|
|
232
|
+
errors
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function evidenceArtifactKind(evidence) {
|
|
236
|
+
const normalized = evidence.toLowerCase();
|
|
237
|
+
if (normalized.includes("component behavior")) {
|
|
238
|
+
return "component-behavior";
|
|
239
|
+
}
|
|
240
|
+
if (normalized.includes("interaction")) {
|
|
241
|
+
return "interaction-smoke";
|
|
242
|
+
}
|
|
243
|
+
if (normalized.includes("responsive")) {
|
|
244
|
+
return "responsive-rendering";
|
|
245
|
+
}
|
|
246
|
+
if (normalized.includes("visual")) {
|
|
247
|
+
return "visual-regression";
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
function readEvidenceManifest(projectRoot, relativePath) {
|
|
252
|
+
const fullPath = path.join(projectRoot, relativePath);
|
|
253
|
+
if (!fs.existsSync(fullPath)) {
|
|
254
|
+
return { ok: false, error: `Missing evidence manifest: ${relativePath}.` };
|
|
255
|
+
}
|
|
256
|
+
let parsed;
|
|
257
|
+
try {
|
|
258
|
+
parsed = readJson(fullPath);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
return { ok: false, error: `Evidence manifest is not valid JSON: ${error instanceof Error ? error.message : String(error)}.` };
|
|
262
|
+
}
|
|
263
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
264
|
+
return { ok: false, error: "Evidence manifest root must be an object." };
|
|
265
|
+
}
|
|
266
|
+
const entries = parsed["entries"];
|
|
267
|
+
if (!Array.isArray(entries)) {
|
|
268
|
+
return { ok: false, error: "Evidence manifest must contain an entries array." };
|
|
269
|
+
}
|
|
270
|
+
return { ok: true, entries: entries.flatMap(parseEvidenceEntry) };
|
|
271
|
+
}
|
|
272
|
+
function parseEvidenceEntry(value) {
|
|
273
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
const entry = value;
|
|
277
|
+
const unitId = entry["unitId"];
|
|
278
|
+
const kind = entry["kind"];
|
|
279
|
+
const artifactPaths = entry["artifactPaths"];
|
|
280
|
+
const createdAt = entry["createdAt"];
|
|
281
|
+
if (typeof unitId !== "string"
|
|
282
|
+
|| typeof kind !== "string"
|
|
283
|
+
|| !Array.isArray(artifactPaths)
|
|
284
|
+
|| artifactPaths.some((artifactPath) => typeof artifactPath !== "string")
|
|
285
|
+
|| typeof createdAt !== "string") {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
return [{
|
|
289
|
+
unitId,
|
|
290
|
+
kind,
|
|
291
|
+
artifactPaths: artifactPaths,
|
|
292
|
+
createdAt
|
|
293
|
+
}];
|
|
294
|
+
}
|
|
295
|
+
function validateEvidenceEntries(projectRoot, requiredEvidence, entries) {
|
|
296
|
+
const errors = [];
|
|
297
|
+
for (const required of requiredEvidence) {
|
|
298
|
+
const entry = entries.find((candidate) => candidate.unitId === required.unitId && candidate.kind === required.kind);
|
|
299
|
+
if (!entry) {
|
|
300
|
+
errors.push(`Missing evidence entry for ${required.unitId}: ${required.kind}.`);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (!/^\d{4}-\d{2}-\d{2}T/.test(entry.createdAt)) {
|
|
304
|
+
errors.push(`Evidence entry for ${required.unitId}: ${required.kind} must include an ISO createdAt timestamp.`);
|
|
305
|
+
}
|
|
306
|
+
if (entry.artifactPaths.length === 0) {
|
|
307
|
+
errors.push(`Evidence entry for ${required.unitId}: ${required.kind} must include at least one artifact path.`);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
for (const artifactPath of entry.artifactPaths) {
|
|
311
|
+
validateEvidenceArtifactPath(projectRoot, artifactPath, `${required.unitId}: ${required.kind}`, errors);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return errors;
|
|
315
|
+
}
|
|
316
|
+
function validateEvidenceArtifactPath(projectRoot, artifactPath, label, errors) {
|
|
317
|
+
const normalized = path.posix.normalize(artifactPath.replace(/\\/g, "/"));
|
|
318
|
+
if (!normalized.startsWith(".frontend-harness/") || normalized.includes("..") || path.isAbsolute(artifactPath)) {
|
|
319
|
+
errors.push(`Evidence artifact for ${label} must be a project-relative path under .frontend-harness/: ${artifactPath}.`);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const fullPath = path.join(projectRoot, normalized);
|
|
323
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
|
|
324
|
+
errors.push(`Evidence artifact for ${label} does not exist: ${artifactPath}.`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
61
327
|
function resolveCommands(projectRoot, options) {
|
|
62
328
|
return discoverVerificationCommands(projectRoot, options)
|
|
63
329
|
.flatMap((entry) => entry.command ? [{ name: entry.name, source: entry.source, command: entry.command }] : []);
|