@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.
Files changed (192) hide show
  1. package/AGENTS.md +2 -6
  2. package/CHANGELOG.md +71 -144
  3. package/README.md +1 -1
  4. package/bin/loop-agent.js +1 -37
  5. package/dist/application/task-lifecycle/advance.js +0 -1
  6. package/dist/application/task-lifecycle/observe.js +0 -15
  7. package/dist/application/task-lifecycle/plan-transitions.js +0 -15
  8. package/dist/cli/command-definitions.js +0 -7
  9. package/dist/cli/program.js +3 -8
  10. package/dist/commands/init-upgrade.js +19 -351
  11. package/dist/commands/init.js +67 -14
  12. package/dist/commands/run-dag-progress.js +0 -14
  13. package/dist/commands/task-advance.js +3 -33
  14. package/dist/executors/dag-pi-executor.js +30 -47
  15. package/dist/executors/pi-sdk-executor.js +0 -34
  16. package/dist/executors/shell-executor.js +95 -6
  17. package/dist/shared/operator/capabilities.js +13 -200
  18. package/dist/shared/package-metadata.js +0 -42
  19. package/dist/task/contract/project.js +0 -4
  20. package/dist/task/source-prepare/completeness.js +0 -53
  21. package/dist/task/source-prepare/parse-intent.js +12 -82
  22. package/dist/task/source-prepare/prepare.js +5 -102
  23. package/dist/task/source-prepare/semantic-intake.js +23 -144
  24. package/dist/worker/console/chat/artifact-card.js +1 -8
  25. package/dist/worker/console/chat/chat-event-store.js +0 -152
  26. package/dist/worker/console/chat/human-gate-card.js +1 -9
  27. package/dist/worker/console/chat/operation-card.js +2 -71
  28. package/dist/worker/console/chat/pi-runtime.js +62 -111
  29. package/dist/worker/console/chat/routes.js +10 -43
  30. package/dist/worker/console/chat/session-store.js +1 -9
  31. package/dist/worker/console/chat/shortcuts.js +7 -9
  32. package/dist/worker/console/chat/turn-process.js +23 -204
  33. package/dist/worker/console/chat/workspace-landing.js +1 -2
  34. package/dist/worker/console/operation-runner.js +6 -155
  35. package/dist/worker/console/operator-actions.js +14 -391
  36. package/dist/worker/console/operator-user-error.js +0 -4
  37. package/dist/worker/console/recovery-cta.js +3 -50
  38. package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
  39. package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
  40. package/dist/worker/console/static/index.html +2 -2
  41. package/dist/worker/console/static-src/app/console-types.js +9 -13
  42. package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
  43. package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
  44. package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
  45. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
  46. package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
  47. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
  48. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
  49. package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
  50. package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
  51. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
  52. package/dist/worker/loop-agent/loop-agent-client.js +3 -17
  53. package/dist/worker/observability/read-model.js +0 -20
  54. package/dist/worker/observe/spec-evidence.js +8 -3
  55. package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
  56. package/dist/worker/observe/static/operator-chrome.js +3 -10
  57. package/dist/worker/observe/static/views/dag-inspector.js +71 -6
  58. package/dist/worker/observe/static/views/dag.js +0 -12
  59. package/dist/worker/preflight.js +1 -2
  60. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
  61. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  62. package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
  63. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  64. package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
  65. package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
  66. package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
  67. package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
  68. package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
  69. package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
  70. package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
  71. package/dist/workflows/dag/frontend-repair.js +18 -219
  72. package/dist/workflows/dag/frontend-verification-trace.js +32 -47
  73. package/dist/workflows/dag/init-hybrid.js +82 -165
  74. package/dist/workflows/dag/node-execution.js +0 -89
  75. package/dist/workflows/dag/recovery-recommendation.js +0 -58
  76. package/dist/workflows/dag/runner.js +15 -333
  77. package/dist/workflows/dag/scheduler.js +3 -257
  78. package/dist/workflows/dag/types.js +9 -130
  79. package/docs/README.md +3 -3
  80. package/docs/architecture/evolution.md +67 -102
  81. package/docs/templates/backend-test-dag.json +50 -32
  82. package/docs/templates/init-managed-agents.md +2 -5
  83. package/harness.json +2 -2
  84. package/package.json +3 -9
  85. package/skills/loop-agent/SKILL.md +0 -1
  86. package/skills/loop-agent/references/command-reference.md +0 -4
  87. package/dist/build-stamp.json +0 -6
  88. package/dist/commands/dag-request-interrupt.js +0 -20
  89. package/dist/worker/console/chat/assistant-content.js +0 -121
  90. package/dist/worker/console/chat/semantic-activity.js +0 -477
  91. package/dist/worker/console/operation-run-facts.js +0 -190
  92. package/dist/worker/console/operation-wait.js +0 -355
  93. package/dist/worker/console/recovery-error-copy.js +0 -198
  94. package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
  95. package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
  96. package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
  97. package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
  98. package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
  99. package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
  100. package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
  101. package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
  102. package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
  103. package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
  104. package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
  105. package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
  106. package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
  107. package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
  108. package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
  109. package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
  110. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
  111. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
  112. package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
  113. package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
  114. package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
  115. package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
  116. package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
  117. package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
  118. package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
  119. package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
  120. package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
  121. package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
  122. package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
  123. package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
  124. package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
  125. package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
  126. package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
  127. package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
  128. package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
  129. package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
  130. package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
  131. package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
  132. package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
  133. package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
  134. package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
  135. package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
  136. package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
  137. package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
  138. package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
  139. package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
  140. package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
  141. package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
  142. package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
  143. package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
  144. package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
  145. package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
  146. package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
  147. package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
  148. package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
  149. package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
  150. package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
  151. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
  152. package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
  153. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
  154. package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
  155. package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
  156. package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
  157. package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
  158. package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
  159. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
  160. package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
  161. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
  162. package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
  163. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
  164. package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
  165. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
  166. package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
  167. package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
  168. package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
  169. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
  170. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
  171. package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
  172. package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
  173. package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
  174. package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
  175. package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
  176. package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
  177. package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
  178. package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
  179. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
  180. package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
  181. package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
  182. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
  183. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
  184. package/dist/worker/observability/interrupt-eligibility.js +0 -264
  185. package/dist/workflows/dag/contract-output-registry.js +0 -14
  186. package/dist/workflows/dag/contract-validator-registrations.js +0 -8
  187. package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
  188. package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
  189. package/dist/workflows/dag/frontend-recovery-run.js +0 -539
  190. package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
  191. package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
  192. 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
- if (!isPathWithinWriteSet(entry, implementWriteSet)) {
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
- // AC-5: repairable=true requires structured evidence (a resolved exitCode,
405
- // commandCategory, or trace-bound verification target). review has its own
406
- // stricter gate (AC-4) and is never unconditionally repairable.
407
- const baseEligible = isFrontendRepairable(failureClass) && attempt <= 1;
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 { targets, hardIssues } = await evaluateVerificationTargets({
221
- contract,
222
- labelOwners,
223
- workspaceRoot: input.workspaceRoot,
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
  }