@tea-agent/loop-agent 0.5.0 → 0.7.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 +142 -142
- package/CHANGELOG.md +132 -98
- package/README.md +195 -195
- package/bin/agent-worker.js +22 -22
- package/bin/loop-agent.js +21 -21
- package/dist/application/dag/args.js +9 -1
- package/dist/application/dag/run-dag.js +16 -2
- package/dist/cli/command-definitions.js +22 -4
- package/dist/cli/help.js +3 -2
- package/dist/cli/program.js +7 -5
- package/dist/commands/import-prd.js +76 -0
- package/dist/commands/init.js +467 -457
- package/dist/commands/instructions.js +90 -58
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/executors/cursor-executor.js +1 -1
- package/dist/executors/dag-pi-executor.js +1 -0
- package/dist/executors/pi-sdk-executor.js +63 -1
- package/dist/shared/preview.js +39 -0
- package/dist/task/config-types.js +3 -0
- package/dist/task/runtime.js +27 -27
- package/dist/task/source-references.js +221 -0
- package/dist/worker/cli.js +62 -1
- package/dist/worker/loop-agent/loop-agent-client.js +97 -5
- package/dist/worker/materialize/harness-task-materializer.js +166 -5
- package/dist/worker/observability/event-store.js +82 -0
- package/dist/worker/observability/events.js +79 -0
- package/dist/worker/observability/progress-composite.js +33 -0
- package/dist/worker/observability/read-model.js +1013 -0
- package/dist/worker/observability/snapshot-store.js +43 -0
- package/dist/worker/observability/types.js +1 -0
- package/dist/worker/observe/paths.js +64 -0
- package/dist/worker/observe/routes.js +423 -0
- package/dist/worker/observe/server.js +61 -0
- package/dist/worker/observe/static/app.js +1419 -0
- package/dist/worker/observe/static/index.html +63 -0
- package/dist/worker/observe/static/styles.css +613 -0
- package/dist/worker/pool/failure-routing.js +41 -6
- package/dist/worker/pool/run-store.js +50 -0
- package/dist/worker/progress-reporter.js +0 -18
- package/dist/worker/run-task/run-task.js +327 -92
- package/dist/worker/runner/run-ready.js +112 -4
- package/dist/worker/task-spec/schema.js +2 -1
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/event-observer.js +132 -0
- package/dist/workflows/dag/init-hybrid.js +182 -21
- package/dist/workflows/dag/observer-compose.js +52 -0
- package/docs/README.md +75 -72
- package/docs/agent-dag-recovery-playbook.md +184 -184
- package/docs/agent-dag-runner.md +42 -42
- package/docs/architecture/runtime-boundaries.md +162 -147
- package/docs/cursor-executor-usage.md +25 -25
- package/docs/decisions/README.md +3 -3
- package/docs/design/README.md +49 -36
- package/docs/development-principles.md +73 -73
- package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -1749
- package/docs/exec-plans/README.md +6 -6
- package/docs/exec-plans/active/README.md +12 -7
- package/docs/exec-plans/completed/README.md +32 -19
- package/docs/feature-workflow.md +186 -186
- package/docs/harness-methodology-debugging.md +153 -153
- package/docs/harness-methodology-tdd.md +130 -130
- package/docs/harness-methodology-verification.md +27 -27
- package/docs/init-surface.manifest.json +208 -199
- package/docs/loop-agent-harness.md +55 -42
- package/docs/production-readiness.md +96 -96
- package/docs/progress/README.md +3 -3
- package/docs/reports/README.md +9 -5
- package/docs/skills/README.md +6 -6
- package/docs/skills/vetted-skill-registry.md +26 -26
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -117
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +454 -454
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/agent-dag.base.json +195 -195
- package/docs/templates/agent-dag.final-verification.json +190 -190
- package/docs/templates/agent-dag.schema.json +316 -316
- package/docs/templates/agent-dag.supervised-implementation.json +500 -500
- package/docs/templates/exec-plan.md +64 -64
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/hybrid-dag.json +193 -193
- package/docs/templates/init-evolution-review.md +33 -33
- package/docs/templates/interactive-ui-round2-experiment.md +66 -0
- package/docs/templates/production-readiness-checklist.md +57 -57
- package/docs/templates/progress-log.md +17 -17
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +52 -0
- package/docs/templates/worker-dogfood-setup.md +48 -0
- package/docs/verification-matrix.md +41 -41
- package/examples/decision-gate-agent-dag.json +123 -123
- package/examples/example-dag.json +51 -51
- package/examples/hybrid-loop-agent-dag.json +194 -194
- package/harness.json +70 -69
- package/package.json +66 -66
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/init-capability-evolution/SKILL.md +69 -69
- package/skills/loop-agent/SKILL.md +149 -147
- package/skills/loop-agent/references/README.md +67 -67
- package/skills/loop-agent/references/command-reference.md +412 -403
- package/skills/loop-agent/references/harness-policy.md +263 -259
- package/skills/loop-agent/references/hybrid-dag.md +216 -216
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/long-running-loop.md +59 -59
- package/skills/loop-agent/references/model-routing.md +36 -36
- package/skills/loop-agent/references/multi-worktree.md +54 -54
- package/skills/loop-agent/references/one-shot-runs.md +85 -85
- package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -169
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +81 -81
- package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -44
- package/skills/loop-agent/references/task-workflow.md +89 -84
- package/skills/loop-agent/references/verification-and-failure-handling.md +128 -128
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/SKILL.md +296 -296
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/test-driven-development/SKILL.md +20 -20
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { appendFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { redactSecrets, truncateUtf8Preview } from "../../shared/preview.js";
|
|
5
|
+
import { resolveModelForTask, } from "./types.js";
|
|
6
|
+
const DEFAULT_MAX_OUTPUT_PREVIEW_BYTES = 4096;
|
|
7
|
+
/**
|
|
8
|
+
* Truncate `text` to fit within `maxBytes` (UTF-8), appending a marker suffix
|
|
9
|
+
* describing the number of elided bytes. Local implementation to avoid importing
|
|
10
|
+
* worker observability (which would create a circular dependency).
|
|
11
|
+
*/
|
|
12
|
+
export function truncateEventPreview(text, maxBytes = DEFAULT_MAX_OUTPUT_PREVIEW_BYTES) {
|
|
13
|
+
return truncateUtf8Preview(text, maxBytes);
|
|
14
|
+
}
|
|
15
|
+
function reportError(context, error) {
|
|
16
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
+
process.stderr.write(`[dag-event-observer] ${context} failed: ${message}\n`);
|
|
18
|
+
}
|
|
19
|
+
function findTask(spec, state, nodeId) {
|
|
20
|
+
const node = state.nodes[nodeId];
|
|
21
|
+
const task = spec?.tasks.find((candidate) => candidate.id === nodeId);
|
|
22
|
+
return { task, node };
|
|
23
|
+
}
|
|
24
|
+
function rankOf(state, nodeId) {
|
|
25
|
+
for (let i = 0; i < state.ranks.length; i += 1) {
|
|
26
|
+
if (state.ranks[i]?.includes(nodeId))
|
|
27
|
+
return String(i);
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
export function createDagEventObserver(options) {
|
|
32
|
+
const { eventsJsonlPath } = options;
|
|
33
|
+
const maxOutputPreviewBytes = options.maxOutputPreviewBytes ?? DEFAULT_MAX_OUTPUT_PREVIEW_BYTES;
|
|
34
|
+
let writeError = null;
|
|
35
|
+
const append = async (event) => {
|
|
36
|
+
try {
|
|
37
|
+
await mkdir(path.dirname(eventsJsonlPath), { recursive: true });
|
|
38
|
+
const line = `${JSON.stringify(event)}\n`;
|
|
39
|
+
await appendFile(eventsJsonlPath, line, "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
writeError = error;
|
|
43
|
+
reportError(`append ${event.type}`, error);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const buildBase = (type, state) => ({
|
|
47
|
+
schemaVersion: 1,
|
|
48
|
+
id: randomUUID(),
|
|
49
|
+
at: new Date().toISOString(),
|
|
50
|
+
dagRunId: state.runId || options.dagRunId || "dag",
|
|
51
|
+
});
|
|
52
|
+
const observer = {
|
|
53
|
+
onRunStart: async (state) => {
|
|
54
|
+
const event = {
|
|
55
|
+
...buildBase("dag.run.started", state),
|
|
56
|
+
type: "dag.run.started",
|
|
57
|
+
status: state.status,
|
|
58
|
+
label: state.title,
|
|
59
|
+
};
|
|
60
|
+
await append(event);
|
|
61
|
+
},
|
|
62
|
+
onNodeStart: async (nodeId, state) => {
|
|
63
|
+
const { task, node } = findTask(options.spec, state, nodeId);
|
|
64
|
+
const event = {
|
|
65
|
+
...buildBase("dag.node.started", state),
|
|
66
|
+
type: "dag.node.started",
|
|
67
|
+
nodeId,
|
|
68
|
+
status: node?.status,
|
|
69
|
+
rank: rankOf(state, nodeId),
|
|
70
|
+
executor: node?.executor,
|
|
71
|
+
model: task
|
|
72
|
+
? resolveModelForTask(task, options.spec?.executorModels)
|
|
73
|
+
: undefined,
|
|
74
|
+
label: node?.id,
|
|
75
|
+
};
|
|
76
|
+
await append(event);
|
|
77
|
+
},
|
|
78
|
+
onNodeOutput: async (nodeId, chunk, state) => {
|
|
79
|
+
const { task, node } = findTask(options.spec, state, nodeId);
|
|
80
|
+
const event = {
|
|
81
|
+
...buildBase("dag.node.output", state),
|
|
82
|
+
type: "dag.node.output",
|
|
83
|
+
nodeId,
|
|
84
|
+
status: node?.status,
|
|
85
|
+
rank: rankOf(state, nodeId),
|
|
86
|
+
executor: node?.executor,
|
|
87
|
+
model: task
|
|
88
|
+
? resolveModelForTask(task, options.spec?.executorModels)
|
|
89
|
+
: undefined,
|
|
90
|
+
label: node?.id,
|
|
91
|
+
outputPreview: truncateEventPreview(redactSecrets(chunk), maxOutputPreviewBytes),
|
|
92
|
+
};
|
|
93
|
+
await append(event);
|
|
94
|
+
},
|
|
95
|
+
onNodeFinish: async (nodeId, state) => {
|
|
96
|
+
const { task, node } = findTask(options.spec, state, nodeId);
|
|
97
|
+
const event = {
|
|
98
|
+
...buildBase("dag.node.finished", state),
|
|
99
|
+
type: "dag.node.finished",
|
|
100
|
+
nodeId,
|
|
101
|
+
status: node?.status,
|
|
102
|
+
rank: rankOf(state, nodeId),
|
|
103
|
+
executor: node?.executor,
|
|
104
|
+
model: task
|
|
105
|
+
? resolveModelForTask(task, options.spec?.executorModels)
|
|
106
|
+
: undefined,
|
|
107
|
+
label: node?.id,
|
|
108
|
+
durationMs: node?.durationMs,
|
|
109
|
+
};
|
|
110
|
+
await append(event);
|
|
111
|
+
},
|
|
112
|
+
onRunFinish: async (state) => {
|
|
113
|
+
const event = {
|
|
114
|
+
...buildBase("dag.run.finished", state),
|
|
115
|
+
type: "dag.run.finished",
|
|
116
|
+
status: state.status,
|
|
117
|
+
label: state.title,
|
|
118
|
+
};
|
|
119
|
+
await append(event);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
observer,
|
|
124
|
+
flush: async () => {
|
|
125
|
+
// No buffering: append is immediate. Surface the last write error
|
|
126
|
+
// (best-effort) only if the caller asks, but never throw.
|
|
127
|
+
if (writeError) {
|
|
128
|
+
reportError("flush", writeError);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { assertValidDagSpec } from "./validate.js";
|
|
@@ -9,11 +9,40 @@ import { resolveAdapter } from "../../adapters/index.js";
|
|
|
9
9
|
import { loadHarnessManifest } from "../../governance/harness.js";
|
|
10
10
|
import { buildAuthoritySurfaceAuditNode, buildAuthoritySurfaceGateNode, resolveAuthoritySurfaceAudit, } from "./authority-surface.js";
|
|
11
11
|
import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
|
|
12
|
+
import { materializeTaskReferenceDocs } from "../../task/source-references.js";
|
|
12
13
|
import { resolveVerifyPreset } from "../../executors/shell-verification.js";
|
|
13
14
|
import { resolveExecutorModelMatrices } from "../../executors/model-routing.js";
|
|
14
15
|
const REQUIREMENT_FILE = "需求.md";
|
|
15
16
|
const CONSTRAINT_FILE = "执行约束.md";
|
|
17
|
+
const REFERENCE_DIRECTORY = "references";
|
|
16
18
|
const MAX_SOURCE_EXCERPT_CHARS = 2000;
|
|
19
|
+
const MAX_SOURCE_REFERENCE_DOCUMENTS = 8;
|
|
20
|
+
const INTERACTIVE_UI_DELIVERY_CONTRACT = [
|
|
21
|
+
"[INTERACTIVE_UI_DELIVERY_CONTRACT]",
|
|
22
|
+
"This is an interactive UI delivery task.",
|
|
23
|
+
"A helper-only, data-only, validation-only, or serializable-view implementation does not satisfy the task.",
|
|
24
|
+
"Before editing: identify the existing UI framework and component conventions, the real render/integration point, and the user-interaction test seam.",
|
|
25
|
+
"Required delivery: a framework-native visible component; integration into the declared route or parent; user interaction and visible error rendering; component-level tests using the repository's UI test stack.",
|
|
26
|
+
"The completion summary must list the component path, integration path, interaction test path, and DOM assertions mapped to acceptance criteria. If any item is missing, do not claim completion.",
|
|
27
|
+
"[/INTERACTIVE_UI_DELIVERY_CONTRACT]",
|
|
28
|
+
].join("\n");
|
|
29
|
+
function isInteractiveUiTask(taskConfig) {
|
|
30
|
+
return taskConfig.capabilities?.includes("interactive-ui") ?? false;
|
|
31
|
+
}
|
|
32
|
+
function writerDeliveryContract(taskConfig) {
|
|
33
|
+
return isInteractiveUiTask(taskConfig)
|
|
34
|
+
? INTERACTIVE_UI_DELIVERY_CONTRACT
|
|
35
|
+
: null;
|
|
36
|
+
}
|
|
37
|
+
function resolveWriterComplexity(taskConfig) {
|
|
38
|
+
if (isInteractiveUiTask(taskConfig))
|
|
39
|
+
return "HIGH";
|
|
40
|
+
if (taskConfig.complexity === "small")
|
|
41
|
+
return "MED";
|
|
42
|
+
if (taskConfig.complexity === "large")
|
|
43
|
+
return "HIGH";
|
|
44
|
+
return "MED";
|
|
45
|
+
}
|
|
17
46
|
const HYBRID_DEFAULTS = {
|
|
18
47
|
executor: "pi",
|
|
19
48
|
piBackend: "sdk-first",
|
|
@@ -101,27 +130,85 @@ function mapTaskComplexity(complexity) {
|
|
|
101
130
|
return "HIGH";
|
|
102
131
|
return "MED";
|
|
103
132
|
}
|
|
104
|
-
function excerptMarkdown(content,
|
|
133
|
+
export function excerptMarkdown(content, options = {}) {
|
|
134
|
+
const maxChars = options.maxChars ?? MAX_SOURCE_EXCERPT_CHARS;
|
|
105
135
|
const trimmed = content.trim();
|
|
106
|
-
if (trimmed.length <= maxChars)
|
|
107
|
-
return
|
|
108
|
-
|
|
136
|
+
if (trimmed.length <= maxChars) {
|
|
137
|
+
return {
|
|
138
|
+
text: trimmed,
|
|
139
|
+
truncated: false,
|
|
140
|
+
originalChars: trimmed.length,
|
|
141
|
+
maxChars,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const omitted = trimmed.length - maxChars;
|
|
145
|
+
const ref = options.sourceRef?.trim();
|
|
146
|
+
const pointer = ref
|
|
147
|
+
? [
|
|
148
|
+
`...[truncated for draft prompt: showing first ${maxChars} of ${trimmed.length} chars; omitted ${omitted} chars]`,
|
|
149
|
+
`Full source (authoritative, do not invent missing content): ${ref}`,
|
|
150
|
+
"When the omitted tail may affect acceptance/non-goals/constraints, re-read that file before deciding.",
|
|
151
|
+
].join("\n")
|
|
152
|
+
: `...[truncated for draft prompt: showing first ${maxChars} of ${trimmed.length} chars; omitted ${omitted} chars]`;
|
|
153
|
+
return {
|
|
154
|
+
text: `${trimmed.slice(0, maxChars)}\n\n${pointer}`,
|
|
155
|
+
truncated: true,
|
|
156
|
+
originalChars: trimmed.length,
|
|
157
|
+
maxChars,
|
|
158
|
+
};
|
|
109
159
|
}
|
|
110
|
-
function
|
|
160
|
+
function isHeadingLine(line) {
|
|
161
|
+
return /^#{1,6}\s+/.test(line.trim());
|
|
162
|
+
}
|
|
163
|
+
function isMetadataLine(line) {
|
|
164
|
+
const trimmed = line.trim();
|
|
165
|
+
if (!trimmed)
|
|
166
|
+
return true;
|
|
167
|
+
if (/^(TaskSpec business id|Feature id|Task type|Risk level)\s*:/i.test(trimmed)) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
if (/^(权威来源|SHA-256|冲突时以)/.test(trimmed))
|
|
171
|
+
return true;
|
|
172
|
+
if (/^>/.test(trimmed) && /(权威来源|SHA-256|原始 PRD|reference)/i.test(trimmed)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
export function extractObjective(requirementMarkdown, title) {
|
|
178
|
+
const sectionMatchers = [
|
|
179
|
+
/##\s*(目标|Objective|Goals?)\s*\n([\s\S]*?)(?=\n##\s|\n#\s|$)/i,
|
|
180
|
+
/##\s*Description\s*\n([\s\S]*?)(?=\n##\s|\n#\s|$)/i,
|
|
181
|
+
];
|
|
182
|
+
for (const matcher of sectionMatchers) {
|
|
183
|
+
const match = requirementMarkdown.match(matcher);
|
|
184
|
+
if (!match)
|
|
185
|
+
continue;
|
|
186
|
+
const body = (match[2] ?? match[1] ?? "").trim();
|
|
187
|
+
for (const line of body.split("\n")) {
|
|
188
|
+
const trimmed = line
|
|
189
|
+
.replace(/^[-*]\s*\[[ xX]\]\s*/, "")
|
|
190
|
+
.replace(/^[-*]\s*/, "")
|
|
191
|
+
.trim();
|
|
192
|
+
if (!trimmed || isHeadingLine(trimmed) || isMetadataLine(trimmed))
|
|
193
|
+
continue;
|
|
194
|
+
return trimmed.slice(0, 500);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
111
197
|
const lines = requirementMarkdown.split("\n");
|
|
112
198
|
for (const line of lines) {
|
|
113
199
|
const trimmed = line.trim();
|
|
114
|
-
if (!trimmed || trimmed
|
|
200
|
+
if (!trimmed || isHeadingLine(trimmed) || isMetadataLine(trimmed))
|
|
115
201
|
continue;
|
|
116
202
|
return trimmed.slice(0, 500);
|
|
117
203
|
}
|
|
118
204
|
return `Implement task: ${title}`;
|
|
119
205
|
}
|
|
120
|
-
|
|
206
|
+
const SUCCESS_CRITERIA_SECTION = /##\s*(验收标准|完成标准|Acceptance(?:\s+References)?|Success Criteria)\s*\n([\s\S]*?)(?=\n##\s|\n#\s|$)/i;
|
|
207
|
+
export function extractSuccessCriteria(requirementMarkdown, taskId) {
|
|
121
208
|
const criteria = [];
|
|
122
|
-
const sectionMatch = requirementMarkdown.match(
|
|
209
|
+
const sectionMatch = requirementMarkdown.match(SUCCESS_CRITERIA_SECTION);
|
|
123
210
|
if (sectionMatch) {
|
|
124
|
-
for (const line of sectionMatch[
|
|
211
|
+
for (const line of sectionMatch[2].split("\n")) {
|
|
125
212
|
const trimmed = line.replace(/^[-*]\s*\[[ xX]\]\s*/, "").trim();
|
|
126
213
|
if (trimmed.startsWith("-") || trimmed.startsWith("*")) {
|
|
127
214
|
criteria.push(trimmed.replace(/^[-*]\s*/, "").trim());
|
|
@@ -226,20 +313,77 @@ function deriveParallelScoutPaths(taskConfig) {
|
|
|
226
313
|
: allowed,
|
|
227
314
|
};
|
|
228
315
|
}
|
|
316
|
+
function toTaskRelativeSourcePath(sources, absolutePath) {
|
|
317
|
+
return path
|
|
318
|
+
.relative(sources.taskDir, absolutePath)
|
|
319
|
+
.replaceAll(path.sep, "/");
|
|
320
|
+
}
|
|
229
321
|
function buildSourceContextBlock(sources) {
|
|
322
|
+
const requirementRef = toTaskRelativeSourcePath(sources, sources.requirementPath);
|
|
323
|
+
const requirementExcerpt = excerptMarkdown(sources.requirementMarkdown, {
|
|
324
|
+
sourceRef: requirementRef,
|
|
325
|
+
});
|
|
230
326
|
const parts = [
|
|
231
327
|
"## Task source: 需求.md",
|
|
232
|
-
|
|
328
|
+
requirementExcerpt.text,
|
|
233
329
|
];
|
|
234
330
|
if (sources.constraintMarkdown) {
|
|
235
|
-
|
|
331
|
+
const constraintRef = toTaskRelativeSourcePath(sources, sources.constraintPath);
|
|
332
|
+
const constraintExcerpt = excerptMarkdown(sources.constraintMarkdown, {
|
|
333
|
+
sourceRef: constraintRef,
|
|
334
|
+
});
|
|
335
|
+
parts.push("## Task source: 执行约束.md", constraintExcerpt.text);
|
|
336
|
+
}
|
|
337
|
+
for (const reference of sources.referenceDocuments ?? []) {
|
|
338
|
+
const relativePath = path
|
|
339
|
+
.relative(path.join(sources.taskDir, "source"), reference.path)
|
|
340
|
+
.replaceAll(path.sep, "/");
|
|
341
|
+
const referenceRef = toTaskRelativeSourcePath(sources, reference.path);
|
|
342
|
+
const referenceExcerpt = excerptMarkdown(reference.markdown, {
|
|
343
|
+
sourceRef: referenceRef,
|
|
344
|
+
});
|
|
345
|
+
parts.push(`## Task source reference: ${relativePath}`, referenceExcerpt.text);
|
|
236
346
|
}
|
|
237
|
-
parts.push("## Task config summary", `- taskId: ${sources.taskConfig.taskId}`, `- flow: ${sources.taskConfig.flow}`, `- complexity: ${sources.taskConfig.complexity}`, `- contextProfile: ${sources.taskConfig.contextProfile}`, `- allowedPaths: ${sources.taskConfig.allowedPaths.join(", ") || "(none — review before execute)"}`, `- forbiddenPaths: ${sources.taskConfig.forbiddenPaths.join(", ") || "(none)"}`, '- Pi DAG nodes are read-only unless toolProfile="write" is explicitly selected for a bounded writer node.', "- Agent DAG read-only nodes must not write root artifacts/**; root artifacts/ is not a per-node scratchpad.");
|
|
347
|
+
parts.push("## Task config summary", `- taskId: ${sources.taskConfig.taskId}`, `- flow: ${sources.taskConfig.flow}`, `- complexity: ${sources.taskConfig.complexity}`, `- contextProfile: ${sources.taskConfig.contextProfile}`, `- allowedPaths: ${sources.taskConfig.allowedPaths.join(", ") || "(none — review before execute)"}`, `- forbiddenPaths: ${sources.taskConfig.forbiddenPaths.join(", ") || "(none)"}`, '- Pi DAG nodes are read-only unless toolProfile="write" is explicitly selected for a bounded writer node.', "- Agent DAG read-only nodes must not write root artifacts/**; root artifacts/ is not a per-node scratchpad.", "- source/references/* are immutable user/source facts; source/需求.md is the derived execution contract.");
|
|
238
348
|
if (sources.taskConfig.hardConstraints.length > 0) {
|
|
239
349
|
parts.push("- hardConstraints:", ...sources.taskConfig.hardConstraints.map((c) => ` - ${c}`));
|
|
240
350
|
}
|
|
241
351
|
return parts.join("\n\n");
|
|
242
352
|
}
|
|
353
|
+
async function loadMaterializedSourceReferences(sourceDir) {
|
|
354
|
+
const referenceDir = path.join(sourceDir, REFERENCE_DIRECTORY);
|
|
355
|
+
const referencePaths = [];
|
|
356
|
+
async function collect(dir) {
|
|
357
|
+
let entries;
|
|
358
|
+
try {
|
|
359
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
if (error.code === "ENOENT")
|
|
363
|
+
return;
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
for (const entry of entries) {
|
|
367
|
+
const entryPath = path.join(dir, entry.name);
|
|
368
|
+
if (entry.isDirectory()) {
|
|
369
|
+
await collect(entryPath);
|
|
370
|
+
}
|
|
371
|
+
else if (entry.isFile()) {
|
|
372
|
+
// Skip index/manifest sidecars; keep only user/source reference content.
|
|
373
|
+
if (entry.name === "index.json" || entry.name === "source-manifest.json") {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
referencePaths.push(entryPath);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
await collect(referenceDir);
|
|
381
|
+
referencePaths.sort((left, right) => left.localeCompare(right));
|
|
382
|
+
return Promise.all(referencePaths.slice(0, MAX_SOURCE_REFERENCE_DOCUMENTS).map(async (filePath) => ({
|
|
383
|
+
path: filePath,
|
|
384
|
+
markdown: await readFile(filePath, "utf-8"),
|
|
385
|
+
})));
|
|
386
|
+
}
|
|
243
387
|
export async function loadTaskHybridSources(repoRoot, taskId) {
|
|
244
388
|
const paths = getTaskPaths(repoRoot, taskId);
|
|
245
389
|
const requirementPath = path.join(paths.sourceDir, REQUIREMENT_FILE);
|
|
@@ -265,6 +409,12 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
|
|
|
265
409
|
// optional
|
|
266
410
|
}
|
|
267
411
|
const taskConfig = await loadTaskConfig(repoRoot, taskId);
|
|
412
|
+
await materializeTaskReferenceDocs({
|
|
413
|
+
repoRoot,
|
|
414
|
+
taskId,
|
|
415
|
+
taskConfig,
|
|
416
|
+
});
|
|
417
|
+
const referenceDocuments = await loadMaterializedSourceReferences(paths.sourceDir);
|
|
268
418
|
const manifest = await loadHarnessManifest(repoRoot);
|
|
269
419
|
const strategy = resolveDagVerifyStrategy(taskConfig);
|
|
270
420
|
let verifyCommands;
|
|
@@ -300,6 +450,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
|
|
|
300
450
|
constraintPath,
|
|
301
451
|
requirementMarkdown,
|
|
302
452
|
constraintMarkdown,
|
|
453
|
+
referenceDocuments,
|
|
303
454
|
taskConfig,
|
|
304
455
|
enabledExecutors: resolveEnabledExecutors(manifest.executors),
|
|
305
456
|
executorModelMatrix: resolveExecutorModelMatrices(manifest),
|
|
@@ -312,11 +463,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
312
463
|
const implementPaths = resolveImplementPaths(taskConfig);
|
|
313
464
|
const scoutPaths = deriveParallelScoutPaths(taskConfig);
|
|
314
465
|
const scoutComplexity = mapTaskComplexity(taskConfig.complexity);
|
|
315
|
-
const implementComplexity = taskConfig
|
|
316
|
-
? "MED"
|
|
317
|
-
: taskConfig.complexity === "large"
|
|
318
|
-
? "HIGH"
|
|
319
|
-
: "MED";
|
|
466
|
+
const implementComplexity = resolveWriterComplexity(taskConfig);
|
|
320
467
|
const writerExecutor = resolveImplementationExecutor(sources);
|
|
321
468
|
const implementId = implementationNodeId(writerExecutor);
|
|
322
469
|
const sourceContext = buildSourceContextBlock(sources);
|
|
@@ -418,8 +565,9 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
418
565
|
subtask_prompt: [
|
|
419
566
|
"Implement the approved plan with minimal focused changes.",
|
|
420
567
|
"Stay within writeSet. Do not write root artifacts/** unless artifacts paths are explicitly declared in writeSet.",
|
|
568
|
+
writerDeliveryContract(taskConfig),
|
|
421
569
|
sourceContext,
|
|
422
|
-
].join("\n\n"),
|
|
570
|
+
].filter((value) => Boolean(value)).join("\n\n"),
|
|
423
571
|
},
|
|
424
572
|
{
|
|
425
573
|
id: "verify-pi",
|
|
@@ -509,6 +657,14 @@ function buildReviewNode(sources) {
|
|
|
509
657
|
"Review upstream implementation and verification evidence.",
|
|
510
658
|
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
511
659
|
"List Critical/Important findings when present; any Critical/Important finding must force request-revision. Read-only: do not modify files.",
|
|
660
|
+
[
|
|
661
|
+
"Three-way source fidelity check (required):",
|
|
662
|
+
"1) immutable originals under source/references/ (especially requirement* and acceptance*);",
|
|
663
|
+
"2) derived source/需求.md execution contract (and source/执行约束.md);",
|
|
664
|
+
"3) actual implementation/diff + verification evidence.",
|
|
665
|
+
"If 需求.md conflicts with source/references/*, prefer references and request-revision when the implementation only satisfies the derived summary.",
|
|
666
|
+
"If prompt excerpts are truncated, re-read the Full source paths before verdict.",
|
|
667
|
+
].join(" "),
|
|
512
668
|
buildSourceContextBlock(sources),
|
|
513
669
|
].join("\n\n"),
|
|
514
670
|
};
|
|
@@ -695,7 +851,11 @@ function buildRepairNode(sources) {
|
|
|
695
851
|
role: "implementer",
|
|
696
852
|
executor: writerExecutor,
|
|
697
853
|
toolProfile: "write",
|
|
698
|
-
complexity: sources.taskConfig
|
|
854
|
+
complexity: isInteractiveUiTask(sources.taskConfig)
|
|
855
|
+
? "HIGH"
|
|
856
|
+
: sources.taskConfig.complexity === "small"
|
|
857
|
+
? "MED"
|
|
858
|
+
: "HIGH",
|
|
699
859
|
writePolicy: "exclusive",
|
|
700
860
|
writeSet: implement.writeSet,
|
|
701
861
|
allowedPaths: implement.allowedPaths,
|
|
@@ -705,8 +865,9 @@ function buildRepairNode(sources) {
|
|
|
705
865
|
"If process-supervisor-pi returned VERDICT: request-revision, apply bounded fixes within writeSet addressing its REPAIR_ARTIFACT_JSON.fixScope and preserving REPAIR_ARTIFACT_JSON.invariant.",
|
|
706
866
|
"Use REPAIR_ARTIFACT_JSON.failureClass and rootCause to choose the smallest repair strategy. Raw log is fallback evidence only when rawLogFallbackAllowed is true.",
|
|
707
867
|
"If VERDICT: pass, return no-op with evidence. Re-run focused tests when you change code.",
|
|
868
|
+
writerDeliveryContract(sources.taskConfig),
|
|
708
869
|
buildSourceContextBlock(sources),
|
|
709
|
-
].join("\n\n"),
|
|
870
|
+
].filter((value) => Boolean(value)).join("\n\n"),
|
|
710
871
|
};
|
|
711
872
|
}
|
|
712
873
|
function buildHardVerifyNode(sources) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
function reportError(context, observerIndex, error) {
|
|
2
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3
|
+
process.stderr.write(`[dag-observer-compose] observer#${observerIndex} ${context} failed: ${message}\n`);
|
|
4
|
+
}
|
|
5
|
+
const HOOK_NAMES = [
|
|
6
|
+
"onRunStart",
|
|
7
|
+
"onNodeStart",
|
|
8
|
+
"onNodeOutput",
|
|
9
|
+
"onNodeFinish",
|
|
10
|
+
"onRunFinish",
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Compose multiple `DagRunObserver`s into one. Each hook is invoked in order.
|
|
14
|
+
* A throw in a single observer's hook is reported to stderr and does not stop
|
|
15
|
+
* subsequent observers — observers are derived views and must not affect
|
|
16
|
+
* canonical DAG execution. Returns `undefined` when no observers are provided.
|
|
17
|
+
*/
|
|
18
|
+
export function composeDagRunObservers(observers) {
|
|
19
|
+
const active = observers.filter((observer) => Boolean(observer));
|
|
20
|
+
if (active.length === 0)
|
|
21
|
+
return undefined;
|
|
22
|
+
const observer = {
|
|
23
|
+
onRunStart: async (state) => {
|
|
24
|
+
await runHook("onRunStart", active, (obs) => obs.onRunStart?.(state));
|
|
25
|
+
},
|
|
26
|
+
onNodeStart: async (nodeId, state) => {
|
|
27
|
+
await runHook("onNodeStart", active, (obs) => obs.onNodeStart?.(nodeId, state));
|
|
28
|
+
},
|
|
29
|
+
onNodeOutput: async (nodeId, chunk, state) => {
|
|
30
|
+
await runHook("onNodeOutput", active, (obs) => obs.onNodeOutput?.(nodeId, chunk, state));
|
|
31
|
+
},
|
|
32
|
+
onNodeFinish: async (nodeId, state) => {
|
|
33
|
+
await runHook("onNodeFinish", active, (obs) => obs.onNodeFinish?.(nodeId, state));
|
|
34
|
+
},
|
|
35
|
+
onRunFinish: async (state) => {
|
|
36
|
+
await runHook("onRunFinish", active, (obs) => obs.onRunFinish?.(state));
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
return observer;
|
|
40
|
+
}
|
|
41
|
+
async function runHook(hookName, active, invoke) {
|
|
42
|
+
for (let i = 0; i < active.length; i += 1) {
|
|
43
|
+
try {
|
|
44
|
+
await invoke(active[i]);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
reportError(hookName, i, error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Re-exported for tests/inspection of hook coverage.
|
|
52
|
+
export const COMPOSED_HOOK_NAMES = HOOK_NAMES;
|
package/docs/README.md
CHANGED
|
@@ -1,72 +1,75 @@
|
|
|
1
|
-
# 文档索引
|
|
2
|
-
|
|
3
|
-
`docs/` 是 loop-agent 的治理根目录,包含工作流规则、方法论、验证规则、执行计划、报告、进度日志、决策记录和可复用模板。
|
|
4
|
-
|
|
5
|
-
顶层 `AGENTS.md` 是操作地图。长期知识应落在此处:决策、契约、计划、验证证据、调试笔记和可复用流程规则应记录在 `docs/` 下,而不是只留在聊天里。
|
|
6
|
-
|
|
7
|
-
## 核心文档
|
|
8
|
-
|
|
9
|
-
- `development-principles.md` — 仓库开发原则
|
|
10
|
-
- `architecture/runtime-boundaries.md` — runtime 层边界与依赖方向
|
|
11
|
-
- `feature-workflow.md` — 有边界的功能工作流
|
|
12
|
-
- `verification-matrix.md` — 验证命令选择
|
|
13
|
-
- `production-readiness.md` — Production Readiness v0.1 范围、证据与 DAG hardening 标准
|
|
14
|
-
- `loop-agent-harness.md` — runtime 与 command surface 概览
|
|
15
|
-
- `agent-dag-runner.md` — Agent DAG runner 指南
|
|
16
|
-
- `cursor-executor-usage.md` — Cursor executor 用法
|
|
17
|
-
- `dynamic-workflow-dag-engine-roadmap.md` — Dynamic Workflow DAG Engine 路线图与适配分析
|
|
18
|
-
- `init-surface.manifest.json` — npm 包范围、目标项目初始化投影与 `init check-update` surface 分类的机器校验契约
|
|
19
|
-
|
|
20
|
-
## 设计思想来源
|
|
21
|
-
|
|
22
|
-
- `../website/docs/practices/` — Anthropic 长时运行 agent harness、OpenAI Codex harness engineering、腾讯端到端 Harness Engineering 与社区 agent harness 实践资料。当前仓库的“人类掌舵、智能体执行”、仓库即记录系统、小步增量、结构化 handoff 和 shell verification 纪律均受这些实践启发;权威执行规则仍以本目录治理文档、根目录 AGENTS.md、harness.json、skills 目录和脚本检查为准。
|
|
23
|
-
|
|
24
|
-
## 方法论
|
|
25
|
-
|
|
26
|
-
- `harness-methodology-tdd.md` — 行为变更与 bug 修复的 TDD 纪律
|
|
27
|
-
- `harness-methodology-verification.md` — 完成声明前的验证纪律
|
|
28
|
-
- `harness-methodology-debugging.md` — 修复前的系统化调试工作流
|
|
29
|
-
|
|
30
|
-
## 产物目录
|
|
31
|
-
|
|
32
|
-
- `design/README.md` — 设计草稿与实现契约
|
|
33
|
-
- `exec-plans/active/README.md` — 进行中的执行计划
|
|
34
|
-
- `exec-plans/completed/README.md` — 已完成的执行计划
|
|
35
|
-
- `progress/README.md` — 进度交接日志
|
|
36
|
-
- `reports/README.md` — 验证与审计报告
|
|
37
|
-
- `decisions/README.md` — 架构决策
|
|
38
|
-
- `skills/README.md` — repo-local skill registry and vetting notes
|
|
39
|
-
- `templates/` — 可复用的规划、报告与 DAG 模板
|
|
40
|
-
|
|
41
|
-
## 仓库 Skills
|
|
42
|
-
|
|
43
|
-
- `../skills/loop-agent/` — loop-agent 自身的 skill 指令与参考资料
|
|
44
|
-
- 每个额外 skill 在仓库根 `../skills/` 下使用独立子目录;这些本地副本由 DAG 模板引用,维护不依赖外部 agent skill 目录
|
|
45
|
-
|
|
46
|
-
## 模板
|
|
47
|
-
|
|
48
|
-
- `templates/project-start-checklist.md` — 开工前检查清单
|
|
49
|
-
- `templates/feature-spec.md` — 有边界的功能规格
|
|
50
|
-
- `templates/sprint-contract.md` — 实现契约与验收标准
|
|
51
|
-
- `templates/exec-plan.md` — 非平凡工作的执行计划
|
|
52
|
-
- `templates/progress-log.md` — 进度与交接日志
|
|
53
|
-
- `templates/qa-report.md` — 验证与 QA 证据
|
|
54
|
-
- `templates/
|
|
55
|
-
- `templates/
|
|
56
|
-
- `templates/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
1
|
+
# 文档索引
|
|
2
|
+
|
|
3
|
+
`docs/` 是 loop-agent 的治理根目录,包含工作流规则、方法论、验证规则、执行计划、报告、进度日志、决策记录和可复用模板。
|
|
4
|
+
|
|
5
|
+
顶层 `AGENTS.md` 是操作地图。长期知识应落在此处:决策、契约、计划、验证证据、调试笔记和可复用流程规则应记录在 `docs/` 下,而不是只留在聊天里。
|
|
6
|
+
|
|
7
|
+
## 核心文档
|
|
8
|
+
|
|
9
|
+
- `development-principles.md` — 仓库开发原则
|
|
10
|
+
- `architecture/runtime-boundaries.md` — runtime 层边界与依赖方向
|
|
11
|
+
- `feature-workflow.md` — 有边界的功能工作流
|
|
12
|
+
- `verification-matrix.md` — 验证命令选择
|
|
13
|
+
- `production-readiness.md` — Production Readiness v0.1 范围、证据与 DAG hardening 标准
|
|
14
|
+
- `loop-agent-harness.md` — runtime 与 command surface 概览
|
|
15
|
+
- `agent-dag-runner.md` — Agent DAG runner 指南
|
|
16
|
+
- `cursor-executor-usage.md` — Cursor executor 用法
|
|
17
|
+
- `dynamic-workflow-dag-engine-roadmap.md` — Dynamic Workflow DAG Engine 路线图与适配分析
|
|
18
|
+
- `init-surface.manifest.json` — npm 包范围、目标项目初始化投影与 `init check-update` surface 分类的机器校验契约
|
|
19
|
+
|
|
20
|
+
## 设计思想来源
|
|
21
|
+
|
|
22
|
+
- `../website/docs/practices/` — Anthropic 长时运行 agent harness、OpenAI Codex harness engineering、腾讯端到端 Harness Engineering 与社区 agent harness 实践资料。当前仓库的“人类掌舵、智能体执行”、仓库即记录系统、小步增量、结构化 handoff 和 shell verification 纪律均受这些实践启发;权威执行规则仍以本目录治理文档、根目录 AGENTS.md、harness.json、skills 目录和脚本检查为准。
|
|
23
|
+
|
|
24
|
+
## 方法论
|
|
25
|
+
|
|
26
|
+
- `harness-methodology-tdd.md` — 行为变更与 bug 修复的 TDD 纪律
|
|
27
|
+
- `harness-methodology-verification.md` — 完成声明前的验证纪律
|
|
28
|
+
- `harness-methodology-debugging.md` — 修复前的系统化调试工作流
|
|
29
|
+
|
|
30
|
+
## 产物目录
|
|
31
|
+
|
|
32
|
+
- `design/README.md` — 设计草稿与实现契约
|
|
33
|
+
- `exec-plans/active/README.md` — 进行中的执行计划
|
|
34
|
+
- `exec-plans/completed/README.md` — 已完成的执行计划
|
|
35
|
+
- `progress/README.md` — 进度交接日志
|
|
36
|
+
- `reports/README.md` — 验证与审计报告
|
|
37
|
+
- `decisions/README.md` — 架构决策
|
|
38
|
+
- `skills/README.md` — repo-local skill registry and vetting notes
|
|
39
|
+
- `templates/` — 可复用的规划、报告与 DAG 模板
|
|
40
|
+
|
|
41
|
+
## 仓库 Skills
|
|
42
|
+
|
|
43
|
+
- `../skills/loop-agent/` — loop-agent 自身的 skill 指令与参考资料
|
|
44
|
+
- 每个额外 skill 在仓库根 `../skills/` 下使用独立子目录;这些本地副本由 DAG 模板引用,维护不依赖外部 agent skill 目录
|
|
45
|
+
|
|
46
|
+
## 模板
|
|
47
|
+
|
|
48
|
+
- `templates/project-start-checklist.md` — 开工前检查清单
|
|
49
|
+
- `templates/feature-spec.md` — 有边界的功能规格
|
|
50
|
+
- `templates/sprint-contract.md` — 实现契约与验收标准
|
|
51
|
+
- `templates/exec-plan.md` — 非平凡工作的执行计划
|
|
52
|
+
- `templates/progress-log.md` — 进度与交接日志
|
|
53
|
+
- `templates/qa-report.md` — 验证与 QA 证据
|
|
54
|
+
- `templates/worker-dogfood-setup.md` — 发布控制器下的真实 Worker sample setup 与 retry 纪律
|
|
55
|
+
- `templates/worker-dogfood-evidence.md` — BE/FE/QA sample、Observe、morning report 与 coverage evidence 模板
|
|
56
|
+
- `templates/interactive-ui-round2-experiment.md` — interactive UI prompt/model A/B/C 对照实验与统一指标模板
|
|
57
|
+
- `templates/production-readiness-checklist.md` — 低/中风险单仓库 DAG readiness 检查清单
|
|
58
|
+
- `templates/init-evolution-review.md` — 初始化能力演化审查报告模板
|
|
59
|
+
- `templates/adr.md` — 架构决策记录(ADR)
|
|
60
|
+
|
|
61
|
+
## 维护
|
|
62
|
+
|
|
63
|
+
文档变更后运行:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bash scripts/check-repo.sh
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Windows 上通过 Git Bash 或已配置的兼容 Bash 运行脚本。实际文件操作使用平台原生路径;`/` 仅用于 repo 引用、JSON/Markdown 证据引用和 glob 约定。
|
|
70
|
+
|
|
71
|
+
完整本地门禁:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bash scripts/ci.sh
|
|
75
|
+
```
|