@tea-agent/loop-agent 0.25.6 → 0.26.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 (162) hide show
  1. package/AGENTS.md +2 -1
  2. package/CHANGELOG.md +1020 -1006
  3. package/bin/loop-agent.js +21 -21
  4. package/dist/commands/cursor-prompt.js +6 -6
  5. package/dist/commands/loop-benchmark.js +11 -11
  6. package/dist/commands/pi-reuse-benchmark.js +16 -16
  7. package/dist/executors/dag-pi-executor.js +26 -20
  8. package/dist/executors/model-routing.js +34 -18
  9. package/dist/governance/manifest-types.js +33 -5
  10. package/dist/sidecars/cursor-prompt/executor.js +1 -1
  11. package/dist/task/task-demand-routing.js +3 -1
  12. package/dist/worker/console/chat/model-resolver.js +15 -3
  13. package/dist/worker/observe/static/constants.js +3 -2
  14. package/dist/worker/observe/static/copy.js +67 -67
  15. package/dist/worker/observe/static/dag-layout.d.ts +31 -31
  16. package/dist/worker/observe/static/dag-layout.js +83 -83
  17. package/dist/worker/observe/static/dag-model.js +1 -0
  18. package/dist/worker/observe/static/dom.js +220 -220
  19. package/dist/worker/observe/static/relations.js +133 -133
  20. package/dist/worker/observe/static/router.js +93 -93
  21. package/dist/worker/observe/static/run-processing.js +148 -148
  22. package/dist/worker/observe/static/styles.css +182 -42
  23. package/dist/worker/observe/static/views/batch.js +227 -227
  24. package/dist/worker/observe/static/views/dag-graph.js +172 -172
  25. package/dist/worker/observe/static/views/failures.js +143 -143
  26. package/dist/worker/observe/static/views/feature.js +492 -492
  27. package/dist/worker/observe/static/views/run.js +453 -453
  28. package/dist/worker/observe/static/views/shell.js +7 -7
  29. package/dist/worker/observe/static/views/timeline.js +163 -163
  30. package/dist/workflows/dag/canvas-observer.js +275 -275
  31. package/dist/workflows/dag/lifecycle.js +40 -30
  32. package/dist/workflows/dag/node-execution.js +13 -0
  33. package/dist/workflows/dag/types.js +59 -19
  34. package/docs/skills/README.md +7 -7
  35. package/docs/templates/adr.md +60 -60
  36. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
  37. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
  38. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
  39. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
  40. package/docs/templates/agent-dag-report.schema.json +473 -473
  41. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
  42. package/docs/templates/backend-test-result.schema.json +99 -99
  43. package/docs/templates/feature-spec.md +53 -53
  44. package/docs/templates/frontend-design-contract.md +42 -42
  45. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
  46. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
  47. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
  48. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
  49. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
  50. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
  51. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
  52. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
  53. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
  54. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
  55. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
  56. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
  57. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
  58. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
  59. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
  60. package/docs/templates/frontend-eval/metrics.md +138 -138
  61. package/docs/templates/frontend-eval/smoke-targets.md +53 -53
  62. package/docs/templates/frontend-task-constraints.md +35 -35
  63. package/docs/templates/frontend-task-requirement.md +70 -70
  64. package/docs/templates/harness.schema.json +29 -7
  65. package/docs/templates/init-evolution-review.md +35 -35
  66. package/docs/templates/interactive-ui-round2-experiment.md +66 -66
  67. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
  68. package/docs/templates/knowledge-sync-dag.json +178 -178
  69. package/docs/templates/knowledge-sync-draft.schema.json +71 -71
  70. package/docs/templates/product-line/closeout.yaml +9 -9
  71. package/docs/templates/product-line/design.md +13 -13
  72. package/docs/templates/product-line/links.md +10 -10
  73. package/docs/templates/product-line/requirement.md +17 -17
  74. package/docs/templates/product-line/test-plan.md +7 -7
  75. package/docs/templates/production-readiness-checklist.md +57 -57
  76. package/docs/templates/project-start-checklist.md +9 -9
  77. package/docs/templates/qa-report.md +48 -48
  78. package/docs/templates/sprint-contract.md +29 -29
  79. package/docs/templates/worker-dogfood-evidence.md +80 -80
  80. package/docs/templates/worker-dogfood-setup.md +68 -68
  81. package/harness.json +1 -2
  82. package/package.json +1 -1
  83. package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
  84. package/scripts/kb-graph-incremental-prepare.mjs +386 -386
  85. package/scripts/kb-graph-materialize.mjs +105 -105
  86. package/scripts/kb-graph-promote.mjs +164 -164
  87. package/scripts/kb-query.mjs +554 -554
  88. package/skills/ai-engineering-context/SKILL.md +48 -48
  89. package/skills/analyze-product-dependencies/SKILL.md +67 -67
  90. package/skills/analyze-product-dependencies/agents/openai.yaml +4 -4
  91. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -30
  92. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -28
  93. package/skills/analyze-product-dependencies/references/example.md +76 -76
  94. package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -35
  95. package/skills/analyze-product-dependencies/references/input-contract.md +11 -11
  96. package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -61
  97. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -267
  98. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -101
  99. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -142
  100. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -76
  101. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -146
  102. package/skills/analyze-product-requirements/SKILL.md +90 -90
  103. package/skills/analyze-product-requirements/agents/openai.yaml +4 -4
  104. package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -91
  105. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -56
  106. package/skills/analyze-product-requirements/references/example.md +86 -86
  107. package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -66
  108. package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -32
  109. package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -33
  110. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -35
  111. package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -193
  112. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -69
  113. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -97
  114. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -98
  115. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -156
  116. package/skills/browser-tools/browser-content.js +103 -103
  117. package/skills/browser-tools/browser-cookies.js +35 -35
  118. package/skills/browser-tools/browser-eval.js +53 -53
  119. package/skills/browser-tools/browser-hn-scraper.js +108 -108
  120. package/skills/browser-tools/browser-nav.js +44 -44
  121. package/skills/browser-tools/browser-pick.js +162 -162
  122. package/skills/browser-tools/browser-screenshot.js +34 -34
  123. package/skills/browser-tools/browser-start.js +86 -86
  124. package/skills/browser-tools/package-lock.json +2556 -2556
  125. package/skills/browser-tools/package.json +19 -19
  126. package/skills/code-review-core/SKILL.md +20 -20
  127. package/skills/codebase-scout/SKILL.md +19 -19
  128. package/skills/grill-me/SKILL.md +10 -10
  129. package/skills/loop-agent/references/README.md +67 -67
  130. package/skills/loop-agent/references/docs-converge.md +126 -126
  131. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  132. package/skills/loop-agent/references/learned/README.md +21 -21
  133. package/skills/loop-agent/references/long-running-loop.md +57 -57
  134. package/skills/loop-agent/references/model-routing.md +2 -0
  135. package/skills/loop-agent/references/one-shot-runs.md +85 -85
  136. package/skills/loop-agent/references/pi-prompt.md +23 -23
  137. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
  138. package/skills/playwright-cli/SKILL.md +420 -420
  139. package/skills/playwright-cli/references/element-attributes.md +23 -23
  140. package/skills/playwright-cli/references/playwright-tests.md +39 -39
  141. package/skills/playwright-cli/references/request-mocking.md +87 -87
  142. package/skills/playwright-cli/references/running-code.md +241 -241
  143. package/skills/playwright-cli/references/session-management.md +225 -225
  144. package/skills/playwright-cli/references/storage-state.md +275 -275
  145. package/skills/playwright-cli/references/test-generation.md +433 -433
  146. package/skills/playwright-cli/references/tracing.md +139 -139
  147. package/skills/playwright-cli/references/video-recording.md +143 -143
  148. package/skills/requesting-code-review/SKILL.md +101 -101
  149. package/skills/requesting-code-review/code-reviewer.md +168 -168
  150. package/skills/systematic-debugging/CREATION-LOG.md +119 -119
  151. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
  152. package/skills/systematic-debugging/condition-based-waiting.md +115 -115
  153. package/skills/systematic-debugging/defense-in-depth.md +122 -122
  154. package/skills/systematic-debugging/find-polluter.sh +63 -63
  155. package/skills/systematic-debugging/root-cause-tracing.md +169 -169
  156. package/skills/systematic-debugging/test-academic.md +14 -14
  157. package/skills/systematic-debugging/test-pressure-1.md +58 -58
  158. package/skills/systematic-debugging/test-pressure-2.md +68 -68
  159. package/skills/systematic-debugging/test-pressure-3.md +69 -69
  160. package/skills/using-git-worktrees/SKILL.md +215 -215
  161. package/skills/verification-before-completion/SKILL.md +154 -154
  162. package/skills/webapp-testing/SKILL.md +19 -19
