@tea-agent/loop-agent 0.1.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 +121 -0
- package/CHANGELOG.md +37 -0
- package/README.md +144 -0
- package/bin/loop-agent.js +21 -0
- package/dist/adapters/aimax.js +91 -0
- package/dist/adapters/context.js +32 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/loop-agent.js +98 -0
- package/dist/adapters/types.js +1 -0
- package/dist/cli/catalog.js +259 -0
- package/dist/cli/help.js +55 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/program.js +505 -0
- package/dist/cli.js +12 -0
- package/dist/commands/closeout.js +13 -0
- package/dist/commands/coverage-audit.js +14 -0
- package/dist/commands/cursor-prompt.js +222 -0
- package/dist/commands/cursor-worker.js +43 -0
- package/dist/commands/dag-approve.js +102 -0
- package/dist/commands/dag-final-verification.js +76 -0
- package/dist/commands/dag-init-hybrid.js +56 -0
- package/dist/commands/dag-reconcile-tasks.js +51 -0
- package/dist/commands/dag-reject.js +91 -0
- package/dist/commands/dag-report.js +177 -0
- package/dist/commands/dag-resume.js +34 -0
- package/dist/commands/dag-run-task.js +470 -0
- package/dist/commands/dag-validate.js +186 -0
- package/dist/commands/dag-workflow-compile.js +91 -0
- package/dist/commands/dag-workflow-plan.js +130 -0
- package/dist/commands/dag-workflow-validate.js +66 -0
- package/dist/commands/delegate.js +132 -0
- package/dist/commands/docs-archive.js +5 -0
- package/dist/commands/docs-audit.js +5 -0
- package/dist/commands/doctor.js +50 -0
- package/dist/commands/goal.js +92 -0
- package/dist/commands/handoff-check.js +5 -0
- package/dist/commands/harvest.js +44 -0
- package/dist/commands/inspect.js +11 -0
- package/dist/commands/instructions.js +195 -0
- package/dist/commands/knowledge.js +64 -0
- package/dist/commands/loop-benchmark.js +72 -0
- package/dist/commands/loop.js +241 -0
- package/dist/commands/new-task.js +5 -0
- package/dist/commands/pi-prompt.js +181 -0
- package/dist/commands/pi-reuse-benchmark.js +153 -0
- package/dist/commands/plan-list.js +5 -0
- package/dist/commands/promote-run.js +29 -0
- package/dist/commands/reference-index.js +16 -0
- package/dist/commands/run-dag.js +184 -0
- package/dist/commands/spine.js +38 -0
- package/dist/commands/stats.js +84 -0
- package/dist/commands/status.js +56 -0
- package/dist/commands/study-init.js +192 -0
- package/dist/commands/workflow.js +259 -0
- package/dist/commands/worktree-create.js +31 -0
- package/dist/commands/worktree-list.js +5 -0
- package/dist/commands/worktree-remove.js +26 -0
- package/dist/cursor-worker-entry.js +8 -0
- package/dist/executors/config-core.js +55 -0
- package/dist/executors/config.js +2 -0
- package/dist/executors/cursor-artifacts.js +33 -0
- package/dist/executors/cursor-execution-log.js +81 -0
- package/dist/executors/cursor-executor-artifacts.js +135 -0
- package/dist/executors/cursor-executor.js +468 -0
- package/dist/executors/cursor-run.js +115 -0
- package/dist/executors/cursor-tool.js +94 -0
- package/dist/executors/cursor-worker-client.js +213 -0
- package/dist/executors/cursor-worker-protocol.js +18 -0
- package/dist/executors/cursor-worker-server.js +54 -0
- package/dist/executors/cursor-worker.js +3 -0
- package/dist/executors/cursor.js +6 -0
- package/dist/executors/dag-cursor-executor.js +88 -0
- package/dist/executors/dag-pi-executor.js +322 -0
- package/dist/executors/dag-static-executor.js +45 -0
- package/dist/executors/dag.js +4 -0
- package/dist/executors/index.js +8 -0
- package/dist/executors/model-routing.js +60 -0
- package/dist/executors/pi-event-serializer.js +43 -0
- package/dist/executors/pi-executor.js +606 -0
- package/dist/executors/pi-reuse-benchmark.js +316 -0
- package/dist/executors/pi-runtime-reuse.js +29 -0
- package/dist/executors/pi-sdk-executor.js +255 -0
- package/dist/executors/pi-sdk.js +1 -0
- package/dist/executors/pi.js +3 -0
- package/dist/executors/shell-executor.js +300 -0
- package/dist/executors/shell-presets.js +47 -0
- package/dist/executors/shell-verification.js +251 -0
- package/dist/executors/shell-write-guard.js +126 -0
- package/dist/executors/shell.js +3 -0
- package/dist/executors/static.js +1 -0
- package/dist/governance/checks.js +434 -0
- package/dist/governance/harness.js +9 -0
- package/dist/governance/index.js +3 -0
- package/dist/governance/manifest-types.js +128 -0
- package/dist/governance/manifest.js +2 -0
- package/dist/governance/path-guard.js +69 -0
- package/dist/governance/path-guards.js +2 -0
- package/dist/governance/profiles.js +3 -0
- package/dist/governance/requirement-coverage.js +425 -0
- package/dist/governance/skill-safety.js +135 -0
- package/dist/governance/spine-audit.js +152 -0
- package/dist/records/closeout.js +2 -0
- package/dist/records/harvest.js +236 -0
- package/dist/records/index.js +3 -0
- package/dist/records/one-shot-runs.js +421 -0
- package/dist/records/promotion.js +199 -0
- package/dist/shared/artifacts-core.js +88 -0
- package/dist/shared/artifacts.js +2 -0
- package/dist/shared/context-files.js +32 -0
- package/dist/shared/context.js +2 -0
- package/dist/shared/copy-dir.js +17 -0
- package/dist/shared/git-progress.js +165 -0
- package/dist/shared/index.js +5 -0
- package/dist/shared/logger.js +23 -0
- package/dist/shared/one-shot-prompt-args.js +98 -0
- package/dist/shared/path-refs.js +31 -0
- package/dist/shared/prompts.js +26 -0
- package/dist/shared/reference-context.js +238 -0
- package/dist/shared/timeout-policy.js +19 -0
- package/dist/shared/timeout.js +1 -0
- package/dist/shared/types.js +5 -0
- package/dist/task/config-types.js +97 -0
- package/dist/task/config.js +2 -0
- package/dist/task/delegate.js +220 -0
- package/dist/task/goal-audit.js +51 -0
- package/dist/task/goal-policy.js +8 -0
- package/dist/task/goal.js +3 -0
- package/dist/task/ids.js +1 -0
- package/dist/task/index.js +9 -0
- package/dist/task/lifecycle.js +1 -0
- package/dist/task/paths.js +1 -0
- package/dist/task/read-model.js +149 -0
- package/dist/task/runtime.js +699 -0
- package/dist/task/source-state.js +1 -0
- package/dist/task/state.js +55 -0
- package/dist/task/subagent-guidance.js +1 -0
- package/dist/task/workflow-state-types.js +92 -0
- package/dist/task/worktree-cleanup.js +140 -0
- package/dist/task/worktree.js +171 -0
- package/dist/workflows/dag/authoring.js +8 -0
- package/dist/workflows/dag/authority-surface.js +138 -0
- package/dist/workflows/dag/canvas-observer.js +474 -0
- package/dist/workflows/dag/decision-envelope.js +502 -0
- package/dist/workflows/dag/decision-evidence.js +153 -0
- package/dist/workflows/dag/decision-gates.js +1 -0
- package/dist/workflows/dag/executor-registry.js +25 -0
- package/dist/workflows/dag/facts.js +4 -0
- package/dist/workflows/dag/failure-category.js +111 -0
- package/dist/workflows/dag/final-verification.js +180 -0
- package/dist/workflows/dag/governance-constants.js +5 -0
- package/dist/workflows/dag/governance-profile.js +405 -0
- package/dist/workflows/dag/index.js +6 -0
- package/dist/workflows/dag/init-hybrid.js +855 -0
- package/dist/workflows/dag/knowledge-curator.js +162 -0
- package/dist/workflows/dag/lifecycle.js +484 -0
- package/dist/workflows/dag/prompt-source.js +88 -0
- package/dist/workflows/dag/prompt.js +130 -0
- package/dist/workflows/dag/reconcile-tasks.js +404 -0
- package/dist/workflows/dag/recovery-recommendation.js +226 -0
- package/dist/workflows/dag/repair-artifact.js +136 -0
- package/dist/workflows/dag/report.js +1019 -0
- package/dist/workflows/dag/runner.js +1677 -0
- package/dist/workflows/dag/runtime.js +5 -0
- package/dist/workflows/dag/skill-instructions.js +471 -0
- package/dist/workflows/dag/skills.js +41 -0
- package/dist/workflows/dag/spec.js +3 -0
- package/dist/workflows/dag/topo.js +30 -0
- package/dist/workflows/dag/types.js +275 -0
- package/dist/workflows/dag/upstream-artifacts.js +95 -0
- package/dist/workflows/dag/validate.js +527 -0
- package/dist/workflows/dynamic/artifacts.js +65 -0
- package/dist/workflows/dynamic/compile.js +360 -0
- package/dist/workflows/dynamic/compileTypes.js +1 -0
- package/dist/workflows/dynamic/errors.js +5 -0
- package/dist/workflows/dynamic/index.js +7 -0
- package/dist/workflows/dynamic/profiles.js +156 -0
- package/dist/workflows/dynamic/spec.js +114 -0
- package/dist/workflows/dynamic/validate.js +275 -0
- package/dist/workflows/loop/actions.js +1334 -0
- package/dist/workflows/loop/benchmark.js +510 -0
- package/dist/workflows/loop/closeout.js +134 -0
- package/dist/workflows/loop/context.js +48 -0
- package/dist/workflows/loop/events.js +25 -0
- package/dist/workflows/loop/hash.js +32 -0
- package/dist/workflows/loop/index.js +8 -0
- package/dist/workflows/loop/paths.js +17 -0
- package/dist/workflows/loop/rounds.js +81 -0
- package/dist/workflows/loop/signals.js +55 -0
- package/dist/workflows/loop/state.js +116 -0
- package/dist/workflows/loop/templates.js +54 -0
- package/dist/workflows/loop/types.js +28 -0
- package/docs/README.md +62 -0
- package/docs/agent-dag-recovery-playbook.md +158 -0
- package/docs/agent-dag-runner.md +40 -0
- package/docs/cursor-executor-usage.md +25 -0
- package/docs/decisions/README.md +3 -0
- package/docs/design/README.md +36 -0
- package/docs/development-principles.md +71 -0
- package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -0
- package/docs/exec-plans/README.md +6 -0
- package/docs/exec-plans/active/README.md +5 -0
- package/docs/exec-plans/completed/README.md +5 -0
- package/docs/feature-workflow.md +184 -0
- package/docs/harness-methodology-debugging.md +153 -0
- package/docs/harness-methodology-tdd.md +130 -0
- package/docs/harness-methodology-verification.md +27 -0
- package/docs/loop-agent-harness.md +42 -0
- package/docs/progress/README.md +3 -0
- package/docs/reports/README.md +3 -0
- package/docs/templates/adr.md +60 -0
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -0
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -0
- package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -0
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -0
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -0
- package/docs/templates/agent-dag-report.schema.json +423 -0
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -0
- package/docs/templates/agent-dag.base.json +195 -0
- package/docs/templates/agent-dag.final-verification.json +190 -0
- package/docs/templates/agent-dag.schema.json +316 -0
- package/docs/templates/agent-dag.supervised-implementation.json +500 -0
- package/docs/templates/exec-plan.md +64 -0
- package/docs/templates/feature-spec.md +53 -0
- package/docs/templates/hybrid-dag.json +193 -0
- package/docs/templates/progress-log.md +17 -0
- package/docs/templates/project-start-checklist.md +9 -0
- package/docs/templates/qa-report.md +42 -0
- package/docs/templates/sprint-contract.md +29 -0
- package/docs/verification-matrix.md +30 -0
- package/examples/decision-gate-agent-dag.json +123 -0
- package/examples/example-dag.json +51 -0
- package/examples/hybrid-loop-agent-dag.json +194 -0
- package/harness.json +92 -0
- package/package.json +61 -0
- package/skills/ai-engineering-context/SKILL.md +48 -0
- package/skills/loop-agent/SKILL.md +260 -0
- package/skills/loop-agent/references/README.md +63 -0
- package/skills/loop-agent/references/command-reference.md +315 -0
- package/skills/loop-agent/references/harness-policy.md +258 -0
- package/skills/loop-agent/references/hybrid-dag.md +216 -0
- package/skills/loop-agent/references/learned/README.md +21 -0
- package/skills/loop-agent/references/model-routing.md +36 -0
- package/skills/loop-agent/references/multi-worktree.md +54 -0
- package/skills/loop-agent/references/one-shot-runs.md +85 -0
- package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -0
- package/skills/loop-agent/references/pi-prompt.md +23 -0
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +83 -0
- package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -0
- package/skills/loop-agent/references/task-workflow.md +84 -0
- package/skills/loop-agent/references/verification-and-failure-handling.md +74 -0
- package/skills/requesting-code-review/SKILL.md +101 -0
- package/skills/requesting-code-review/code-reviewer.md +168 -0
- package/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/skills/systematic-debugging/SKILL.md +296 -0
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/skills/systematic-debugging/find-polluter.sh +63 -0
- package/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/skills/systematic-debugging/test-academic.md +14 -0
- package/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/skills/verification-before-completion/SKILL.md +154 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const CONTROL_GROUPS = [
|
|
4
|
+
"single-repair",
|
|
5
|
+
"3-pass-convergence",
|
|
6
|
+
"3-pass-convergence+quota",
|
|
7
|
+
];
|
|
8
|
+
const METRICS_SCHEMA = [
|
|
9
|
+
"pass",
|
|
10
|
+
"fullVerifyPass",
|
|
11
|
+
"wallTimeMs",
|
|
12
|
+
"tokenEstimate",
|
|
13
|
+
"executorCalls",
|
|
14
|
+
"humanPause",
|
|
15
|
+
"regressionPause",
|
|
16
|
+
"changedPaths",
|
|
17
|
+
"repairPasses",
|
|
18
|
+
];
|
|
19
|
+
function result(group, metrics, evidenceRefs, notes) {
|
|
20
|
+
return { group, metrics, evidenceRefs, notes };
|
|
21
|
+
}
|
|
22
|
+
function sample(id, title, taskClass, results) {
|
|
23
|
+
return {
|
|
24
|
+
id,
|
|
25
|
+
title,
|
|
26
|
+
taskClass,
|
|
27
|
+
evidenceType: "deterministic-fixture",
|
|
28
|
+
results,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export const LOOP_BENCHMARK_FIXTURE_SAMPLES = [
|
|
32
|
+
sample("LB-01", "hard verify fails twice then pass 3 succeeds", "dag-runtime", [
|
|
33
|
+
result("single-repair", {
|
|
34
|
+
pass: false,
|
|
35
|
+
fullVerifyPass: false,
|
|
36
|
+
wallTimeMs: 118000,
|
|
37
|
+
tokenEstimate: 6400,
|
|
38
|
+
executorCalls: 5,
|
|
39
|
+
humanPause: false,
|
|
40
|
+
regressionPause: false,
|
|
41
|
+
changedPaths: 2,
|
|
42
|
+
repairPasses: 1,
|
|
43
|
+
}, ["./test/dag-runner.test.ts"], "Single repair exhausts before the third successful hard verification."),
|
|
44
|
+
result("3-pass-convergence", {
|
|
45
|
+
pass: true,
|
|
46
|
+
fullVerifyPass: true,
|
|
47
|
+
wallTimeMs: 236000,
|
|
48
|
+
tokenEstimate: 13700,
|
|
49
|
+
executorCalls: 11,
|
|
50
|
+
humanPause: false,
|
|
51
|
+
regressionPause: false,
|
|
52
|
+
changedPaths: 2,
|
|
53
|
+
repairPasses: 3,
|
|
54
|
+
}, ["./test/dag-runner.test.ts"], "Convergence re-enters supervisor, repair, and hard verify until pass 3."),
|
|
55
|
+
result("3-pass-convergence+quota", {
|
|
56
|
+
pass: true,
|
|
57
|
+
fullVerifyPass: true,
|
|
58
|
+
wallTimeMs: 172000,
|
|
59
|
+
tokenEstimate: 9800,
|
|
60
|
+
executorCalls: 11,
|
|
61
|
+
humanPause: false,
|
|
62
|
+
regressionPause: false,
|
|
63
|
+
changedPaths: 2,
|
|
64
|
+
repairPasses: 3,
|
|
65
|
+
}, [
|
|
66
|
+
"./test/dag-runner.test.ts",
|
|
67
|
+
"./test/verify-step.test.ts",
|
|
68
|
+
], "Intermediate verification uses quota while final hard gate remains full."),
|
|
69
|
+
]),
|
|
70
|
+
sample("LB-02", "shell success count regression pauses run", "dag-runtime", [
|
|
71
|
+
result("single-repair", {
|
|
72
|
+
pass: false,
|
|
73
|
+
fullVerifyPass: false,
|
|
74
|
+
wallTimeMs: 94000,
|
|
75
|
+
tokenEstimate: 5100,
|
|
76
|
+
executorCalls: 5,
|
|
77
|
+
humanPause: false,
|
|
78
|
+
regressionPause: false,
|
|
79
|
+
changedPaths: 3,
|
|
80
|
+
repairPasses: 1,
|
|
81
|
+
}, ["./test/dag-runner.test.ts"], "Single repair ends failed without enough evidence to classify regression."),
|
|
82
|
+
result("3-pass-convergence", {
|
|
83
|
+
pass: false,
|
|
84
|
+
fullVerifyPass: false,
|
|
85
|
+
wallTimeMs: 149000,
|
|
86
|
+
tokenEstimate: 8200,
|
|
87
|
+
executorCalls: 8,
|
|
88
|
+
humanPause: true,
|
|
89
|
+
regressionPause: true,
|
|
90
|
+
changedPaths: 3,
|
|
91
|
+
repairPasses: 2,
|
|
92
|
+
}, ["./test/dag-runner.test.ts"], "Convergence pauses instead of retrying blindly after regression signal."),
|
|
93
|
+
result("3-pass-convergence+quota", {
|
|
94
|
+
pass: false,
|
|
95
|
+
fullVerifyPass: false,
|
|
96
|
+
wallTimeMs: 121000,
|
|
97
|
+
tokenEstimate: 6900,
|
|
98
|
+
executorCalls: 8,
|
|
99
|
+
humanPause: true,
|
|
100
|
+
regressionPause: true,
|
|
101
|
+
changedPaths: 3,
|
|
102
|
+
repairPasses: 2,
|
|
103
|
+
}, ["./test/dag-runner.test.ts"], "Quota reduces intermediate cost but still pauses on regression."),
|
|
104
|
+
]),
|
|
105
|
+
sample("LB-03", "write guard hard failure must not retry", "dag-runtime", [
|
|
106
|
+
result("single-repair", {
|
|
107
|
+
pass: false,
|
|
108
|
+
fullVerifyPass: false,
|
|
109
|
+
wallTimeMs: 42000,
|
|
110
|
+
tokenEstimate: 2200,
|
|
111
|
+
executorCalls: 4,
|
|
112
|
+
humanPause: false,
|
|
113
|
+
regressionPause: false,
|
|
114
|
+
changedPaths: 1,
|
|
115
|
+
repairPasses: 1,
|
|
116
|
+
}, ["./test/dag-runner.test.ts"], "Write-guard failure remains terminal."),
|
|
117
|
+
result("3-pass-convergence", {
|
|
118
|
+
pass: false,
|
|
119
|
+
fullVerifyPass: false,
|
|
120
|
+
wallTimeMs: 43000,
|
|
121
|
+
tokenEstimate: 2300,
|
|
122
|
+
executorCalls: 4,
|
|
123
|
+
humanPause: false,
|
|
124
|
+
regressionPause: false,
|
|
125
|
+
changedPaths: 1,
|
|
126
|
+
repairPasses: 1,
|
|
127
|
+
}, ["./test/dag-runner.test.ts"], "Non-retry class is not re-entered."),
|
|
128
|
+
result("3-pass-convergence+quota", {
|
|
129
|
+
pass: false,
|
|
130
|
+
fullVerifyPass: false,
|
|
131
|
+
wallTimeMs: 43000,
|
|
132
|
+
tokenEstimate: 2300,
|
|
133
|
+
executorCalls: 4,
|
|
134
|
+
humanPause: false,
|
|
135
|
+
regressionPause: false,
|
|
136
|
+
changedPaths: 1,
|
|
137
|
+
repairPasses: 1,
|
|
138
|
+
}, ["./test/dag-runner.test.ts"], "Quota strategy does not change write-guard retry policy."),
|
|
139
|
+
]),
|
|
140
|
+
sample("LB-04", "quota final gate must stay full", "loop-runtime", [
|
|
141
|
+
result("single-repair", {
|
|
142
|
+
pass: true,
|
|
143
|
+
fullVerifyPass: true,
|
|
144
|
+
wallTimeMs: 78000,
|
|
145
|
+
tokenEstimate: 2600,
|
|
146
|
+
executorCalls: 2,
|
|
147
|
+
humanPause: false,
|
|
148
|
+
regressionPause: false,
|
|
149
|
+
changedPaths: 1,
|
|
150
|
+
repairPasses: 1,
|
|
151
|
+
}, ["./test/verify-step.test.ts"], "Baseline full verification passes."),
|
|
152
|
+
result("3-pass-convergence", {
|
|
153
|
+
pass: true,
|
|
154
|
+
fullVerifyPass: true,
|
|
155
|
+
wallTimeMs: 79000,
|
|
156
|
+
tokenEstimate: 2700,
|
|
157
|
+
executorCalls: 2,
|
|
158
|
+
humanPause: false,
|
|
159
|
+
regressionPause: false,
|
|
160
|
+
changedPaths: 1,
|
|
161
|
+
repairPasses: 1,
|
|
162
|
+
}, ["./test/verify-step.test.ts"], "No retry needed."),
|
|
163
|
+
result("3-pass-convergence+quota", {
|
|
164
|
+
pass: true,
|
|
165
|
+
fullVerifyPass: true,
|
|
166
|
+
wallTimeMs: 52000,
|
|
167
|
+
tokenEstimate: 1900,
|
|
168
|
+
executorCalls: 3,
|
|
169
|
+
humanPause: false,
|
|
170
|
+
regressionPause: false,
|
|
171
|
+
changedPaths: 1,
|
|
172
|
+
repairPasses: 1,
|
|
173
|
+
}, ["./test/verify-step.test.ts"], "Intermediate quota is followed by full final gate."),
|
|
174
|
+
]),
|
|
175
|
+
sample("LB-05", "loop auto cursor fix remains opt-in", "loop-runtime", [
|
|
176
|
+
result("single-repair", {
|
|
177
|
+
pass: false,
|
|
178
|
+
fullVerifyPass: false,
|
|
179
|
+
wallTimeMs: 31000,
|
|
180
|
+
tokenEstimate: 1800,
|
|
181
|
+
executorCalls: 2,
|
|
182
|
+
humanPause: true,
|
|
183
|
+
regressionPause: false,
|
|
184
|
+
changedPaths: 0,
|
|
185
|
+
repairPasses: 0,
|
|
186
|
+
}, ["./test/loop-workflow.test.ts"], "Default auto loop will not write."),
|
|
187
|
+
result("3-pass-convergence", {
|
|
188
|
+
pass: true,
|
|
189
|
+
fullVerifyPass: true,
|
|
190
|
+
wallTimeMs: 96000,
|
|
191
|
+
tokenEstimate: 5200,
|
|
192
|
+
executorCalls: 5,
|
|
193
|
+
humanPause: false,
|
|
194
|
+
regressionPause: false,
|
|
195
|
+
changedPaths: 1,
|
|
196
|
+
repairPasses: 1,
|
|
197
|
+
}, ["./test/loop-workflow.test.ts"], "Approval or enabled policy triggers bounded cursor-fix."),
|
|
198
|
+
result("3-pass-convergence+quota", {
|
|
199
|
+
pass: true,
|
|
200
|
+
fullVerifyPass: true,
|
|
201
|
+
wallTimeMs: 76000,
|
|
202
|
+
tokenEstimate: 4400,
|
|
203
|
+
executorCalls: 5,
|
|
204
|
+
humanPause: false,
|
|
205
|
+
regressionPause: false,
|
|
206
|
+
changedPaths: 1,
|
|
207
|
+
repairPasses: 1,
|
|
208
|
+
}, ["./test/loop-workflow.test.ts"], "Quota only affects verify rounds after bounded write."),
|
|
209
|
+
]),
|
|
210
|
+
sample("LB-06", "structured fix brief feeds repair", "loop-runtime", [
|
|
211
|
+
result("single-repair", {
|
|
212
|
+
pass: false,
|
|
213
|
+
fullVerifyPass: false,
|
|
214
|
+
wallTimeMs: 70000,
|
|
215
|
+
tokenEstimate: 4200,
|
|
216
|
+
executorCalls: 3,
|
|
217
|
+
humanPause: false,
|
|
218
|
+
regressionPause: false,
|
|
219
|
+
changedPaths: 1,
|
|
220
|
+
repairPasses: 1,
|
|
221
|
+
}, ["./test/auto-run-loop.test.ts"], "Raw failure summary alone does not encode invariant or scope."),
|
|
222
|
+
result("3-pass-convergence", {
|
|
223
|
+
pass: true,
|
|
224
|
+
fullVerifyPass: true,
|
|
225
|
+
wallTimeMs: 112000,
|
|
226
|
+
tokenEstimate: 6600,
|
|
227
|
+
executorCalls: 6,
|
|
228
|
+
humanPause: false,
|
|
229
|
+
regressionPause: false,
|
|
230
|
+
changedPaths: 1,
|
|
231
|
+
repairPasses: 2,
|
|
232
|
+
}, ["./test/auto-run-loop.test.ts"], "Structured category, root cause, scope, and invariant make pass 2 succeed."),
|
|
233
|
+
result("3-pass-convergence+quota", {
|
|
234
|
+
pass: true,
|
|
235
|
+
fullVerifyPass: true,
|
|
236
|
+
wallTimeMs: 91000,
|
|
237
|
+
tokenEstimate: 5700,
|
|
238
|
+
executorCalls: 6,
|
|
239
|
+
humanPause: false,
|
|
240
|
+
regressionPause: false,
|
|
241
|
+
changedPaths: 1,
|
|
242
|
+
repairPasses: 2,
|
|
243
|
+
}, ["./test/auto-run-loop.test.ts"], "Structured fix brief plus quota keeps final verification strict."),
|
|
244
|
+
]),
|
|
245
|
+
sample("LB-07", "learned guidance injection is bounded", "skill-policy", [
|
|
246
|
+
result("single-repair", {
|
|
247
|
+
pass: true,
|
|
248
|
+
fullVerifyPass: true,
|
|
249
|
+
wallTimeMs: 54000,
|
|
250
|
+
tokenEstimate: 3300,
|
|
251
|
+
executorCalls: 2,
|
|
252
|
+
humanPause: false,
|
|
253
|
+
regressionPause: false,
|
|
254
|
+
changedPaths: 1,
|
|
255
|
+
repairPasses: 1,
|
|
256
|
+
}, ["./test/dag-skills.test.ts"], "Baseline prompt has no learned guidance."),
|
|
257
|
+
result("3-pass-convergence", {
|
|
258
|
+
pass: true,
|
|
259
|
+
fullVerifyPass: true,
|
|
260
|
+
wallTimeMs: 53000,
|
|
261
|
+
tokenEstimate: 3500,
|
|
262
|
+
executorCalls: 2,
|
|
263
|
+
humanPause: false,
|
|
264
|
+
regressionPause: false,
|
|
265
|
+
changedPaths: 1,
|
|
266
|
+
repairPasses: 1,
|
|
267
|
+
}, ["./test/dag-skills.test.ts"], "Learned guidance injection remains advisory and top-3 bounded."),
|
|
268
|
+
result("3-pass-convergence+quota", {
|
|
269
|
+
pass: true,
|
|
270
|
+
fullVerifyPass: true,
|
|
271
|
+
wallTimeMs: 48000,
|
|
272
|
+
tokenEstimate: 3200,
|
|
273
|
+
executorCalls: 2,
|
|
274
|
+
humanPause: false,
|
|
275
|
+
regressionPause: false,
|
|
276
|
+
changedPaths: 1,
|
|
277
|
+
repairPasses: 1,
|
|
278
|
+
}, ["./test/dag-skills.test.ts"], "Quota has no effect on learned guidance safety."),
|
|
279
|
+
]),
|
|
280
|
+
sample("LB-08", "supervised template exposes structured repair contract", "dag-runtime", [
|
|
281
|
+
result("single-repair", {
|
|
282
|
+
pass: true,
|
|
283
|
+
fullVerifyPass: true,
|
|
284
|
+
wallTimeMs: 61000,
|
|
285
|
+
tokenEstimate: 3000,
|
|
286
|
+
executorCalls: 2,
|
|
287
|
+
humanPause: false,
|
|
288
|
+
regressionPause: false,
|
|
289
|
+
changedPaths: 0,
|
|
290
|
+
repairPasses: 0,
|
|
291
|
+
}, ["./test/dag-supervised-template.test.ts"], "Template contract exists."),
|
|
292
|
+
result("3-pass-convergence", {
|
|
293
|
+
pass: true,
|
|
294
|
+
fullVerifyPass: true,
|
|
295
|
+
wallTimeMs: 62000,
|
|
296
|
+
tokenEstimate: 3100,
|
|
297
|
+
executorCalls: 2,
|
|
298
|
+
humanPause: false,
|
|
299
|
+
regressionPause: false,
|
|
300
|
+
changedPaths: 0,
|
|
301
|
+
repairPasses: 0,
|
|
302
|
+
}, ["./test/dag-supervised-template.test.ts"], "Structured fields are required across supervised convergence."),
|
|
303
|
+
result("3-pass-convergence+quota", {
|
|
304
|
+
pass: true,
|
|
305
|
+
fullVerifyPass: true,
|
|
306
|
+
wallTimeMs: 58000,
|
|
307
|
+
tokenEstimate: 2900,
|
|
308
|
+
executorCalls: 2,
|
|
309
|
+
humanPause: false,
|
|
310
|
+
regressionPause: false,
|
|
311
|
+
changedPaths: 0,
|
|
312
|
+
repairPasses: 0,
|
|
313
|
+
}, ["./test/dag-supervised-template.test.ts"], "Quota does not weaken the repair contract."),
|
|
314
|
+
]),
|
|
315
|
+
sample("LB-09", "task config stays backward compatible", "docs-governance", [
|
|
316
|
+
result("single-repair", {
|
|
317
|
+
pass: true,
|
|
318
|
+
fullVerifyPass: true,
|
|
319
|
+
wallTimeMs: 47000,
|
|
320
|
+
tokenEstimate: 2100,
|
|
321
|
+
executorCalls: 1,
|
|
322
|
+
humanPause: false,
|
|
323
|
+
regressionPause: false,
|
|
324
|
+
changedPaths: 0,
|
|
325
|
+
repairPasses: 0,
|
|
326
|
+
}, ["./test/task-runtime.test.ts"], "Old task.json parses without convergence fields."),
|
|
327
|
+
result("3-pass-convergence", {
|
|
328
|
+
pass: true,
|
|
329
|
+
fullVerifyPass: true,
|
|
330
|
+
wallTimeMs: 48000,
|
|
331
|
+
tokenEstimate: 2200,
|
|
332
|
+
executorCalls: 1,
|
|
333
|
+
humanPause: false,
|
|
334
|
+
regressionPause: false,
|
|
335
|
+
changedPaths: 0,
|
|
336
|
+
repairPasses: 0,
|
|
337
|
+
}, ["./test/task-runtime.test.ts"], "Convergence config remains optional."),
|
|
338
|
+
result("3-pass-convergence+quota", {
|
|
339
|
+
pass: true,
|
|
340
|
+
fullVerifyPass: true,
|
|
341
|
+
wallTimeMs: 46000,
|
|
342
|
+
tokenEstimate: 2100,
|
|
343
|
+
executorCalls: 1,
|
|
344
|
+
humanPause: false,
|
|
345
|
+
regressionPause: false,
|
|
346
|
+
changedPaths: 0,
|
|
347
|
+
repairPasses: 0,
|
|
348
|
+
}, ["./test/task-runtime.test.ts"], "Quota config remains optional."),
|
|
349
|
+
]),
|
|
350
|
+
sample("LB-10", "handoff coverage stays separate from completion", "docs-governance", [
|
|
351
|
+
result("single-repair", {
|
|
352
|
+
pass: false,
|
|
353
|
+
fullVerifyPass: false,
|
|
354
|
+
wallTimeMs: 39000,
|
|
355
|
+
tokenEstimate: 1700,
|
|
356
|
+
executorCalls: 1,
|
|
357
|
+
humanPause: true,
|
|
358
|
+
regressionPause: false,
|
|
359
|
+
changedPaths: 0,
|
|
360
|
+
repairPasses: 0,
|
|
361
|
+
}, ["./test/requirement-coverage.test.ts"], "Missing acceptance coverage blocks handoff."),
|
|
362
|
+
result("3-pass-convergence", {
|
|
363
|
+
pass: false,
|
|
364
|
+
fullVerifyPass: false,
|
|
365
|
+
wallTimeMs: 40000,
|
|
366
|
+
tokenEstimate: 1800,
|
|
367
|
+
executorCalls: 1,
|
|
368
|
+
humanPause: true,
|
|
369
|
+
regressionPause: false,
|
|
370
|
+
changedPaths: 0,
|
|
371
|
+
repairPasses: 0,
|
|
372
|
+
}, ["./test/requirement-coverage.test.ts"], "Extra passes do not bypass missing acceptance coverage."),
|
|
373
|
+
result("3-pass-convergence+quota", {
|
|
374
|
+
pass: false,
|
|
375
|
+
fullVerifyPass: false,
|
|
376
|
+
wallTimeMs: 40000,
|
|
377
|
+
tokenEstimate: 1800,
|
|
378
|
+
executorCalls: 1,
|
|
379
|
+
humanPause: true,
|
|
380
|
+
regressionPause: false,
|
|
381
|
+
changedPaths: 0,
|
|
382
|
+
repairPasses: 0,
|
|
383
|
+
}, ["./test/requirement-coverage.test.ts"], "Quota does not bypass requirement coverage."),
|
|
384
|
+
]),
|
|
385
|
+
];
|
|
386
|
+
export function aggregateLoopBenchmark(samples) {
|
|
387
|
+
return CONTROL_GROUPS.map((group) => {
|
|
388
|
+
const results = samples.flatMap((entry) => entry.results.filter((item) => item.group === group));
|
|
389
|
+
const changedPathsTotal = results.reduce((sum, item) => sum + item.metrics.changedPaths, 0);
|
|
390
|
+
return {
|
|
391
|
+
group,
|
|
392
|
+
sampleCount: results.length,
|
|
393
|
+
passCount: results.filter((item) => item.metrics.pass).length,
|
|
394
|
+
fullVerifyPassCount: results.filter((item) => item.metrics.fullVerifyPass).length,
|
|
395
|
+
humanPauseCount: results.filter((item) => item.metrics.humanPause)
|
|
396
|
+
.length,
|
|
397
|
+
regressionPauseCount: results.filter((item) => item.metrics.regressionPause).length,
|
|
398
|
+
totalWallTimeMs: results.reduce((sum, item) => sum + item.metrics.wallTimeMs, 0),
|
|
399
|
+
totalTokenEstimate: results.reduce((sum, item) => sum + item.metrics.tokenEstimate, 0),
|
|
400
|
+
totalExecutorCalls: results.reduce((sum, item) => sum + item.metrics.executorCalls, 0),
|
|
401
|
+
avgChangedPaths: results.length
|
|
402
|
+
? Number((changedPathsTotal / results.length).toFixed(2))
|
|
403
|
+
: 0,
|
|
404
|
+
};
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
export function buildLoopBenchmarkResult(options = {}) {
|
|
408
|
+
const samples = options.samples ?? LOOP_BENCHMARK_FIXTURE_SAMPLES;
|
|
409
|
+
const aggregates = aggregateLoopBenchmark(samples);
|
|
410
|
+
const single = aggregates.find((entry) => entry.group === "single-repair");
|
|
411
|
+
const quota = aggregates.find((entry) => entry.group === "3-pass-convergence+quota");
|
|
412
|
+
const deterministicOnly = samples.every((entry) => entry.evidenceType === "deterministic-fixture");
|
|
413
|
+
const rationale = [
|
|
414
|
+
"Deterministic fixtures cover the three control groups across 10 task samples.",
|
|
415
|
+
"At least one fixture shows failed repair becoming successful only under three-pass convergence.",
|
|
416
|
+
"Live Pi/Cursor dogfood evidence is not present in this benchmark artifact.",
|
|
417
|
+
"convergence.enabled remains opt-in; this benchmark does not justify a global default change.",
|
|
418
|
+
];
|
|
419
|
+
const recommendation = deterministicOnly || !single || !quota
|
|
420
|
+
? "maintain-opt-in"
|
|
421
|
+
: quota.passCount >= single.passCount
|
|
422
|
+
? "expand-live-sample"
|
|
423
|
+
: "maintain-opt-in";
|
|
424
|
+
return {
|
|
425
|
+
command: "loop-benchmark",
|
|
426
|
+
generatedAt: options.generatedAt ?? new Date().toISOString(),
|
|
427
|
+
liveCallsPerformed: false,
|
|
428
|
+
sampleCount: samples.length,
|
|
429
|
+
controlGroups: CONTROL_GROUPS,
|
|
430
|
+
metricsSchema: METRICS_SCHEMA,
|
|
431
|
+
samples,
|
|
432
|
+
aggregates,
|
|
433
|
+
liveDogfood: {
|
|
434
|
+
status: "blocked",
|
|
435
|
+
reason: "No live Pi/Cursor executor run refs were supplied to this deterministic M0 baseline.",
|
|
436
|
+
requiredEvidence: [
|
|
437
|
+
"completed DAG run ref with convergence.enabled=true",
|
|
438
|
+
"single-repair comparison run ref for the same task",
|
|
439
|
+
"3-pass+quota comparison run ref for the same task",
|
|
440
|
+
],
|
|
441
|
+
},
|
|
442
|
+
recommendation,
|
|
443
|
+
rationale,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function formatBool(value) {
|
|
447
|
+
return value ? "yes" : "no";
|
|
448
|
+
}
|
|
449
|
+
function formatMs(value) {
|
|
450
|
+
return `${Math.round(value / 1000)}s`;
|
|
451
|
+
}
|
|
452
|
+
export function formatLoopBenchmarkMarkdown(result) {
|
|
453
|
+
const lines = [
|
|
454
|
+
"# loop-agent Loop Benchmark Baseline",
|
|
455
|
+
"",
|
|
456
|
+
`Date: ${result.generatedAt.slice(0, 10)}`,
|
|
457
|
+
"",
|
|
458
|
+
"## Scope",
|
|
459
|
+
"",
|
|
460
|
+
"This report is the M0 deterministic baseline for `docs/exec-plans/active/2026-06-30-loop-agent-loop-evolution-roadmap.md`.",
|
|
461
|
+
"It compares `single-repair`, `3-pass-convergence`, and `3-pass-convergence+quota` on fixture-backed loop-agent workflow samples.",
|
|
462
|
+
"It performs no live Pi or Cursor calls and must not be treated as statistical evidence for enabling convergence by default.",
|
|
463
|
+
"",
|
|
464
|
+
"## Command",
|
|
465
|
+
"",
|
|
466
|
+
"```bash",
|
|
467
|
+
"cd .",
|
|
468
|
+
"npm run dev -- loop-benchmark --markdown --output docs/reports/2026-06-30-loop-agent-loop-benchmark.md",
|
|
469
|
+
"```",
|
|
470
|
+
"",
|
|
471
|
+
"## Recommendation",
|
|
472
|
+
"",
|
|
473
|
+
`recommendation | ${result.recommendation}`,
|
|
474
|
+
`liveCallsPerformed | ${String(result.liveCallsPerformed)}`,
|
|
475
|
+
`liveDogfood | ${result.liveDogfood.status}`,
|
|
476
|
+
`sampleCount | ${result.sampleCount}`,
|
|
477
|
+
"",
|
|
478
|
+
"Rationale:",
|
|
479
|
+
...result.rationale.map((item) => `- ${item}`),
|
|
480
|
+
"",
|
|
481
|
+
"## Metrics Schema",
|
|
482
|
+
"",
|
|
483
|
+
...result.metricsSchema.map((item) => `- ${item}`),
|
|
484
|
+
"",
|
|
485
|
+
"## Aggregate Results",
|
|
486
|
+
"",
|
|
487
|
+
"| Control Group | Samples | Pass | Full Verify Pass | Human Pause | Regression Pause | Wall Time | Token Estimate | Executor Calls | Avg Changed Paths |",
|
|
488
|
+
"|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|",
|
|
489
|
+
...result.aggregates.map((entry) => `| ${entry.group} | ${entry.sampleCount} | ${entry.passCount} | ${entry.fullVerifyPassCount} | ${entry.humanPauseCount} | ${entry.regressionPauseCount} | ${formatMs(entry.totalWallTimeMs)} | ${entry.totalTokenEstimate} | ${entry.totalExecutorCalls} | ${entry.avgChangedPaths} |`),
|
|
490
|
+
"",
|
|
491
|
+
"## Sample Matrix",
|
|
492
|
+
"",
|
|
493
|
+
"| Sample | Class | Evidence | Control Group | Pass | Full Verify | Repair Passes | Evidence Refs | Notes |",
|
|
494
|
+
"|---|---|---|---|---|---|---:|---|---|",
|
|
495
|
+
];
|
|
496
|
+
for (const entry of result.samples) {
|
|
497
|
+
for (const item of entry.results) {
|
|
498
|
+
lines.push(`| ${entry.id} ${entry.title} | ${entry.taskClass} | ${entry.evidenceType} | ${item.group} | ${formatBool(item.metrics.pass)} | ${formatBool(item.metrics.fullVerifyPass)} | ${item.metrics.repairPasses} | ${item.evidenceRefs.join("<br>")} | ${item.notes} |`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
lines.push("", "## Live Dogfood Status", "", `Status: ${result.liveDogfood.status}`, "", result.liveDogfood.reason, "", "Required evidence before default expansion:", ...result.liveDogfood.requiredEvidence.map((item) => `- ${item}`), "", "## Acceptance Coverage", "", "- Benchmark report lists samples, control groups, metrics, and evidence refs.", "- Deterministic fixtures are explicitly separated from live dogfood.", "- Recommendation is go/no-go: keep convergence opt-in and expand live sample collection.", "- `LB-01` records failed single repair becoming successful under three-pass convergence.", "- This report does not change `convergence.enabled` default.", "");
|
|
502
|
+
return lines.join("\n");
|
|
503
|
+
}
|
|
504
|
+
export async function writeLoopBenchmarkMarkdown(repoRoot, outputPath, result) {
|
|
505
|
+
const resolved = path.isAbsolute(outputPath)
|
|
506
|
+
? outputPath
|
|
507
|
+
: path.resolve(repoRoot, outputPath);
|
|
508
|
+
await writeFile(resolved, formatLoopBenchmarkMarkdown(result), "utf-8");
|
|
509
|
+
return resolved;
|
|
510
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { getLoopPaths } from "./paths.js";
|
|
3
|
+
import { readLoopRounds } from "./rounds.js";
|
|
4
|
+
import { readLoopSignals, pendingLoopSignals } from "./signals.js";
|
|
5
|
+
import { loadLoopState } from "./state.js";
|
|
6
|
+
import { loadTaskConfig } from "../../task/runtime.js";
|
|
7
|
+
import { repoRelativePath } from "../../shared/path-refs.js";
|
|
8
|
+
function unique(values) {
|
|
9
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
10
|
+
}
|
|
11
|
+
function bulletList(values, fallback) {
|
|
12
|
+
const items = values.length > 0 ? values : [fallback];
|
|
13
|
+
return items.map((item) => `- ${item}`).join("\n");
|
|
14
|
+
}
|
|
15
|
+
function resultStatus(status) {
|
|
16
|
+
if (status === "complete")
|
|
17
|
+
return "complete";
|
|
18
|
+
if (status === "paused")
|
|
19
|
+
return "paused";
|
|
20
|
+
if (status === "blocked")
|
|
21
|
+
return "blocked";
|
|
22
|
+
return "partial";
|
|
23
|
+
}
|
|
24
|
+
function resolveWorkflowPath(input) {
|
|
25
|
+
if (input.complexity === "small")
|
|
26
|
+
return "micro-or-small";
|
|
27
|
+
if (input.hasDagRound)
|
|
28
|
+
return "dag";
|
|
29
|
+
if (input.dagFallbackReason.trim())
|
|
30
|
+
return "explicit-fallback";
|
|
31
|
+
return "missing-dag-evidence";
|
|
32
|
+
}
|
|
33
|
+
export async function generateLoopCloseout(repoRoot, taskId) {
|
|
34
|
+
const paths = getLoopPaths(repoRoot, taskId);
|
|
35
|
+
const [state, rounds, signals, objective, context, taskConfig] = await Promise.all([
|
|
36
|
+
loadLoopState(repoRoot, taskId),
|
|
37
|
+
readLoopRounds(repoRoot, taskId),
|
|
38
|
+
readLoopSignals(repoRoot, taskId),
|
|
39
|
+
readFile(paths.objectivePath, "utf-8").catch(() => ""),
|
|
40
|
+
readFile(paths.contextPath, "utf-8").catch(() => ""),
|
|
41
|
+
loadTaskConfig(repoRoot, taskId),
|
|
42
|
+
]);
|
|
43
|
+
const completedCriteria = unique([
|
|
44
|
+
...state.completedCriteria,
|
|
45
|
+
...rounds.flatMap((round) => round.completedCriteria),
|
|
46
|
+
]);
|
|
47
|
+
const verificationEvidence = unique(rounds.flatMap((round) => [
|
|
48
|
+
round.verification,
|
|
49
|
+
...round.refs.filter((ref) => /verification|review|run|dag|result/i.test(ref)),
|
|
50
|
+
]));
|
|
51
|
+
const pendingSignals = pendingLoopSignals(signals);
|
|
52
|
+
const hasDagRound = rounds.some((round) => round.action === "dag");
|
|
53
|
+
const dagFallbackReason = (taskConfig.dagFallbackReason ?? "").trim();
|
|
54
|
+
const workflowPath = resolveWorkflowPath({
|
|
55
|
+
complexity: taskConfig.complexity,
|
|
56
|
+
hasDagRound,
|
|
57
|
+
dagFallbackReason,
|
|
58
|
+
});
|
|
59
|
+
const remainingRisks = unique([
|
|
60
|
+
...(state.status === "complete" ? [] : state.openFrontier),
|
|
61
|
+
...(state.freshness.sourceStale ? ["Task source is stale against loop state."] : []),
|
|
62
|
+
...pendingSignals.map((signal) => `Pending ${signal.type} signal: ${signal.message}`),
|
|
63
|
+
...(workflowPath === "missing-dag-evidence"
|
|
64
|
+
? ["Complex loop has no DAG round evidence and no task dagFallbackReason."]
|
|
65
|
+
: []),
|
|
66
|
+
...(state.status === "complete"
|
|
67
|
+
? []
|
|
68
|
+
: [`Loop status is ${state.status}; treat this closeout as partial.`]),
|
|
69
|
+
]);
|
|
70
|
+
const recordedAt = new Date().toISOString();
|
|
71
|
+
const relativeCloseoutPath = repoRelativePath(repoRoot, paths.closeoutPath);
|
|
72
|
+
const record = {
|
|
73
|
+
schemaVersion: 1,
|
|
74
|
+
taskId,
|
|
75
|
+
status: state.status,
|
|
76
|
+
resultStatus: resultStatus(state.status),
|
|
77
|
+
closeoutPath: relativeCloseoutPath,
|
|
78
|
+
workflowPath,
|
|
79
|
+
dagFallbackReason,
|
|
80
|
+
completedCriteria,
|
|
81
|
+
verificationEvidence,
|
|
82
|
+
remainingRisks,
|
|
83
|
+
roundCount: rounds.length,
|
|
84
|
+
recordedAt,
|
|
85
|
+
};
|
|
86
|
+
const latestRounds = rounds
|
|
87
|
+
.slice(-5)
|
|
88
|
+
.map((round) => `- round ${round.round}: ${round.action} -> ${round.result}; next=${round.next}`)
|
|
89
|
+
.join("\n");
|
|
90
|
+
const markdown = [
|
|
91
|
+
"# Loop Closeout Draft",
|
|
92
|
+
"",
|
|
93
|
+
`taskId: ${taskId}`,
|
|
94
|
+
`status: ${record.status}`,
|
|
95
|
+
`resultStatus: ${record.resultStatus}`,
|
|
96
|
+
`workflowPath: ${record.workflowPath}`,
|
|
97
|
+
...(record.dagFallbackReason ? [`dagFallbackReason: ${record.dagFallbackReason}`] : []),
|
|
98
|
+
`rounds: ${record.roundCount}`,
|
|
99
|
+
`recordedAt: ${record.recordedAt}`,
|
|
100
|
+
"",
|
|
101
|
+
"## Workflow Path",
|
|
102
|
+
`- ${record.workflowPath}`,
|
|
103
|
+
...(record.dagFallbackReason ? [`- fallback reason: ${record.dagFallbackReason}`] : []),
|
|
104
|
+
"",
|
|
105
|
+
"## Completed Criteria",
|
|
106
|
+
bulletList(completedCriteria, "No completed criteria recorded."),
|
|
107
|
+
"",
|
|
108
|
+
"## Verification Evidence",
|
|
109
|
+
bulletList(verificationEvidence, "No concrete verification evidence recorded."),
|
|
110
|
+
"",
|
|
111
|
+
"## Remaining Risks",
|
|
112
|
+
bulletList(remainingRisks, "No remaining risks recorded."),
|
|
113
|
+
"",
|
|
114
|
+
"## Recent Rounds",
|
|
115
|
+
latestRounds || "- No rounds recorded.",
|
|
116
|
+
"",
|
|
117
|
+
"## Objective Snapshot",
|
|
118
|
+
"```md",
|
|
119
|
+
objective.trim() || "(empty)",
|
|
120
|
+
"```",
|
|
121
|
+
"",
|
|
122
|
+
"## Context Snapshot",
|
|
123
|
+
"```md",
|
|
124
|
+
context.trim() || "(empty)",
|
|
125
|
+
"```",
|
|
126
|
+
"",
|
|
127
|
+
"## Notes",
|
|
128
|
+
"- This draft is derived from loop state and executor refs.",
|
|
129
|
+
"- It does not modify completed DAG or one-shot run facts.",
|
|
130
|
+
"",
|
|
131
|
+
].join("\n");
|
|
132
|
+
await writeFile(paths.closeoutPath, markdown, "utf-8");
|
|
133
|
+
return record;
|
|
134
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { getLoopPaths } from "./paths.js";
|
|
3
|
+
import { readLoopRounds } from "./rounds.js";
|
|
4
|
+
import { refreshLoopState } from "./state.js";
|
|
5
|
+
import { appendLoopEvent } from "./events.js";
|
|
6
|
+
export async function rewriteLoopContext(repoRoot, taskId) {
|
|
7
|
+
const rounds = await readLoopRounds(repoRoot, taskId);
|
|
8
|
+
const latest = rounds.at(-1);
|
|
9
|
+
if (!latest)
|
|
10
|
+
return "";
|
|
11
|
+
const content = [
|
|
12
|
+
"# Loop Context",
|
|
13
|
+
"",
|
|
14
|
+
"## Current State",
|
|
15
|
+
`- Completed: ${latest.completedCriteria.length > 0 ? latest.completedCriteria.join("; ") : "none newly completed"}.`,
|
|
16
|
+
`- Open frontier: ${latest.next || "choose the next bounded action"}.`,
|
|
17
|
+
`- Current hypothesis: ${latest.decision === "continue" ? latest.next : `loop decision is ${latest.decision}`}.`,
|
|
18
|
+
"",
|
|
19
|
+
"## Verified Facts",
|
|
20
|
+
`- Latest action: ${latest.action}.`,
|
|
21
|
+
`- Latest result: ${latest.result}.`,
|
|
22
|
+
`- Verification: ${latest.verification || "not recorded"}.`,
|
|
23
|
+
`- Evidence refs: ${latest.refs.length > 0 ? latest.refs.join(", ") : "none"}.`,
|
|
24
|
+
"",
|
|
25
|
+
"## Last Round Lessons",
|
|
26
|
+
`- What worked: ${latest.result}.`,
|
|
27
|
+
`- What failed: ${latest.failureCategory || "not classified"}.`,
|
|
28
|
+
`- Avoid next round: ${latest.lesson}.`,
|
|
29
|
+
"",
|
|
30
|
+
"## Next Suggested Action",
|
|
31
|
+
`- Action: ${latest.next}.`,
|
|
32
|
+
`- Why: ${latest.lesson}.`,
|
|
33
|
+
"- Expected evidence: a new rounds.jsonl entry and deterministic verification when claiming completion.",
|
|
34
|
+
"",
|
|
35
|
+
"## Risk Notes",
|
|
36
|
+
`- Last decision: ${latest.decision}.`,
|
|
37
|
+
"",
|
|
38
|
+
].join("\n");
|
|
39
|
+
await writeFile(getLoopPaths(repoRoot, taskId).contextPath, content, "utf-8");
|
|
40
|
+
await refreshLoopState(repoRoot, taskId);
|
|
41
|
+
await appendLoopEvent(repoRoot, taskId, {
|
|
42
|
+
type: "context_rewrite",
|
|
43
|
+
round: latest.round,
|
|
44
|
+
message: `context rewritten from round ${latest.round}`,
|
|
45
|
+
refs: latest.refs,
|
|
46
|
+
});
|
|
47
|
+
return content;
|
|
48
|
+
}
|