@tea-agent/loop-agent 0.37.0 → 0.37.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -6
- package/CHANGELOG.md +71 -144
- package/README.md +1 -1
- package/bin/loop-agent.js +1 -37
- package/dist/application/task-lifecycle/advance.js +0 -1
- package/dist/application/task-lifecycle/observe.js +0 -15
- package/dist/application/task-lifecycle/plan-transitions.js +0 -15
- package/dist/cli/command-definitions.js +0 -7
- package/dist/cli/program.js +3 -8
- package/dist/commands/init-upgrade.js +19 -351
- package/dist/commands/init.js +67 -14
- package/dist/commands/run-dag-progress.js +0 -14
- package/dist/commands/task-advance.js +3 -33
- package/dist/executors/dag-pi-executor.js +30 -47
- package/dist/executors/pi-sdk-executor.js +0 -34
- package/dist/executors/shell-executor.js +95 -6
- package/dist/shared/operator/capabilities.js +13 -200
- package/dist/shared/package-metadata.js +0 -42
- package/dist/task/contract/project.js +0 -4
- package/dist/task/source-prepare/completeness.js +0 -53
- package/dist/task/source-prepare/parse-intent.js +12 -82
- package/dist/task/source-prepare/prepare.js +5 -102
- package/dist/task/source-prepare/semantic-intake.js +23 -144
- package/dist/worker/console/chat/artifact-card.js +1 -8
- package/dist/worker/console/chat/chat-event-store.js +0 -152
- package/dist/worker/console/chat/human-gate-card.js +1 -9
- package/dist/worker/console/chat/operation-card.js +2 -71
- package/dist/worker/console/chat/pi-runtime.js +62 -111
- package/dist/worker/console/chat/routes.js +10 -43
- package/dist/worker/console/chat/session-store.js +1 -9
- package/dist/worker/console/chat/shortcuts.js +7 -9
- package/dist/worker/console/chat/turn-process.js +23 -204
- package/dist/worker/console/chat/workspace-landing.js +1 -2
- package/dist/worker/console/operation-runner.js +6 -155
- package/dist/worker/console/operator-actions.js +14 -391
- package/dist/worker/console/operator-user-error.js +0 -4
- package/dist/worker/console/recovery-cta.js +3 -50
- package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
- package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -13
- package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
- package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
- package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
- package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
- package/dist/worker/loop-agent/loop-agent-client.js +3 -17
- package/dist/worker/observability/read-model.js +0 -20
- package/dist/worker/observe/spec-evidence.js +8 -3
- package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
- package/dist/worker/observe/static/operator-chrome.js +3 -10
- package/dist/worker/observe/static/views/dag-inspector.js +71 -6
- package/dist/worker/observe/static/views/dag.js +0 -12
- package/dist/worker/preflight.js +1 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
- package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
- package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
- package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
- package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
- package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
- package/dist/workflows/dag/frontend-repair.js +18 -219
- package/dist/workflows/dag/frontend-verification-trace.js +32 -47
- package/dist/workflows/dag/init-hybrid.js +82 -165
- package/dist/workflows/dag/node-execution.js +0 -89
- package/dist/workflows/dag/recovery-recommendation.js +0 -58
- package/dist/workflows/dag/runner.js +15 -333
- package/dist/workflows/dag/scheduler.js +3 -257
- package/dist/workflows/dag/types.js +9 -130
- package/docs/README.md +3 -3
- package/docs/architecture/evolution.md +67 -102
- package/docs/templates/backend-test-dag.json +50 -32
- package/docs/templates/init-managed-agents.md +2 -5
- package/harness.json +2 -2
- package/package.json +3 -9
- package/skills/loop-agent/SKILL.md +0 -1
- package/skills/loop-agent/references/command-reference.md +0 -4
- package/dist/build-stamp.json +0 -6
- package/dist/commands/dag-request-interrupt.js +0 -20
- package/dist/worker/console/chat/assistant-content.js +0 -121
- package/dist/worker/console/chat/semantic-activity.js +0 -477
- package/dist/worker/console/operation-run-facts.js +0 -190
- package/dist/worker/console/operation-wait.js +0 -355
- package/dist/worker/console/recovery-error-copy.js +0 -198
- package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
- package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
- package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
- package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
- package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
- package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
- package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
- package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
- package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
- package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
- package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
- package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
- package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
- package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
- package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
- package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
- package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
- package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
- package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
- package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
- package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
- package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
- package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
- package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
- package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
- package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
- package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
- package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
- package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
- package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
- package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
- package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
- package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
- package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
- package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
- package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
- package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
- package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
- package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
- package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
- package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
- package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
- package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
- package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
- package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
- package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
- package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
- package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
- package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
- package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
- package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
- package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
- package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
- package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
- package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
- package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
- package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
- package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
- package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
- package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
- package/dist/worker/observability/interrupt-eligibility.js +0 -264
- package/dist/workflows/dag/contract-output-registry.js +0 -14
- package/dist/workflows/dag/contract-validator-registrations.js +0 -8
- package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
- package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
- package/dist/workflows/dag/frontend-recovery-run.js +0 -539
- package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
- package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
- package/dist/workflows/dag/interrupt-request.js +0 -559
|
@@ -20,14 +20,6 @@ export const frontendRepairFailureClassSchema = z.enum([
|
|
|
20
20
|
"spec-unclear",
|
|
21
21
|
"unknown",
|
|
22
22
|
]);
|
|
23
|
-
export const frontendCommandCategorySchema = z.enum([
|
|
24
|
-
"typecheck",
|
|
25
|
-
"build",
|
|
26
|
-
"lint",
|
|
27
|
-
"component-test",
|
|
28
|
-
"unit-test",
|
|
29
|
-
"trace",
|
|
30
|
-
]);
|
|
31
23
|
const REPAIRABLE = new Set([
|
|
32
24
|
"typecheck",
|
|
33
25
|
"build",
|
|
@@ -35,72 +27,12 @@ const REPAIRABLE = new Set([
|
|
|
35
27
|
"component-test",
|
|
36
28
|
"unit-test",
|
|
37
29
|
"trace",
|
|
30
|
+
"review",
|
|
38
31
|
]);
|
|
39
32
|
export function isFrontendRepairable(failureClass) {
|
|
40
33
|
return REPAIRABLE.has(failureClass);
|
|
41
34
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Deterministic commandLabel → commandCategory mapping (AC-3).
|
|
44
|
-
* `targetType` comes from the contract verificationTargets[].type
|
|
45
|
-
* (static | unit | component | integration | mock); the literal commandLabel
|
|
46
|
-
* wins over type so a `static` typecheck target is never misread as a test.
|
|
47
|
-
*/
|
|
48
|
-
export function mapCommandLabelToCategory(commandLabel, targetType) {
|
|
49
|
-
const label = commandLabel.toLowerCase();
|
|
50
|
-
if (/(?:typecheck|check-types|type-check|\btsc\b)/.test(label))
|
|
51
|
-
return "typecheck";
|
|
52
|
-
if (/(?:vite\s+build|next\s+build|webpack|rollup|\bbuild\b)/.test(label))
|
|
53
|
-
return "build";
|
|
54
|
-
if (/(?:eslint|\blint\b)/.test(label))
|
|
55
|
-
return "lint";
|
|
56
|
-
if (targetType === "component")
|
|
57
|
-
return "component-test";
|
|
58
|
-
if (targetType === "unit")
|
|
59
|
-
return "unit-test";
|
|
60
|
-
if (/(?:component|testing-library|render\()/.test(label))
|
|
61
|
-
return "component-test";
|
|
62
|
-
return "unit-test";
|
|
63
|
-
}
|
|
64
|
-
export function isReviewRepairable(finding, implementWriteSet) {
|
|
65
|
-
const hasFile = Boolean(finding.file && finding.file.trim().length > 0);
|
|
66
|
-
const hasLocation = Boolean((finding.line !== undefined && finding.line > 0) ||
|
|
67
|
-
(finding.symbol && finding.symbol.trim().length > 0));
|
|
68
|
-
const hasRequirement = Boolean((finding.requirementId && finding.requirementId.trim().length > 0) ||
|
|
69
|
-
(finding.acceptanceCriteriaId &&
|
|
70
|
-
finding.acceptanceCriteriaId.trim().length > 0));
|
|
71
|
-
const fixScope = finding.fixScope ?? (hasFile ? [finding.file] : []);
|
|
72
|
-
const fixScopeSubset = fixScope.length > 0 &&
|
|
73
|
-
fixScope.every((entry) => isPathWithinWriteSet(entry, implementWriteSet));
|
|
74
|
-
return (hasFile &&
|
|
75
|
-
hasLocation &&
|
|
76
|
-
hasRequirement &&
|
|
77
|
-
fixScopeSubset &&
|
|
78
|
-
finding.requiresContractReplan !== true);
|
|
79
|
-
}
|
|
80
35
|
export function classifyFrontendFailure(input) {
|
|
81
|
-
// AC-2: structured evidence takes priority over string-blob heuristics.
|
|
82
|
-
if (input.failureCategory === "write-guard") {
|
|
83
|
-
return "path";
|
|
84
|
-
}
|
|
85
|
-
if (input.traceTarget || input.commandCategory === "trace") {
|
|
86
|
-
return "trace";
|
|
87
|
-
}
|
|
88
|
-
if (input.commandCategory) {
|
|
89
|
-
switch (input.commandCategory) {
|
|
90
|
-
case "typecheck":
|
|
91
|
-
return "typecheck";
|
|
92
|
-
case "build":
|
|
93
|
-
return "build";
|
|
94
|
-
case "lint":
|
|
95
|
-
return "lint";
|
|
96
|
-
case "component-test":
|
|
97
|
-
return "component-test";
|
|
98
|
-
case "unit-test":
|
|
99
|
-
return "unit-test";
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
// Fallback: string-blob heuristic, preserved in its original branch order so
|
|
103
|
-
// legacy assertions (error TS2304 → typecheck, write-guard → path, etc.) stay green.
|
|
104
36
|
const blob = `${input.nodeId}\n${input.failureCategory ?? ""}\n${input.stdout ?? ""}\n${input.stderr ?? ""}`.toLowerCase();
|
|
105
37
|
if (input.nodeId.includes("contract") ||
|
|
106
38
|
blob.includes("contract mismatch") ||
|
|
@@ -188,23 +120,6 @@ export const frontendRepairAssessmentSchema = z
|
|
|
188
120
|
allowedRepairWriteSet: z.array(z.string().min(1)),
|
|
189
121
|
evidenceRefs: z.array(z.string().min(1)),
|
|
190
122
|
browserStatus: z.literal("not-run"),
|
|
191
|
-
// AC-5 structured evidence fields (all optional so existing minimal
|
|
192
|
-
// fixtures and frontend-review-context safeParse stay compatible).
|
|
193
|
-
verificationTargetId: z.string().min(1).optional(),
|
|
194
|
-
commandLabel: z.string().min(1).optional(),
|
|
195
|
-
exitCode: z.number().int().optional(),
|
|
196
|
-
commandCategory: frontendCommandCategorySchema.optional(),
|
|
197
|
-
requirementIds: z.array(z.string().min(1)).optional(),
|
|
198
|
-
acceptanceCriteriaIds: z.array(z.string().min(1)).optional(),
|
|
199
|
-
fixScope: z.array(z.string().min(1)).optional(),
|
|
200
|
-
changedPaths: z.array(z.string().min(1)).optional(),
|
|
201
|
-
traceRef: z
|
|
202
|
-
.object({
|
|
203
|
-
relativePath: z.string().min(1),
|
|
204
|
-
schemaId: z.string().min(1),
|
|
205
|
-
})
|
|
206
|
-
.strict()
|
|
207
|
-
.optional(),
|
|
208
123
|
})
|
|
209
124
|
.strict();
|
|
210
125
|
function nodeHadFailure(record) {
|
|
@@ -220,24 +135,19 @@ function nodeHadFailure(record) {
|
|
|
220
135
|
function normalize(value) {
|
|
221
136
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
222
137
|
}
|
|
223
|
-
/**
|
|
224
|
-
* Whether a concrete path or glob entry is contained by the implement writeSet.
|
|
225
|
-
* Exact glob equality is the norm (repair writeSet === implement writeSet);
|
|
226
|
-
* concrete (non-glob) paths may also be covered by an implement glob.
|
|
227
|
-
*/
|
|
228
|
-
export function isPathWithinWriteSet(entry, implementWriteSet) {
|
|
229
|
-
const normalizedEntry = normalize(entry);
|
|
230
|
-
const normalizedImplement = implementWriteSet.map(normalize);
|
|
231
|
-
if (normalizedImplement.includes(normalizedEntry))
|
|
232
|
-
return true;
|
|
233
|
-
if (!normalizedEntry.includes("*")) {
|
|
234
|
-
return implementWriteSet.some((pattern) => pathMatchesPattern(normalizedEntry, pattern));
|
|
235
|
-
}
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
138
|
export function assertWriteSetSubset(repairWriteSet, implementWriteSet) {
|
|
239
139
|
for (const entry of repairWriteSet) {
|
|
240
|
-
|
|
140
|
+
const ok = implementWriteSet.some((pattern) => normalize(entry) === normalize(pattern) ||
|
|
141
|
+
pathMatchesPattern(normalize(entry), pattern) ||
|
|
142
|
+
pathMatchesPattern(normalize(pattern), entry));
|
|
143
|
+
// exact equality preferred for generation-time copy
|
|
144
|
+
if (!implementWriteSet.map(normalize).includes(normalize(entry))) {
|
|
145
|
+
// allow identical globs only
|
|
146
|
+
if (!ok || normalize(entry) !== normalize(entry)) {
|
|
147
|
+
// require exact membership for exclusive writeSet entries
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!implementWriteSet.map(normalize).includes(normalize(entry))) {
|
|
241
151
|
throw new Error(`repair writeSet entry "${entry}" is not ⊆ implement writeSet`);
|
|
242
152
|
}
|
|
243
153
|
}
|
|
@@ -260,63 +170,6 @@ async function loadImplementWriteSet(runDir, fallback) {
|
|
|
260
170
|
}
|
|
261
171
|
return fallback;
|
|
262
172
|
}
|
|
263
|
-
function matchVerificationTarget(contract, commandLabel, command) {
|
|
264
|
-
for (const target of contract.verificationTargets) {
|
|
265
|
-
if (commandLabel && target.commandLabel === commandLabel)
|
|
266
|
-
return target;
|
|
267
|
-
if (command &&
|
|
268
|
-
(command.includes(target.commandLabel) ||
|
|
269
|
-
target.commandLabel.includes(command))) {
|
|
270
|
-
return target;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return undefined;
|
|
274
|
-
}
|
|
275
|
-
function deriveFailureEvidence(input) {
|
|
276
|
-
const evidence = {};
|
|
277
|
-
for (const { record } of input.failed) {
|
|
278
|
-
const results = record.commandResults ?? [];
|
|
279
|
-
const failedResult = results.find((item) => item.ok === false) ?? results[0];
|
|
280
|
-
if (failedResult &&
|
|
281
|
-
evidence.exitCode === undefined &&
|
|
282
|
-
typeof failedResult.exitCode === "number") {
|
|
283
|
-
evidence.exitCode = failedResult.exitCode;
|
|
284
|
-
}
|
|
285
|
-
if (failedResult?.commandLabel && !evidence.commandLabel) {
|
|
286
|
-
evidence.commandLabel = failedResult.commandLabel;
|
|
287
|
-
}
|
|
288
|
-
// Structured trace facts passed by the verification bundle / trace gate.
|
|
289
|
-
const failedTraceTarget = (record.traceTargets ?? []).find((target) => target.status === "failed");
|
|
290
|
-
if (failedTraceTarget && !evidence.verificationTargetId) {
|
|
291
|
-
evidence.verificationTargetId = failedTraceTarget.id;
|
|
292
|
-
if (!evidence.commandLabel)
|
|
293
|
-
evidence.commandLabel = failedTraceTarget.commandLabel;
|
|
294
|
-
evidence.traceTarget = failedTraceTarget;
|
|
295
|
-
evidence.traceRef = {
|
|
296
|
-
relativePath: "contracts/frontend-verification-trace.json",
|
|
297
|
-
schemaId: "frontend-verification-trace-v1",
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
if (record.changedPaths?.length && !evidence.changedPaths) {
|
|
301
|
-
evidence.changedPaths = [...record.changedPaths];
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
// Resolve commandLabel → contract verification target → category + scope.
|
|
305
|
-
const target = matchVerificationTarget(input.contract, evidence.commandLabel);
|
|
306
|
-
if (target) {
|
|
307
|
-
if (!evidence.verificationTargetId)
|
|
308
|
-
evidence.verificationTargetId = target.id;
|
|
309
|
-
if (!evidence.commandLabel)
|
|
310
|
-
evidence.commandLabel = target.commandLabel;
|
|
311
|
-
evidence.requirementIds ??= [...target.requirementIds];
|
|
312
|
-
evidence.acceptanceCriteriaIds ??= [...target.requirementIds];
|
|
313
|
-
evidence.fixScope ??= [target.file];
|
|
314
|
-
}
|
|
315
|
-
if (evidence.commandLabel) {
|
|
316
|
-
evidence.commandCategory ??= mapCommandLabelToCategory(evidence.commandLabel, target?.type);
|
|
317
|
-
}
|
|
318
|
-
return evidence;
|
|
319
|
-
}
|
|
320
173
|
export async function runFrontendFailureAssessGate(input) {
|
|
321
174
|
const attempt = input.attempt ?? 1;
|
|
322
175
|
const implementWriteSet = await loadImplementWriteSet(input.runDir, input.implementWriteSet ?? []);
|
|
@@ -326,14 +179,12 @@ export async function runFrontendFailureAssessGate(input) {
|
|
|
326
179
|
const contractRel = "contracts/frontend-implementation-contract.json";
|
|
327
180
|
const contractPath = path.join(input.runDir, contractRel);
|
|
328
181
|
let contractSchemaId = FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID;
|
|
329
|
-
let contract;
|
|
330
182
|
try {
|
|
331
183
|
const raw = JSON.parse(await readFile(contractPath, "utf8"));
|
|
332
184
|
const parsed = frontendImplementationContractSchema.safeParse(raw);
|
|
333
185
|
if (!parsed.success) {
|
|
334
186
|
throw new Error("invalid frontend implementation contract");
|
|
335
187
|
}
|
|
336
|
-
contract = parsed.data;
|
|
337
188
|
contractSchemaId = FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID;
|
|
338
189
|
}
|
|
339
190
|
catch {
|
|
@@ -367,6 +218,7 @@ export async function runFrontendFailureAssessGate(input) {
|
|
|
367
218
|
// missing optional nodes ok
|
|
368
219
|
}
|
|
369
220
|
}
|
|
221
|
+
assertWriteSetSubset(implementWriteSet, implementWriteSet);
|
|
370
222
|
if (failed.length === 0) {
|
|
371
223
|
const assessment = {
|
|
372
224
|
schemaVersion: 1,
|
|
@@ -389,49 +241,16 @@ export async function runFrontendFailureAssessGate(input) {
|
|
|
389
241
|
return assessment;
|
|
390
242
|
}
|
|
391
243
|
const primary = failed[0];
|
|
392
|
-
const evidence = deriveFailureEvidence({ contract, failed });
|
|
393
244
|
const failureClass = classifyFrontendFailure({
|
|
394
245
|
nodeId: primary.nodeId,
|
|
395
246
|
failureCategory: primary.record.failureCategory,
|
|
396
247
|
stdout: primary.record.stdout,
|
|
397
248
|
stderr: primary.record.stderr,
|
|
398
|
-
commandLabel: evidence.commandLabel,
|
|
399
|
-
commandCategory: evidence.commandCategory,
|
|
400
|
-
exitCode: evidence.exitCode,
|
|
401
|
-
traceTarget: evidence.traceTarget ?? null,
|
|
402
|
-
changedPaths: evidence.changedPaths,
|
|
403
249
|
});
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
let eligible;
|
|
409
|
-
let reason;
|
|
410
|
-
if (failureClass === "review") {
|
|
411
|
-
const finding = primary.record.reviewFinding ?? {};
|
|
412
|
-
eligible = attempt <= 1 && isReviewRepairable(finding, implementWriteSet);
|
|
413
|
-
reason = eligible
|
|
414
|
-
? `repairable review finding on ${primary.nodeId}`
|
|
415
|
-
: `review finding lacks file/line-symbol/requirement evidence or is outside writeSet on ${primary.nodeId}`;
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
const hasStructuredEvidence = evidence.exitCode !== undefined ||
|
|
419
|
-
evidence.commandCategory !== undefined ||
|
|
420
|
-
Boolean(evidence.verificationTargetId);
|
|
421
|
-
eligible = baseEligible && hasStructuredEvidence;
|
|
422
|
-
reason = eligible
|
|
423
|
-
? `repairable failureClass=${failureClass} on ${primary.nodeId}`
|
|
424
|
-
: !baseEligible
|
|
425
|
-
? `non-repairable or ineligible: failureClass=${failureClass} attempt=${attempt}`
|
|
426
|
-
: `missing structured evidence for failureClass=${failureClass} on ${primary.nodeId}`;
|
|
427
|
-
}
|
|
428
|
-
// fixScope and changedPaths must stay inside the implement writeSet.
|
|
429
|
-
if (evidence.fixScope?.length) {
|
|
430
|
-
assertWriteSetSubset(evidence.fixScope, implementWriteSet);
|
|
431
|
-
}
|
|
432
|
-
if (evidence.changedPaths?.length) {
|
|
433
|
-
assertWriteSetSubset(evidence.changedPaths, implementWriteSet);
|
|
434
|
-
}
|
|
250
|
+
const eligible = isFrontendRepairable(failureClass) && attempt <= 1;
|
|
251
|
+
const reason = eligible
|
|
252
|
+
? `repairable failureClass=${failureClass} on ${primary.nodeId}`
|
|
253
|
+
: `non-repairable or ineligible: failureClass=${failureClass} attempt=${attempt}`;
|
|
435
254
|
const assessment = {
|
|
436
255
|
schemaVersion: 1,
|
|
437
256
|
schemaId: FRONTEND_REPAIR_ASSESSMENT_SCHEMA_ID,
|
|
@@ -448,25 +267,6 @@ export async function runFrontendFailureAssessGate(input) {
|
|
|
448
267
|
allowedRepairWriteSet: [...implementWriteSet],
|
|
449
268
|
evidenceRefs: [contractRel, ...failed.map((item) => `${item.nodeId}.json`)],
|
|
450
269
|
browserStatus: "not-run",
|
|
451
|
-
...(evidence.verificationTargetId
|
|
452
|
-
? { verificationTargetId: evidence.verificationTargetId }
|
|
453
|
-
: {}),
|
|
454
|
-
...(evidence.commandLabel ? { commandLabel: evidence.commandLabel } : {}),
|
|
455
|
-
...(evidence.exitCode !== undefined ? { exitCode: evidence.exitCode } : {}),
|
|
456
|
-
...(evidence.commandCategory
|
|
457
|
-
? { commandCategory: evidence.commandCategory }
|
|
458
|
-
: {}),
|
|
459
|
-
...(evidence.requirementIds?.length
|
|
460
|
-
? { requirementIds: evidence.requirementIds }
|
|
461
|
-
: {}),
|
|
462
|
-
...(evidence.acceptanceCriteriaIds?.length
|
|
463
|
-
? { acceptanceCriteriaIds: evidence.acceptanceCriteriaIds }
|
|
464
|
-
: {}),
|
|
465
|
-
...(evidence.fixScope?.length ? { fixScope: evidence.fixScope } : {}),
|
|
466
|
-
...(evidence.changedPaths?.length
|
|
467
|
-
? { changedPaths: evidence.changedPaths }
|
|
468
|
-
: {}),
|
|
469
|
-
...(evidence.traceRef ? { traceRef: evidence.traceRef } : {}),
|
|
470
270
|
};
|
|
471
271
|
await writeAssessment(input.runDir, assessment);
|
|
472
272
|
return assessment;
|
|
@@ -522,8 +322,7 @@ export async function runFrontendRepairContractGate(input) {
|
|
|
522
322
|
if (!assessment.eligible) {
|
|
523
323
|
throw new Error(`repair-contract: non-repairable failure (${assessment.failureClass}): ${assessment.reason}`);
|
|
524
324
|
}
|
|
525
|
-
if (!isFrontendRepairable(assessment.failureClass)
|
|
526
|
-
assessment.failureClass !== "review") {
|
|
325
|
+
if (!isFrontendRepairable(assessment.failureClass)) {
|
|
527
326
|
throw new Error(`repair-contract: failureClass ${assessment.failureClass} is not repairable`);
|
|
528
327
|
}
|
|
529
328
|
return { ok: true, assessment };
|
|
@@ -40,48 +40,6 @@ function symbolEvidenceCandidates(symbol) {
|
|
|
40
40
|
function isConfigurationVerificationFile(file) {
|
|
41
41
|
return /(?:^|\/)(?:package\.json|tsconfig(?:\.[^/]+)?\.json|(?:vite|webpack|rollup|docusaurus)\.config\.[cm]?[jt]s)$/.test(file.replace(/\\/g, "/"));
|
|
42
42
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Pure verification-target evaluation shared by the trace gate and the repair
|
|
45
|
-
* assess gate. It derives, for each contract verification target, whether the
|
|
46
|
-
* target bound to frozen evidence (commandLabel owners), and whether the file
|
|
47
|
-
* and optional symbol exist in the workspace. It performs no writes and throws
|
|
48
|
-
* nothing: failures are returned as `hardIssues` plus per-target `status`.
|
|
49
|
-
*/
|
|
50
|
-
export async function evaluateVerificationTargets(input) {
|
|
51
|
-
const targets = [];
|
|
52
|
-
const hardIssues = [];
|
|
53
|
-
for (const target of input.contract.verificationTargets) {
|
|
54
|
-
const issues = [];
|
|
55
|
-
const owners = input.labelOwners.get(target.commandLabel);
|
|
56
|
-
if (!owners?.length) {
|
|
57
|
-
issues.push(`commandLabel not executed in current-run frozen evidence: ${target.commandLabel}`);
|
|
58
|
-
}
|
|
59
|
-
const fileIssues = await assertFileAndSymbol({
|
|
60
|
-
workspaceRoot: input.workspaceRoot,
|
|
61
|
-
file: target.file,
|
|
62
|
-
// Configuration files prove that the command's entrypoint exists;
|
|
63
|
-
// they do not expose source symbols. LLMs sometimes derive a
|
|
64
|
-
// filename fragment such as "build" from tsconfig.build.json.
|
|
65
|
-
symbol: isConfigurationVerificationFile(target.file)
|
|
66
|
-
? undefined
|
|
67
|
-
: target.symbol,
|
|
68
|
-
});
|
|
69
|
-
issues.push(...fileIssues);
|
|
70
|
-
const status = issues.length ? "failed" : "ok";
|
|
71
|
-
if (issues.length)
|
|
72
|
-
hardIssues.push(...issues.map((i) => `${target.id}: ${i}`));
|
|
73
|
-
targets.push({
|
|
74
|
-
id: target.id,
|
|
75
|
-
commandLabel: target.commandLabel,
|
|
76
|
-
file: target.file,
|
|
77
|
-
symbol: target.symbol,
|
|
78
|
-
status,
|
|
79
|
-
matchedNodeIds: owners ?? [],
|
|
80
|
-
issues,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
return { targets, hardIssues };
|
|
84
|
-
}
|
|
85
43
|
async function assertFileAndSymbol(input) {
|
|
86
44
|
const issues = [];
|
|
87
45
|
const absolute = path.resolve(input.workspaceRoot, input.file);
|
|
@@ -217,11 +175,38 @@ export async function runFrontendVerificationTraceGate(input) {
|
|
|
217
175
|
}
|
|
218
176
|
}
|
|
219
177
|
}
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
178
|
+
const targets = [];
|
|
179
|
+
const hardIssues = [];
|
|
180
|
+
for (const target of contract.verificationTargets) {
|
|
181
|
+
const issues = [];
|
|
182
|
+
const owners = labelOwners.get(target.commandLabel);
|
|
183
|
+
if (!owners?.length) {
|
|
184
|
+
issues.push(`commandLabel not executed in current-run frozen evidence: ${target.commandLabel}`);
|
|
185
|
+
}
|
|
186
|
+
const fileIssues = await assertFileAndSymbol({
|
|
187
|
+
workspaceRoot: input.workspaceRoot,
|
|
188
|
+
file: target.file,
|
|
189
|
+
// Configuration files prove that the command's entrypoint exists;
|
|
190
|
+
// they do not expose source symbols. LLMs sometimes derive a
|
|
191
|
+
// filename fragment such as "build" from tsconfig.build.json.
|
|
192
|
+
symbol: isConfigurationVerificationFile(target.file)
|
|
193
|
+
? undefined
|
|
194
|
+
: target.symbol,
|
|
195
|
+
});
|
|
196
|
+
issues.push(...fileIssues);
|
|
197
|
+
const status = issues.length ? "failed" : "ok";
|
|
198
|
+
if (issues.length)
|
|
199
|
+
hardIssues.push(...issues.map((i) => `${target.id}: ${i}`));
|
|
200
|
+
targets.push({
|
|
201
|
+
id: target.id,
|
|
202
|
+
commandLabel: target.commandLabel,
|
|
203
|
+
file: target.file,
|
|
204
|
+
symbol: target.symbol,
|
|
205
|
+
status,
|
|
206
|
+
matchedNodeIds: owners ?? [],
|
|
207
|
+
issues,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
225
210
|
if (hardIssues.length) {
|
|
226
211
|
throw new Error(`trace: ${hardIssues.join("; ")}`);
|
|
227
212
|
}
|