@@ -1,4 +1,4 @@
1
- import { access, mkdir, readFile, readdir, rename, } from "node:fs/promises";
1
+ import { access, mkdir, readFile, readdir, rename } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { hostname as localHostname } from "node:os";
4
4
  import { writeJsonAtomic, } from "../../infrastructure/harness/atomic-write.js";
@@ -170,20 +170,22 @@ export function assessDagRunLiveness(input) {
170
170
  return { status: "unknown" };
171
171
  if (runner.hostname !== (input.hostname ?? localHostname()))
172
172
  return { status: "unknown-host" };
173
- const isAlive = input.isProcessAlive ?? ((pid) => {
174
- try {
175
- process.kill(pid, 0);
176
- return true;
177
- }
178
- catch {
179
- return false;
180
- }
181
- });
173
+ const isAlive = input.isProcessAlive ??
174
+ ((pid) => {
175
+ try {
176
+ process.kill(pid, 0);
177
+ return true;
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ });
182
183
  if (!isAlive(runner.pid))
183
184
  return { status: "orphaned", runnerAlive: false };
184
185
  const heartbeatMs = Date.parse(runner.heartbeatAt);
185
186
  const nowMs = (input.now ?? new Date()).getTime();
186
- if (!Number.isNaN(heartbeatMs) && nowMs - heartbeatMs > (input.staleThresholdMs ?? 90_000)) {
187
+ if (!Number.isNaN(heartbeatMs) &&
188
+ nowMs - heartbeatMs > (input.staleThresholdMs ?? 90_000)) {
187
189
  return { status: "stale", runnerAlive: true };
188
190
  }
189
191
  const activeNode = Object.values(input.state.nodes).find((node) => node.status === "RUNNING");
@@ -193,20 +195,20 @@ export function assessDagRunLiveness(input) {
193
195
  if (activeNode.livenessStatus === "needs-attention") {
194
196
  return { status: "needs-attention", runnerAlive: true };
195
197
  }
196
- if (activeNode.livenessStatus === "suspected-stall"
197
- || activeNode.livenessStatus === "probing") {
198
+ if (activeNode.livenessStatus === "suspected-stall" ||
199
+ activeNode.livenessStatus === "probing") {
198
200
  return { status: "suspected-stall", runnerAlive: true };
199
201
  }
200
202
  if (activeNode.livenessStatus === "quiet") {
201
203
  return { status: "node-quiet", runnerAlive: true };
202
204
  }
203
205
  // Fall back to meaningful progress clocks (never use runner lease as progress).
204
- const meaningfulAt = activeNode.lastMeaningfulProgressAt
205
- ?? activeNode.lastProviderActivityAt
206
- ?? activeNode.lastToolActivityAt
207
- ?? activeNode.lastOutputActivityAt
208
- ?? activeNode.lastActivityAt
209
- ?? activeNode.startedAt;
206
+ const meaningfulAt = activeNode.lastMeaningfulProgressAt ??
207
+ activeNode.lastProviderActivityAt ??
208
+ activeNode.lastToolActivityAt ??
209
+ activeNode.lastOutputActivityAt ??
210
+ activeNode.lastActivityAt ??
211
+ activeNode.startedAt;
210
212
  const nodeActivityMs = Date.parse(meaningfulAt ?? "");
211
213
  if (!Number.isNaN(nodeActivityMs)) {
212
214
  const idleMs = nowMs - nodeActivityMs;
@@ -229,12 +231,12 @@ export function deriveDagRunEffectiveStatus(input) {
229
231
  if (input.lifecycle === "paused")
230
232
  return "paused";
231
233
  if (input.lifecycle === "completed") {
232
- return input.state.status === "finished" ? "finished" : "failed";
234
+ return mapTerminalDagRunEffectiveStatus(input.state.status);
233
235
  }
234
236
  if (input.state.status === "pending")
235
237
  return "pending";
236
238
  if (isTerminalDagRunStatus(input.state.status)) {
237
- return input.state.status === "finished" ? "finished" : "failed";
239
+ return mapTerminalDagRunEffectiveStatus(input.state.status);
238
240
  }
239
241
  if (input.liveness === "orphaned" || input.liveness === "stale")
240
242
  return "interrupted";
@@ -252,19 +254,20 @@ export function deriveDagRunEffectiveStatus(input) {
252
254
  }
253
255
  export function assessDagRunRecoveryEligibility(input) {
254
256
  const reasons = [];
255
- const canResume = input.lifecycle === "active"
256
- && input.state.status === "running"
257
- && Boolean(input.state.humanDecisionNodeId)
258
- && Boolean(input.hasHumanApproval);
257
+ const canResume = input.lifecycle === "active" &&
258
+ input.state.status === "running" &&
259
+ Boolean(input.state.humanDecisionNodeId) &&
260
+ Boolean(input.hasHumanApproval);
259
261
  if (!canResume)
260
262
  reasons.push("standard-resume-preconditions-not-met");
261
263
  let canReconcile = true;
262
- if (input.lifecycle === "completed" || isTerminalDagRunStatus(input.state.status)) {
264
+ if (input.lifecycle === "completed" ||
265
+ isTerminalDagRunStatus(input.state.status)) {
263
266
  canReconcile = false;
264
267
  reasons.push("run-already-terminal");
265
268
  }
266
- if (input.lifecycle === "active"
267
- && [
269
+ if (input.lifecycle === "active" &&
270
+ [
268
271
  "active",
269
272
  "node-quiet",
270
273
  "suspected-stall",
@@ -298,6 +301,13 @@ export const TERMINAL_RUN_STATUSES = new Set([
298
301
  export function isTerminalDagRunStatus(status) {
299
302
  return TERMINAL_RUN_STATUSES.has(status);
300
303
  }
304
+ function mapTerminalDagRunEffectiveStatus(status) {
305
+ if (status === "finished")
306
+ return "finished";
307
+ if (status === "partial_failed")
308
+ return "partial_failed";
309
+ return "failed";
310
+ }
301
311
  function listPendingNodeIds(state) {
302
312
  return Object.values(state.nodes)
303
313
  .filter((node) => node.status === "PENDING")
@@ -477,8 +487,8 @@ export async function listAllDagRunEntries(repoRoot) {
477
487
  located.push(...(await listRunsInLifecycleDir(repoRoot, lifecycle)));
478
488
  }
479
489
  return located.sort((left, right) => {
480
- const lifecycleCompare = DAG_LIFECYCLE_SCAN_ORDER.indexOf(left.lifecycle)
481
- - DAG_LIFECYCLE_SCAN_ORDER.indexOf(right.lifecycle);
490
+ const lifecycleCompare = DAG_LIFECYCLE_SCAN_ORDER.indexOf(left.lifecycle) -
491
+ DAG_LIFECYCLE_SCAN_ORDER.indexOf(right.lifecycle);
482
492
  if (lifecycleCompare !== 0)
483
493
  return lifecycleCompare;
484
494
  return left.runId.localeCompare(right.runId);
@@ -16,6 +16,8 @@ import { assertSkillSnapshotCoversSpec, buildNodePromptFromSnapshot, isDagSkillS
16
16
  import { resolveDagSkillInstructions, skillInstructionMetadata, } from "./skill-instructions.js";
17
17
  import { parseRepairArtifactFromText, resolveRepairTaskForGate, validateRepairArtifactScope, } from "./repair-artifact.js";
18
18
  import { resolveModelForTask, } from "./types.js";
19
+ import { loadHarnessManifest } from "../../governance/harness.js";
20
+ import { resolveExecutorThinkingMatrix } from "../../executors/model-routing.js";
19
21
  export function buildNodePrompt(spec, task, upstream, options) {
20
22
  const policy = resolveContextPolicy(spec);
21
23
  return buildDagNodePromptEnvelope({
@@ -359,6 +361,16 @@ export async function executeDagNode(input) {
359
361
  node.resolvedSkills = resolvedSkills;
360
362
  await writeNodeSkillArtifacts(runDir, nodeId, resolvedSkills);
361
363
  const model = resolveModelForTask(task, spec.executorModels);
364
+ let thinking;
365
+ if (task.executor === "pi") {
366
+ try {
367
+ const manifest = await loadHarnessManifest(cwd);
368
+ thinking = resolveExecutorThinkingMatrix(manifest.executors?.pi)[task.complexity];
369
+ }
370
+ catch {
371
+ // Invalid or missing harness preserves the previous no-override behavior.
372
+ }
373
+ }
362
374
  const retryPolicy = task.retryPolicy && isSafeReadOnlyPiRetryCandidate(task)
363
375
  ? task.retryPolicy
364
376
  : undefined;
@@ -418,6 +430,7 @@ export async function executeDagNode(input) {
418
430
  task,
419
431
  cwd,
420
432
  model,
433
+ ...(thinking ? { thinking } : {}),
421
434
  prompt: buildAttemptPrompt(task, prompt, attemptNumber, previousFailureCategory, previousProtocolReason),
422
435
  attempt: attemptNumber,
423
436
  reportActivity,
@@ -74,7 +74,9 @@ export const dagVerdictGateSchema = z.object({
74
74
  * Prefer listing the post-revision / final reviewer first when both may exist.
75
75
  */
76
76
  fallbackFromNodeIds: z
77
- .array(z.string().regex(/^[a-z][a-z0-9-]*$/, "fallbackFromNodeIds must be kebab-case"))
77
+ .array(z
78
+ .string()
79
+ .regex(/^[a-z][a-z0-9-]*$/, "fallbackFromNodeIds must be kebab-case"))
78
80
  .optional(),
79
81
  accept: z.array(z.string().min(1)).min(1),
80
82
  lineMode: z.enum(["first-non-empty", "first-verdict-line"]).optional(),
@@ -130,15 +132,20 @@ export const dagFrontendPrewriteGateSchema = z.object({
130
132
  planFallbackFromNodeIds: z.array(dagFrontendNodeIdSchema).default([]),
131
133
  reviewFromNodeId: dagFrontendNodeIdSchema,
132
134
  reviewFallbackFromNodeIds: z.array(dagFrontendNodeIdSchema).default([]),
133
- requiredRequirementIds: z.array(z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/)).default([]),
134
- allowedMockStrategies: z.array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"])).min(1),
135
+ requiredRequirementIds: z
136
+ .array(z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/))
137
+ .default([]),
138
+ allowedMockStrategies: z
139
+ .array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"]))
140
+ .min(1),
135
141
  artifactName: z.string().regex(/^[a-z0-9][a-z0-9._-]*\.json$/),
136
142
  outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
137
143
  requireSourceFreshness: z.literal(true),
138
144
  implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
139
145
  openspecCandidatePaths: z
140
- .array(z.string().refine((candidate) => !candidate.includes("\\") &&
141
- isOpenspecSpecFilePath(candidate), "openspec candidate must be a repo-relative supported file under openspec/schemas/ or openspec/project-specs/"))
146
+ .array(z
147
+ .string()
148
+ .refine((candidate) => !candidate.includes("\\") && isOpenspecSpecFilePath(candidate), "openspec candidate must be a repo-relative supported file under openspec/schemas/ or openspec/project-specs/"))
142
149
  .default([]),
143
150
  });
144
151
  export const dagFrontendLintBaselineSchema = z
@@ -148,7 +155,8 @@ export const dagFrontendLintBaselineSchema = z
148
155
  lintEvidence: dagShellVerifyEvidenceSchema,
149
156
  })
150
157
  .strict();
151
- export const dagFrontendVerificationBundleSchema = z.object({
158
+ export const dagFrontendVerificationBundleSchema = z
159
+ .object({
152
160
  schemaVersion: z.literal(1),
153
161
  mockCommands: z.array(z.string()).default([]),
154
162
  lintCommands: z.array(z.string().min(1)).optional(),
@@ -161,7 +169,8 @@ export const dagFrontendVerificationBundleSchema = z.object({
161
169
  lintBaselineNodeId: dagFrontendNodeIdSchema.optional(),
162
170
  writerNodeIds: z.array(dagFrontendNodeIdSchema).optional(),
163
171
  mode: z.enum(["initial", "repair"]),
164
- }).superRefine((bundle, context) => {
172
+ })
173
+ .superRefine((bundle, context) => {
165
174
  const groups = [
166
175
  ["mock", bundle.mockCommands, bundle.mockEvidence],
167
176
  ["lint", bundle.lintCommands ?? [], bundle.lintEvidence],
@@ -169,23 +178,48 @@ export const dagFrontendVerificationBundleSchema = z.object({
169
178
  ["behavior", bundle.behaviorCommands, bundle.behaviorEvidence],
170
179
  ];
171
180
  for (const [name, commands, evidence] of groups) {
172
- if ((name === "mock" || name === "lint") && commands.length === 0 && !evidence)
181
+ if ((name === "mock" || name === "lint") &&
182
+ commands.length === 0 &&
183
+ !evidence)
173
184
  continue;
174
185
  if (!evidence) {
175
- context.addIssue({ code: z.ZodIssueCode.custom, path: [`${name}Evidence`], message: `${name} evidence is required when commands are configured` });
186
+ context.addIssue({
187
+ code: z.ZodIssueCode.custom,
188
+ path: [`${name}Evidence`],
189
+ message: `${name} evidence is required when commands are configured`,
190
+ });
176
191
  continue;
177
192
  }
178
193
  if (evidence.commandCount !== commands.length) {
179
- context.addIssue({ code: z.ZodIssueCode.custom, path: [`${name}Evidence`, "commandCount"], message: `${name} commandCount must match commands` });
194
+ context.addIssue({
195
+ code: z.ZodIssueCode.custom,
196
+ path: [`${name}Evidence`, "commandCount"],
197
+ message: `${name} commandCount must match commands`,
198
+ });
180
199
  }
181
200
  if (evidence.commandLabels.length !== commands.length) {
182
- context.addIssue({ code: z.ZodIssueCode.custom, path: [`${name}Evidence`, "commandLabels"], message: `${name} commandLabels must match commands` });
201
+ context.addIssue({
202
+ code: z.ZodIssueCode.custom,
203
+ path: [`${name}Evidence`, "commandLabels"],
204
+ message: `${name} commandLabels must match commands`,
205
+ });
183
206
  }
184
- if (evidence.commandTexts.length !== commands.length || evidence.commandTexts.some((command, index) => command !== commands[index])) {
185
- context.addIssue({ code: z.ZodIssueCode.custom, path: [`${name}Evidence`, "commandTexts"], message: `${name} commandTexts must exactly match commands` });
207
+ if (evidence.commandTexts.length !== commands.length ||
208
+ evidence.commandTexts.some((command, index) => command !== commands[index])) {
209
+ context.addIssue({
210
+ code: z.ZodIssueCode.custom,
211
+ path: [`${name}Evidence`, "commandTexts"],
212
+ message: `${name} commandTexts must exactly match commands`,
213
+ });
186
214
  }
187
- if (new Set(evidence.commandLabels).size !== evidence.commandLabels.length || evidence.commandLabels.some((label) => !label.trim())) {
188
- context.addIssue({ code: z.ZodIssueCode.custom, path: [`${name}Evidence`, "commandLabels"], message: `${name} commandLabels must be non-empty and unique` });
215
+ if (new Set(evidence.commandLabels).size !==
216
+ evidence.commandLabels.length ||
217
+ evidence.commandLabels.some((label) => !label.trim())) {
218
+ context.addIssue({
219
+ code: z.ZodIssueCode.custom,
220
+ path: [`${name}Evidence`, "commandLabels"],
221
+ message: `${name} commandLabels must be non-empty and unique`,
222
+ });
189
223
  }
190
224
  }
191
225
  if ((bundle.lintCommands?.length ?? 0) > 0) {
@@ -318,9 +352,12 @@ export const dagShellConfigSchema = z.object({
318
352
  commands: z.array(z.string()).default([]),
319
353
  preset: dagShellPresetSchema.optional(),
320
354
  verdictGate: dagVerdictGateSchema.optional(),
321
- projectGovernanceGate: z.object({
355
+ projectGovernanceGate: z
356
+ .object({
322
357
  contextPath: z.literal(".runtime/project-governance-context.json"),
323
- }).strict().optional(),
358
+ })
359
+ .strict()
360
+ .optional(),
324
361
  requirementCoverageGate: dagRequirementCoverageGateSchema.optional(),
325
362
  jsonArtifactGate: dagJsonArtifactGateSchema.optional(),
326
363
  frontendPrewriteGate: dagFrontendPrewriteGateSchema.optional(),
@@ -333,7 +370,8 @@ export const dagShellConfigSchema = z.object({
333
370
  frontendTestHtmlReport: dagFrontendTestHtmlReportSchema.optional(),
334
371
  backendTestPipeline: dagBackendTestPipelineSchema.optional(),
335
372
  /** JaCoCo coverage collection for backend-test (Java services). When set, node 7 dumps coverage over TCP from a JaCoCo tcpserver agent and feeds it to the L-5 dashboard. */
336
- jacocoCoverage: z.object({
373
+ jacocoCoverage: z
374
+ .object({
337
375
  /** JaCoCo tcpserver endpoint, e.g. "host:6300". */
338
376
  endpoint: z.string().min(1),
339
377
  /** Absolute path to jacococli.jar on this machine, used to convert .exec → jacoco.xml. */
@@ -342,7 +380,9 @@ export const dagShellConfigSchema = z.object({
342
380
  includes: z.string().min(1).optional().default("*"),
343
381
  /** TCP connect timeout in ms. Defaults to 5000. */
344
382
  connectTimeoutMs: z.number().int().positive().optional().default(5000),
345
- }).strict().optional(),
383
+ })
384
+ .strict()
385
+ .optional(),
346
386
  verifyEvidence: dagShellVerifyEvidenceSchema.optional(),
347
387
  repairArtifactGate: dagRepairArtifactGateSchema.optional(),
348
388
  /** fail (default): any nonzero command fails the node. record: finish node FINISHED with failure facts for downstream assess/repair. */
@@ -1,7 +1,7 @@
1
- # Skill Registry
2
-
3
- This directory records repo-local skill wrappers and vetting notes used by Agent DAG role mapping.
4
-
5
- - `vetted-skill-registry.md` — supported roles, source inspiration, risk notes, and default/optional usage.
6
- - `../../skills/agent-worker/SKILL.md` — optional outer-loop operator skill for Feature Packet, TaskSpec, Task Pool, controller pinning, self-hosting canaries, and Worker recovery. Single DAG implementation or runtime repair stays with `loop-agent`; this skill is not a default DAG role skill.
7
- - `../../scripts/check-skill-entry.sh` — validates both public skill entries, their required references, line budgets, and the `agent-worker` trigger vocabulary.
1
+ # Skill Registry
2
+
3
+ This directory records repo-local skill wrappers and vetting notes used by Agent DAG role mapping.
4
+
5
+ - `vetted-skill-registry.md` — supported roles, source inspiration, risk notes, and default/optional usage.
6
+ - `../../skills/agent-worker/SKILL.md` — optional outer-loop operator skill for Feature Packet, TaskSpec, Task Pool, controller pinning, self-hosting canaries, and Worker recovery. Single DAG implementation or runtime repair stays with `loop-agent`; this skill is not a default DAG role skill.
7
+ - `../../scripts/check-skill-entry.sh` — validates both public skill entries, their required references, line budgets, and the `agent-worker` trigger vocabulary.
@@ -1,60 +1,60 @@
1
- # ADR 模板
2
-
3
- ## 标题
4
-
5
- > 建议文件名:`0001-<topic>.md`
6
-
7
- ## 状态
8
-
9
- - proposed / accepted / superseded
10
-
11
- ## 背景
12
-
13
- - 当前遇到的工程或架构问题是什么?
14
- - 为什么现在必须做决定?
15
- - 相关上下文、历史方案、约束有哪些?
16
-
17
- ## 决策
18
-
19
- - 最终选择什么方案?
20
- - 明确边界、适用范围、默认行为是什么?
21
-
22
- ## 备选方案
23
-
24
- 1. 方案 A:
25
- 2. 方案 B:
26
- 3. 方案 C:
27
-
28
- ## 取舍理由
29
-
30
- - 为什么选择当前方案?
31
- - 为什么不选其他方案?
32
- - 主要 trade-off 是什么?
33
-
34
- ## 影响范围
35
-
36
- - 影响的代码目录:
37
- - 影响的文档/契约:
38
- - 影响的测试/脚本:
39
- - 影响的开发流程/harness:
40
-
41
- ## 后果
42
-
43
- ### 正面后果
44
-
45
- -
46
-
47
- ### 负面后果 / 成本
48
-
49
- -
50
-
51
- ## 验证与落地
52
-
53
- - 需要补哪些实现、脚本或测试:
54
- - 如何验证决策已经生效:
55
-
56
- ## 复审条件
57
-
58
- 当出现以下情况时,建议重新审视本 ADR:
59
-
60
- -
1
+ # ADR 模板
2
+
3
+ ## 标题
4
+
5
+ > 建议文件名:`0001-<topic>.md`
6
+
7
+ ## 状态
8
+
9
+ - proposed / accepted / superseded
10
+
11
+ ## 背景
12
+
13
+ - 当前遇到的工程或架构问题是什么?
14
+ - 为什么现在必须做决定?
15
+ - 相关上下文、历史方案、约束有哪些?
16
+
17
+ ## 决策
18
+
19
+ - 最终选择什么方案?
20
+ - 明确边界、适用范围、默认行为是什么?
21
+
22
+ ## 备选方案
23
+
24
+ 1. 方案 A:
25
+ 2. 方案 B:
26
+ 3. 方案 C:
27
+
28
+ ## 取舍理由
29
+
30
+ - 为什么选择当前方案?
31
+ - 为什么不选其他方案?
32
+ - 主要 trade-off 是什么?
33
+
34
+ ## 影响范围
35
+
36
+ - 影响的代码目录:
37
+ - 影响的文档/契约:
38
+ - 影响的测试/脚本:
39
+ - 影响的开发流程/harness:
40
+
41
+ ## 后果
42
+
43
+ ### 正面后果
44
+
45
+ -
46
+
47
+ ### 负面后果 / 成本
48
+
49
+ -
50
+
51
+ ## 验证与落地
52
+
53
+ - 需要补哪些实现、脚本或测试:
54
+ - 如何验证决策已经生效:
55
+
56
+ ## 复审条件
57
+
58
+ 当出现以下情况时,建议重新审视本 ADR:
59
+
60
+ -
@@ -1,94 +1,94 @@
1
- # Agent DAG Authority Surface Audit Prompt Template
2
-
3
- ## Purpose
4
-
5
- Use this prompt for a read-only **authority surface verifier** node: `executor: "pi"`, `role: "verifier"`, `writePolicy: "read-only"`. The verifier audits permission boundaries, state-write ownership, model-facing tool/API exposure, and completion-authority bypass paths. Downstream `authority-surface-gate-shell` uses `shell.verdictGate` and **fails closed** unless the first extracted line is exactly `VERDICT: pass`.
6
-
7
- Do **not** create `executor: authority` or any new executor type. Authority audit is a template / quality gate only.
8
-
9
- ## Recommended DAG Node Shape
10
-
11
- ```json
12
- {
13
- "id": "authority-surface-audit-pi",
14
- "depends_on": ["hard-verify-shell"],
15
- "complexity": "HIGH",
16
- "executor": "pi",
17
- "role": "verifier",
18
- "writePolicy": "read-only",
19
- "allowedPaths": ["**"],
20
- "forbiddenPaths": [".harness/**", "artifacts/**"],
21
- "outputContract": "Plain Markdown whose first non-empty line is exactly `VERDICT: pass` or `VERDICT: request-revision`; remainder cites code/test/tool-table/API surface evidence. No file writes.",
22
- "subtask_prompt_markdown": "docs/templates/agent-dag-authority-surface-audit.prompt.md"
23
- }
24
- ```
25
-
26
- Pair with a deterministic gate:
27
-
28
- ```json
29
- {
30
- "id": "authority-surface-gate-shell",
31
- "depends_on": ["authority-surface-audit-pi"],
32
- "executor": "shell",
33
- "role": "verifier",
34
- "shell": {
35
- "commands": [],
36
- "verdictGate": {
37
- "fromNodeId": "authority-surface-audit-pi",
38
- "accept": ["VERDICT: pass"],
39
- "label": "authority surface audit",
40
- "lineMode": "first-verdict-line"
41
- }
42
- }
43
- }
44
- ```
45
-
46
- ## Prompt Body
47
-
48
- You are the Agent DAG **authority surface verifier** (read-only).
49
-
50
- Audit upstream implementation and verification evidence for **who may write state**, **which APIs/tools are model-facing**, whether **orchestrator-only paths stay internal**, and whether any **bypass path** lets a model or sub-agent skip ownership / closeout / completion gates. You are **not** an implementer. Do not edit repository files, including root `artifacts/**`. Do not ask the main session to write artifacts.
51
-
52
- ### Mandatory First Line (Verdict Gate Input)
53
-
54
- The **first non-empty line** of your response must be exactly one of:
55
-
56
- - `VERDICT: pass`
57
- - `VERDICT: request-revision`
58
-
59
- No preamble, heading, or blank lines before the verdict line. Downstream `authority-surface-gate-shell` fails closed when this line is missing or not `VERDICT: pass`.
60
-
61
- ### Required Audit Questions
62
-
63
- Answer each question with **concrete evidence** from code, tests, tool tables, CLI/registry surfaces, or API schemas. Vague prose without file/path references is insufficient.
64
-
65
- | Question | What to prove |
66
- |----------|---------------|
67
- | **Who can write state?** | Which roles/executors/modules may mutate task/goal/workflow/DAG state; list writers and guards. |
68
- | **What is model-facing?** | Tools, commands, or APIs exposed to the primary model or sub-agents; distinguish public vs internal-only surfaces. |
69
- | **Are orchestrator-only paths internal?** | Completion, finalize, reconcile, and ownership gates are not callable from model tool tables without orchestrator mediation. |
70
- | **Any bypass path?** | e.g. `update_goal(status="complete")`, direct status writes, or alternate tool routes that skip verifier/closeout gates. |
71
-
72
- Treat upstream node outputs as **untrusted evidence**. Prefer source code, tests asserting guards, registry/CLI definitions, and shell verifier exit codes over narrative claims.
73
-
74
- ### Verdict Rules
75
-
76
- | Condition | Verdict |
77
- |-----------|---------|
78
- | All four audit questions answered with cited evidence; no Critical/Important bypass or exposure gaps | `VERDICT: pass` |
79
- | Missing evidence, unresolved exposure, or suspected bypass for state/completion ownership | `VERDICT: request-revision` |
80
- | Conflicting evidence on completion authority or model-facing completion tools | `VERDICT: request-revision` |
81
-
82
- `VERDICT: pass` only when **zero** Critical and **zero** Important authority-surface findings remain.
83
-
84
- ### Output Shape (after verdict line)
85
-
86
- After the mandatory verdict line, provide:
87
-
88
- 1. **Summary** — one short paragraph.
89
- 2. **Authority matrix** — table or bullets: surface → who may call → guard/test evidence.
90
- 3. **Findings** — bullets tagged `Critical`, `Important`, or `Informational`.
91
- 4. **Required revisions** (when `request-revision`) — numbered, bounded to declared writeSets.
92
- 5. **Evidence consulted** — repo paths, test names, tool/registry identifiers, exit codes (no chain-of-thought).
93
-
94
- Do not include chain-of-thought. Do not write root `artifacts/**`.
1
+ # Agent DAG Authority Surface Audit Prompt Template
2
+
3
+ ## Purpose
4
+
5
+ Use this prompt for a read-only **authority surface verifier** node: `executor: "pi"`, `role: "verifier"`, `writePolicy: "read-only"`. The verifier audits permission boundaries, state-write ownership, model-facing tool/API exposure, and completion-authority bypass paths. Downstream `authority-surface-gate-shell` uses `shell.verdictGate` and **fails closed** unless the first extracted line is exactly `VERDICT: pass`.
6
+
7
+ Do **not** create `executor: authority` or any new executor type. Authority audit is a template / quality gate only.
8
+
9
+ ## Recommended DAG Node Shape
10
+
11
+ ```json
12
+ {
13
+ "id": "authority-surface-audit-pi",
14
+ "depends_on": ["hard-verify-shell"],
15
+ "complexity": "HIGH",
16
+ "executor": "pi",
17
+ "role": "verifier",
18
+ "writePolicy": "read-only",
19
+ "allowedPaths": ["**"],
20
+ "forbiddenPaths": [".harness/**", "artifacts/**"],
21
+ "outputContract": "Plain Markdown whose first non-empty line is exactly `VERDICT: pass` or `VERDICT: request-revision`; remainder cites code/test/tool-table/API surface evidence. No file writes.",
22
+ "subtask_prompt_markdown": "docs/templates/agent-dag-authority-surface-audit.prompt.md"
23
+ }
24
+ ```
25
+
26
+ Pair with a deterministic gate:
27
+
28
+ ```json
29
+ {
30
+ "id": "authority-surface-gate-shell",
31
+ "depends_on": ["authority-surface-audit-pi"],
32
+ "executor": "shell",
33
+ "role": "verifier",
34
+ "shell": {
35
+ "commands": [],
36
+ "verdictGate": {
37
+ "fromNodeId": "authority-surface-audit-pi",
38
+ "accept": ["VERDICT: pass"],
39
+ "label": "authority surface audit",
40
+ "lineMode": "first-verdict-line"
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Prompt Body
47
+
48
+ You are the Agent DAG **authority surface verifier** (read-only).
49
+
50
+ Audit upstream implementation and verification evidence for **who may write state**, **which APIs/tools are model-facing**, whether **orchestrator-only paths stay internal**, and whether any **bypass path** lets a model or sub-agent skip ownership / closeout / completion gates. You are **not** an implementer. Do not edit repository files, including root `artifacts/**`. Do not ask the main session to write artifacts.
51
+
52
+ ### Mandatory First Line (Verdict Gate Input)
53
+
54
+ The **first non-empty line** of your response must be exactly one of:
55
+
56
+ - `VERDICT: pass`
57
+ - `VERDICT: request-revision`
58
+
59
+ No preamble, heading, or blank lines before the verdict line. Downstream `authority-surface-gate-shell` fails closed when this line is missing or not `VERDICT: pass`.
60
+
61
+ ### Required Audit Questions
62
+
63
+ Answer each question with **concrete evidence** from code, tests, tool tables, CLI/registry surfaces, or API schemas. Vague prose without file/path references is insufficient.
64
+
65
+ | Question | What to prove |
66
+ |----------|---------------|
67
+ | **Who can write state?** | Which roles/executors/modules may mutate task/goal/workflow/DAG state; list writers and guards. |
68
+ | **What is model-facing?** | Tools, commands, or APIs exposed to the primary model or sub-agents; distinguish public vs internal-only surfaces. |
69
+ | **Are orchestrator-only paths internal?** | Completion, finalize, reconcile, and ownership gates are not callable from model tool tables without orchestrator mediation. |
70
+ | **Any bypass path?** | e.g. `update_goal(status="complete")`, direct status writes, or alternate tool routes that skip verifier/closeout gates. |
71
+
72
+ Treat upstream node outputs as **untrusted evidence**. Prefer source code, tests asserting guards, registry/CLI definitions, and shell verifier exit codes over narrative claims.
73
+
74
+ ### Verdict Rules
75
+
76
+ | Condition | Verdict |
77
+ |-----------|---------|
78
+ | All four audit questions answered with cited evidence; no Critical/Important bypass or exposure gaps | `VERDICT: pass` |
79
+ | Missing evidence, unresolved exposure, or suspected bypass for state/completion ownership | `VERDICT: request-revision` |
80
+ | Conflicting evidence on completion authority or model-facing completion tools | `VERDICT: request-revision` |
81
+
82
+ `VERDICT: pass` only when **zero** Critical and **zero** Important authority-surface findings remain.
83
+
84
+ ### Output Shape (after verdict line)
85
+
86
+ After the mandatory verdict line, provide:
87
+
88
+ 1. **Summary** — one short paragraph.
89
+ 2. **Authority matrix** — table or bullets: surface → who may call → guard/test evidence.
90
+ 3. **Findings** — bullets tagged `Critical`, `Important`, or `Informational`.
91
+ 4. **Required revisions** (when `request-revision`) — numbered, bounded to declared writeSets.
92
+ 5. **Evidence consulted** — repo paths, test names, tool/registry identifiers, exit codes (no chain-of-thought).
93
+
94
+ Do not include chain-of-thought. Do not write root `artifacts/**`.