@tea-agent/loop-agent 0.33.6 → 0.33.7-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -23
- package/dist/application/task-lifecycle/advance.js +4 -254
- package/dist/application/task-lifecycle/gates.js +0 -50
- package/dist/application/task-lifecycle/observe.js +2 -11
- package/dist/commands/init-upgrade.js +1 -32
- package/dist/commands/init.js +3 -94
- package/dist/executors/shell-executor.js +91 -4
- package/dist/executors/shell-write-guard.js +8 -26
- package/dist/shared/operator/capabilities.js +42 -72
- package/dist/task/source-prepare/index.js +0 -2
- package/dist/task/source-prepare/parse-intent.js +10 -58
- package/dist/task/source-prepare/prepare.js +16 -180
- package/dist/task/source-prepare/reference-integrity.js +2 -18
- package/dist/worker/console/app-data.js +0 -2
- package/dist/worker/console/chat/chat-event-store.js +25 -190
- package/dist/worker/console/chat/instruction-skills.js +217 -0
- package/dist/worker/console/chat/pi-console-config.js +32 -250
- package/dist/worker/console/chat/pi-runtime.js +71 -625
- package/dist/worker/console/chat/resource-loader.js +4 -5
- package/dist/worker/console/chat/routes.js +146 -324
- package/dist/worker/console/chat/runtime-context.js +12 -48
- package/dist/worker/console/chat/runtime-selection.js +0 -59
- package/dist/worker/console/chat/shortcuts.js +0 -1
- package/dist/worker/console/chat/tool-adapter.js +3 -9
- package/dist/worker/console/chat/tools.js +1 -5
- package/dist/worker/console/operator-actions.js +68 -559
- package/dist/worker/console/server.js +15 -8
- package/dist/worker/console/static/assets/index-CnUXAqxG.css +1 -0
- package/dist/worker/console/static/assets/index-CteJFFL2.js +29 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +8 -45
- package/dist/worker/console/static-src/operator-chat/refs.js +0 -9
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +0 -16
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +184 -210
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +5 -49
- package/dist/worker/console/static-src/operator-chat/useComposer.js +0 -17
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +74 -225
- package/dist/worker/delivery/final-verification.js +5 -13
- package/dist/worker/delivery/package.js +19 -31
- package/dist/worker/delivery/verification-bundle.js +4 -6
- package/dist/worker/observe/static/operator-chrome.css +2 -5
- package/dist/worker/observe/static/operator-chrome.js +1 -6
- package/dist/worker/observe/static/styles.css +9 -39
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +462 -33
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +25 -1
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
- package/dist/workflows/dag/backend-test-scenario-param.js +269 -82
- package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
- package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
- package/dist/workflows/dag/frontend-worktree-diff.js +27 -12
- package/dist/workflows/dag/init-hybrid.js +46 -34
- package/dist/workflows/dag/types.js +7 -0
- package/dist/workflows/dag/workspace-checkpoint.js +27 -8
- package/docs/templates/backend-test-dag.json +32 -29
- package/harness.json +1 -1
- package/package.json +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -3
- package/skills/loop-agent/references/source-and-plan-practice.md +0 -13
- package/skills/loop-agent/references/task-workflow.md +0 -4
- package/dist/shared/resilient-git.js +0 -133
- package/dist/task/source-prepare/artifact-meta.js +0 -137
- package/dist/task/source-prepare/semantic-intake.js +0 -404
- package/dist/worker/console/dag-execution-receipt.js +0 -380
- package/dist/worker/console/static/assets/index-BUOLppPr.js +0 -28
- package/dist/worker/console/static/assets/index-C1KzazY5.css +0 -1
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +0 -257
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +0 -196
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
const HEADING_ALIASES = {
|
|
2
|
-
objective: new Set([
|
|
3
|
-
|
|
4
|
-
"objective",
|
|
5
|
-
"问题与目标",
|
|
6
|
-
"业务目标",
|
|
7
|
-
"需求概述",
|
|
8
|
-
]),
|
|
9
|
-
scope: new Set([
|
|
10
|
-
"范围",
|
|
11
|
-
"scope",
|
|
12
|
-
"in scope",
|
|
13
|
-
"需求范围",
|
|
14
|
-
"已确认范围",
|
|
15
|
-
"已确认需求",
|
|
16
|
-
]),
|
|
2
|
+
objective: new Set(["目标", "objective", "问题与目标"]),
|
|
3
|
+
scope: new Set(["范围", "scope", "in scope"]),
|
|
17
4
|
nonGoals: new Set(["非目标", "out of scope", "non-goals", "non goals"]),
|
|
18
5
|
acceptance: new Set(["验收标准", "acceptance criteria", "acceptance"]),
|
|
19
6
|
constraints: new Set(["约束", "constraints", "不变式", "invariants"]),
|
|
@@ -21,8 +8,6 @@ const HEADING_ALIASES = {
|
|
|
21
8
|
assumptions: new Set(["假设", "assumptions"]),
|
|
22
9
|
};
|
|
23
10
|
const OBJECTIVE_MAX_CHARS = 1200;
|
|
24
|
-
/** Product Requirement V3/V4 style embedded AC heading, e.g. AC-FE-001 / AC-BE-12 / AC-001. */
|
|
25
|
-
const EMBEDDED_AC_HEADING = /^(AC(?:[-_](?:FE|BE|UI|API))?[-_]?\d+(?:[-_]\d+)?)\s*[::]?\s*(.*)$/iu;
|
|
26
11
|
function stripFencedAndComments(text) {
|
|
27
12
|
// Remove fenced code blocks and HTML comments so headings inside them are ignored.
|
|
28
13
|
return text
|
|
@@ -33,28 +18,10 @@ function stripFencedAndComments(text) {
|
|
|
33
18
|
function normalizeHeadingText(raw) {
|
|
34
19
|
return raw
|
|
35
20
|
.trim()
|
|
36
|
-
// Strip leading section numbers: "2. 业务目标" / "3.1 已确认范围" / "3.2、非目标"
|
|
37
|
-
.replace(/^(?:\d+(?:\.\d+)*[..、]?\s*)+/u, "")
|
|
38
21
|
.toLowerCase()
|
|
39
22
|
.replace(/[::]\s*$/u, "")
|
|
40
23
|
.replace(/\s+/g, " ");
|
|
41
24
|
}
|
|
42
|
-
function normalizeAcceptanceId(raw) {
|
|
43
|
-
return raw
|
|
44
|
-
.trim()
|
|
45
|
-
.toUpperCase()
|
|
46
|
-
.replace(/_/g, "-")
|
|
47
|
-
.replace(/^(AC)(FE|BE|UI|API)(?=-|\d)/u, "$1-$2");
|
|
48
|
-
}
|
|
49
|
-
function parseEmbeddedAcHeading(headingText) {
|
|
50
|
-
const normalized = headingText.trim().replace(/^(?:\d+(?:\.\d+)*[..、]?\s*)+/u, "");
|
|
51
|
-
const match = EMBEDDED_AC_HEADING.exec(normalized);
|
|
52
|
-
if (!match)
|
|
53
|
-
return null;
|
|
54
|
-
const id = normalizeAcceptanceId(match[1]);
|
|
55
|
-
const text = match[2]?.trim() || id;
|
|
56
|
-
return { id, text };
|
|
57
|
-
}
|
|
58
25
|
function classifyHeading(text) {
|
|
59
26
|
const normalized = normalizeHeadingText(text);
|
|
60
27
|
for (const [key, aliases] of Object.entries(HEADING_ALIASES)) {
|
|
@@ -81,17 +48,14 @@ function parseAcceptanceLine(line, nextId) {
|
|
|
81
48
|
const body = (checklist?.[1] ?? bullet?.[1] ?? line.trim()).trim();
|
|
82
49
|
if (!body)
|
|
83
50
|
return null;
|
|
84
|
-
const embedded = parseEmbeddedAcHeading(body);
|
|
85
|
-
if (embedded)
|
|
86
|
-
return embedded;
|
|
87
51
|
const withId = /^(AC[-_]?\d+)\s*[::]\s*(.+)$/iu.exec(body);
|
|
88
52
|
if (withId) {
|
|
89
|
-
return { id:
|
|
53
|
+
return { id: withId[1].toUpperCase().replace("_", "-"), text: withId[2].trim() };
|
|
90
54
|
}
|
|
91
55
|
const bareAc = /^(AC[-_]?\d+)\s+(.+)$/iu.exec(body);
|
|
92
56
|
if (bareAc) {
|
|
93
57
|
return {
|
|
94
|
-
id:
|
|
58
|
+
id: bareAc[1].toUpperCase().replace("_", "-"),
|
|
95
59
|
text: bareAc[2].trim(),
|
|
96
60
|
};
|
|
97
61
|
}
|
|
@@ -150,27 +114,9 @@ export function extractRequirementFactsFromMarkdown(markdown, options) {
|
|
|
150
114
|
const sections = [];
|
|
151
115
|
let current = { key: "other", lines: [] };
|
|
152
116
|
sections.push(current);
|
|
153
|
-
let acCounter = options?.acCounterStart ?? 1;
|
|
154
|
-
const nextId = () => {
|
|
155
|
-
const id = `AC-${String(acCounter).padStart(3, "0")}`;
|
|
156
|
-
acCounter += 1;
|
|
157
|
-
return id;
|
|
158
|
-
};
|
|
159
117
|
for (const line of lines) {
|
|
160
118
|
const heading = isAtxHeading(line);
|
|
161
119
|
if (heading) {
|
|
162
|
-
// Product Requirement V3/V4 embeds formal AC as headings (#### AC-FE-001 ...).
|
|
163
|
-
// Capture the heading as a single AC; GWT body lists under it stay non-AC.
|
|
164
|
-
const embeddedAc = parseEmbeddedAcHeading(heading.text);
|
|
165
|
-
if (embeddedAc) {
|
|
166
|
-
sections.push({
|
|
167
|
-
key: "acceptance",
|
|
168
|
-
lines: [`- ${embeddedAc.id}: ${embeddedAc.text}`],
|
|
169
|
-
});
|
|
170
|
-
current = { key: "other", lines: [] };
|
|
171
|
-
sections.push(current);
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
120
|
const key = classifyHeading(heading.text);
|
|
175
121
|
current = { key, lines: [] };
|
|
176
122
|
sections.push(current);
|
|
@@ -178,6 +124,12 @@ export function extractRequirementFactsFromMarkdown(markdown, options) {
|
|
|
178
124
|
}
|
|
179
125
|
current.lines.push(line);
|
|
180
126
|
}
|
|
127
|
+
let acCounter = options?.acCounterStart ?? 1;
|
|
128
|
+
const nextId = () => {
|
|
129
|
+
const id = `AC-${String(acCounter).padStart(3, "0")}`;
|
|
130
|
+
acCounter += 1;
|
|
131
|
+
return id;
|
|
132
|
+
};
|
|
181
133
|
let objective;
|
|
182
134
|
const scope = [];
|
|
183
135
|
const nonGoals = [];
|
|
@@ -6,7 +6,6 @@ import { getTaskContractPaths } from "../contract/paths.js";
|
|
|
6
6
|
import { loadTaskConfig } from "../runtime.js";
|
|
7
7
|
import { buildPrepareDraft } from "./build-draft.js";
|
|
8
8
|
import { hasBlockingGaps, hasBlockingRisks, isFreshLegacyUnversioned, listPrepareGaps, } from "./completeness.js";
|
|
9
|
-
import { detectProductArtifactMeta, isIntakeSoftGapCode, } from "./artifact-meta.js";
|
|
10
9
|
import { extractRequirementFactsFromMarkdown, fillMissingRequirementFacts, mergeRequirementFacts, } from "./parse-intent.js";
|
|
11
10
|
import { draftReferencesFromManifest, validateImportedPrdReferences, } from "./reference-integrity.js";
|
|
12
11
|
import { readSourceManifest } from "../source-references.js";
|
|
@@ -132,139 +131,31 @@ function mutationOutcome(code) {
|
|
|
132
131
|
}
|
|
133
132
|
function buildNextSteps(input) {
|
|
134
133
|
const next = [];
|
|
135
|
-
const codes = new Set(input.gaps.map((g) => g.code));
|
|
136
134
|
if (!input.hasImportedPrd) {
|
|
137
|
-
next.push(`loop-agent
|
|
135
|
+
next.push(`loop-agent import-prd ${input.taskId} --file <prd.md> --role requirement`);
|
|
138
136
|
}
|
|
139
|
-
if (
|
|
140
|
-
next.push("
|
|
141
|
-
next.push("禁止改写原始 product-analysis / product-requirement 产物,禁止在原 PRD 目录生成兼容 Markdown 或 reviewed-draft.json");
|
|
137
|
+
if (input.gaps.some((g) => g.code === "EMPTY_ALLOWED_PATHS")) {
|
|
138
|
+
next.push("补 --allowed-path <glob>");
|
|
142
139
|
}
|
|
143
|
-
if (
|
|
144
|
-
next.push("
|
|
145
|
-
}
|
|
146
|
-
if (codes.has("PRODUCT_REQUIREMENT_PENDING")) {
|
|
147
|
-
next.push("先完成 analyze-product-requirements 澄清并将 product-requirement.md 标为 complete,再 task advance --prd");
|
|
148
|
-
}
|
|
149
|
-
if (codes.has("EMPTY_ALLOWED_PATHS")) {
|
|
150
|
-
next.push("补 --allowed-path <glob>(工程边界必须显式给出)");
|
|
151
|
-
}
|
|
152
|
-
if (codes.has("EMPTY_ACCEPTANCE") || codes.has("EMPTY_OBJECTIVE")) {
|
|
153
|
-
next.push("PRD 真缺验收标准/目标时:补完整 product-requirement(Given/When/Then 或 AC 标题),或显式 --from-text;不要自动生成 sidecar draft JSON");
|
|
154
|
-
}
|
|
155
|
-
if (codes.has("SEMANTIC_INTAKE_RECOMMENDED")) {
|
|
156
|
-
next.push("文档语义完整但结构非标准:整理为 product-requirement 或标准「目标/范围/非目标/验收标准」Markdown;不要在原 PRD 目录生成兼容 md/json");
|
|
157
|
-
}
|
|
158
|
-
if (codes.has("SEMANTIC_INTAKE_FAILED") || codes.has("SEMANTIC_INTAKE_INVALID_OUTPUT")) {
|
|
159
|
-
next.push("semantic intake 未能产出可接受 draft:补全 product-requirement 或标准验收标准后重试;禁止在原 PRD 目录生成 sidecar JSON");
|
|
140
|
+
if (input.gaps.some((g) => g.code === "EMPTY_ACCEPTANCE")) {
|
|
141
|
+
next.push("补 --ac <id:text> 或完善 PRD 验收标准章节");
|
|
160
142
|
}
|
|
161
143
|
if (input.mode === "dry-run" && input.ok) {
|
|
162
|
-
next.push(`loop-agent task
|
|
144
|
+
next.push(`loop-agent task source prepare ${input.taskId} --use-imported-prd ... --apply --json`);
|
|
163
145
|
}
|
|
164
146
|
if (input.ok && input.mode === "apply") {
|
|
165
147
|
next.push(`loop-agent task status ${input.taskId} --json`);
|
|
166
148
|
next.push(`loop-agent task advance ${input.taskId} --profile auto --json`);
|
|
167
149
|
next.push("审查 DAG writeSet 后再 task advance --approve-gate ...");
|
|
168
150
|
}
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
next.push(`高级恢复(仅人工 reviewed draft):loop-agent task advance ${input.taskId} --from-draft <reviewed-draft.json> --json`);
|
|
172
|
-
next.push("--from-draft 不是 EMPTY_ACCEPTANCE 的默认恢复路径;禁止在原 PRD 目录自动生成 draft JSON");
|
|
151
|
+
if (input.gaps.some((g) => g.code === "DIRTY_SOURCE" || g.code === "MANAGED_PROJECTION_INCOMPLETE")) {
|
|
152
|
+
next.push(`loop-agent task advance ${input.taskId} --from-draft <reviewed-draft.json> --json`);
|
|
173
153
|
}
|
|
174
|
-
if (
|
|
154
|
+
if (input.gaps.some((g) => g.code === "TRANSACTION_INCOMPLETE")) {
|
|
175
155
|
next.push(`loop-agent task advance ${input.taskId} --json`);
|
|
176
156
|
}
|
|
177
157
|
return next;
|
|
178
158
|
}
|
|
179
|
-
function appendProductArtifactGaps(input) {
|
|
180
|
-
if (!input.sourceIntegrity)
|
|
181
|
-
return;
|
|
182
|
-
const docs = input.sourceIntegrity.documents.filter((d) => d.content);
|
|
183
|
-
if (docs.length === 0)
|
|
184
|
-
return;
|
|
185
|
-
let sawAnalysisOnly = false;
|
|
186
|
-
let sawClarificationOnly = false;
|
|
187
|
-
let sawPendingRequirement = false;
|
|
188
|
-
let sawExecutableRequirement = false;
|
|
189
|
-
let sawUnknownNarrative = false;
|
|
190
|
-
for (const doc of docs) {
|
|
191
|
-
const meta = detectProductArtifactMeta(doc.content);
|
|
192
|
-
if (meta.artifactType === "product-analysis") {
|
|
193
|
-
sawAnalysisOnly = true;
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (meta.artifactType === "requirement-clarification") {
|
|
197
|
-
sawClarificationOnly = true;
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (meta.artifactType === "product-requirement") {
|
|
201
|
-
if (meta.requirementStatus &&
|
|
202
|
-
meta.requirementStatus.trim().toLowerCase() === "pending") {
|
|
203
|
-
sawPendingRequirement = true;
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
sawExecutableRequirement = true;
|
|
207
|
-
}
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (doc.role === "requirement" &&
|
|
211
|
-
meta.artifactType === "unknown" &&
|
|
212
|
-
!input.hasAcceptance) {
|
|
213
|
-
sawUnknownNarrative = true;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
const hasRequirementRole = input.sourceIntegrity.parseableDocuments.some((d) => d.role === "requirement" || d.role === "acceptance");
|
|
217
|
-
if (!hasRequirementRole && sawAnalysisOnly) {
|
|
218
|
-
input.gaps.push({
|
|
219
|
-
code: "PRODUCT_ANALYSIS_NOT_EXECUTABLE",
|
|
220
|
-
level: "blocking",
|
|
221
|
-
field: "acceptanceCriteria",
|
|
222
|
-
message: "imported document is product-analysis (no formal AC by design); use product-requirement.md as --prd requirement",
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
if (!hasRequirementRole &&
|
|
226
|
-
sawClarificationOnly &&
|
|
227
|
-
!sawExecutableRequirement) {
|
|
228
|
-
input.gaps.push({
|
|
229
|
-
code: "PRODUCT_CLARIFICATION_NOT_EXECUTABLE",
|
|
230
|
-
level: "blocking",
|
|
231
|
-
field: "acceptanceCriteria",
|
|
232
|
-
message: "imported document is requirement-clarification (decision log only); use complete product-requirement.md",
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
if (sawPendingRequirement && !input.hasAcceptance) {
|
|
236
|
-
input.gaps.push({
|
|
237
|
-
code: "PRODUCT_REQUIREMENT_PENDING",
|
|
238
|
-
level: "blocking",
|
|
239
|
-
field: "acceptanceCriteria",
|
|
240
|
-
message: "product-requirement is still pending clarification; complete analyze-product-requirements before task advance",
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
if (sawUnknownNarrative &&
|
|
244
|
-
!input.hasAcceptance &&
|
|
245
|
-
!sawPendingRequirement &&
|
|
246
|
-
!sawAnalysisOnly) {
|
|
247
|
-
input.gaps.push({
|
|
248
|
-
code: "SEMANTIC_INTAKE_RECOMMENDED",
|
|
249
|
-
level: "warning",
|
|
250
|
-
message: "requirement body lacks structured AC headings; use product-requirement or standard Markdown sections — do not auto-write sidecar drafts",
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
const precise = new Set([
|
|
254
|
-
"PRODUCT_ANALYSIS_NOT_EXECUTABLE",
|
|
255
|
-
"PRODUCT_CLARIFICATION_NOT_EXECUTABLE",
|
|
256
|
-
"PRODUCT_REQUIREMENT_PENDING",
|
|
257
|
-
]);
|
|
258
|
-
if (input.gaps.some((g) => precise.has(g.code))) {
|
|
259
|
-
for (const gap of input.gaps) {
|
|
260
|
-
if (gap.code === "EMPTY_ACCEPTANCE") {
|
|
261
|
-
gap.level = "warning";
|
|
262
|
-
gap.message =
|
|
263
|
-
"acceptance criteria empty (superseded by product-artifact diagnostic)";
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
159
|
function emptyObservedHashPlaceholder() {
|
|
269
160
|
return "0".repeat(64);
|
|
270
161
|
}
|
|
@@ -498,36 +389,13 @@ export async function prepareTaskSource(input) {
|
|
|
498
389
|
message: "no parseable requirement document and no --ac / supplemental text; import a requirement-role PRD or pass flags",
|
|
499
390
|
});
|
|
500
391
|
}
|
|
501
|
-
// Product Analysis / pending Product Requirement diagnostics (precise gaps).
|
|
502
|
-
if (input.intent.kind === "facts" && input.intent.useImportedPrd) {
|
|
503
|
-
appendProductArtifactGaps({
|
|
504
|
-
gaps,
|
|
505
|
-
sourceIntegrity,
|
|
506
|
-
hasAcceptance: built.draft.requirement.acceptanceCriteria.length > 0,
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
392
|
const validation = await validateDraftForTask({
|
|
510
393
|
repoRoot,
|
|
511
394
|
taskId,
|
|
512
395
|
draft: built.draft,
|
|
513
396
|
});
|
|
514
397
|
if (!validation.ok) {
|
|
515
|
-
const productArtifactBlocking = gaps.some((gap) => [
|
|
516
|
-
"PRODUCT_ANALYSIS_NOT_EXECUTABLE",
|
|
517
|
-
"PRODUCT_CLARIFICATION_NOT_EXECUTABLE",
|
|
518
|
-
"PRODUCT_REQUIREMENT_PENDING",
|
|
519
|
-
].includes(gap.code));
|
|
520
398
|
for (const message of validation.errors) {
|
|
521
|
-
const isAcceptanceSchema = /acceptance criterion|acceptanceCriteria/i.test(message);
|
|
522
|
-
// Prefer product-artifact diagnostics over generic schema AC errors.
|
|
523
|
-
if (productArtifactBlocking && isAcceptanceSchema) {
|
|
524
|
-
gaps.push({
|
|
525
|
-
code: "DRAFT_VALIDATION",
|
|
526
|
-
level: "warning",
|
|
527
|
-
message: `${message} (superseded by product-artifact diagnostic)`,
|
|
528
|
-
});
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
399
|
gaps.push({
|
|
532
400
|
code: "DRAFT_VALIDATION",
|
|
533
401
|
level: "blocking",
|
|
@@ -560,32 +428,13 @@ export async function prepareTaskSource(input) {
|
|
|
560
428
|
.filter((gap) => gap.level === "blocking")
|
|
561
429
|
.map((gap) => gap.code);
|
|
562
430
|
const transactionIncomplete = blockingCodes.includes("TRANSACTION_INCOMPLETE");
|
|
563
|
-
const productArtifactCodes = [
|
|
564
|
-
"PRODUCT_ANALYSIS_NOT_EXECUTABLE",
|
|
565
|
-
"PRODUCT_CLARIFICATION_NOT_EXECUTABLE",
|
|
566
|
-
"PRODUCT_REQUIREMENT_PENDING",
|
|
567
|
-
"PRODUCT_ARTIFACT_NOT_EXECUTABLE",
|
|
568
|
-
];
|
|
569
|
-
const primaryBlockingCode = blockingCodes.find((code) => productArtifactCodes.includes(code)) ??
|
|
570
|
-
blockingCodes.find((code) => code !== "EMPTY_ACCEPTANCE") ??
|
|
571
|
-
blockingCodes[0] ??
|
|
572
|
-
"INVALID_INPUT";
|
|
573
|
-
// Soft intake only when PRD was imported (or product-artifact diagnostics apply).
|
|
574
|
-
// Bare missing flags without import stay hard-invalid.
|
|
575
|
-
const softIntakeOnly = blocking &&
|
|
576
|
-
blockingCodes.length > 0 &&
|
|
577
|
-
blockingCodes.every((code) => isIntakeSoftGapCode(code)) &&
|
|
578
|
-
(hasImportedPrd ||
|
|
579
|
-
blockingCodes.some((code) => productArtifactCodes.includes(code)));
|
|
580
431
|
if (input.mode !== "apply") {
|
|
581
432
|
return {
|
|
582
433
|
ok: !blocking,
|
|
583
434
|
outcome: blocking
|
|
584
435
|
? transactionIncomplete
|
|
585
436
|
? "needs-reconcile"
|
|
586
|
-
:
|
|
587
|
-
? "blocked"
|
|
588
|
-
: "invalid"
|
|
437
|
+
: "invalid"
|
|
589
438
|
: "succeeded",
|
|
590
439
|
mode: "dry-run",
|
|
591
440
|
taskId,
|
|
@@ -603,13 +452,9 @@ export async function prepareTaskSource(input) {
|
|
|
603
452
|
error: {
|
|
604
453
|
code: transactionIncomplete
|
|
605
454
|
? "TRANSACTION_INCOMPLETE"
|
|
606
|
-
:
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
message: softIntakeOnly
|
|
610
|
-
? "PRD imported or facts present but intake incomplete; DAG not generated"
|
|
611
|
-
: "task source draft has blocking gaps",
|
|
612
|
-
details: { gaps: blockingCodes, softIntake: softIntakeOnly },
|
|
455
|
+
: "INVALID_INPUT",
|
|
456
|
+
message: "task source draft has blocking gaps",
|
|
457
|
+
details: { gaps: blockingCodes },
|
|
613
458
|
},
|
|
614
459
|
}
|
|
615
460
|
: {}),
|
|
@@ -641,9 +486,7 @@ export async function prepareTaskSource(input) {
|
|
|
641
486
|
if (blocking) {
|
|
642
487
|
const outcome = blockingCodes.includes("DIRTY_SOURCE")
|
|
643
488
|
? "blocked"
|
|
644
|
-
:
|
|
645
|
-
? "blocked"
|
|
646
|
-
: "invalid";
|
|
489
|
+
: "invalid";
|
|
647
490
|
return {
|
|
648
491
|
ok: false,
|
|
649
492
|
outcome,
|
|
@@ -659,17 +502,10 @@ export async function prepareTaskSource(input) {
|
|
|
659
502
|
contractStatus: state.effectiveStatus,
|
|
660
503
|
warnings,
|
|
661
504
|
error: {
|
|
662
|
-
code:
|
|
663
|
-
|
|
664
|
-
: softIntakeOnly
|
|
665
|
-
? primaryBlockingCode
|
|
666
|
-
: "INVALID_INPUT",
|
|
667
|
-
message: softIntakeOnly
|
|
668
|
-
? "PRD imported or facts present but intake incomplete; DAG not generated"
|
|
669
|
-
: "task source draft has blocking gaps",
|
|
505
|
+
code: outcome === "blocked" ? "DIRTY_SOURCE" : "INVALID_INPUT",
|
|
506
|
+
message: "task source draft has blocking gaps",
|
|
670
507
|
details: {
|
|
671
508
|
gaps: blockingCodes,
|
|
672
|
-
softIntake: softIntakeOnly,
|
|
673
509
|
},
|
|
674
510
|
},
|
|
675
511
|
};
|
|
@@ -5,16 +5,7 @@ import { getTaskPaths } from "../runtime.js";
|
|
|
5
5
|
import { readSourceManifest, } from "../source-references.js";
|
|
6
6
|
import { PREPARE_MAX_FILE_BYTES, PREPARE_MAX_PARSE_DOCS, PREPARE_MAX_TOTAL_BYTES, } from "./types.js";
|
|
7
7
|
const PARSEABLE_EXTENSIONS = new Set([".md", ".markdown", ".txt"]);
|
|
8
|
-
/** Roles that contribute requirement facts (objective/AC/scope). */
|
|
9
8
|
const PARSEABLE_ROLES = new Set(["requirement", "acceptance"]);
|
|
10
|
-
/** Roles whose body is loaded for artifact meta / diagnostics (not fact extraction). */
|
|
11
|
-
const CONTENT_LOAD_ROLES = new Set([
|
|
12
|
-
"requirement",
|
|
13
|
-
"acceptance",
|
|
14
|
-
"analysis",
|
|
15
|
-
"clarification",
|
|
16
|
-
"design",
|
|
17
|
-
]);
|
|
18
9
|
function isSafeRelativeReferencePath(materializedPath) {
|
|
19
10
|
const normalized = materializedPath.replace(/\\/g, "/").trim();
|
|
20
11
|
if (!normalized)
|
|
@@ -61,12 +52,6 @@ function isParseableDoc(doc) {
|
|
|
61
52
|
return false;
|
|
62
53
|
return PARSEABLE_ROLES.has(doc.role);
|
|
63
54
|
}
|
|
64
|
-
function shouldLoadContent(doc) {
|
|
65
|
-
const ext = path.extname(doc.materializedPath).toLowerCase();
|
|
66
|
-
if (!PARSEABLE_EXTENSIONS.has(ext))
|
|
67
|
-
return false;
|
|
68
|
-
return CONTENT_LOAD_ROLES.has(doc.role) || PARSEABLE_ROLES.has(doc.role);
|
|
69
|
-
}
|
|
70
55
|
/**
|
|
71
56
|
* Validate source-manifest + reference files before deriving a draft.
|
|
72
57
|
* Fail-closed on taskId/path/hash/symlink/size issues.
|
|
@@ -185,7 +170,6 @@ export async function validateImportedPrdReferences(input) {
|
|
|
185
170
|
continue;
|
|
186
171
|
}
|
|
187
172
|
const parseable = isParseableDoc(doc);
|
|
188
|
-
const loadContent = shouldLoadContent(doc);
|
|
189
173
|
if (parseable) {
|
|
190
174
|
parseableCount += 1;
|
|
191
175
|
if (parseableCount > PREPARE_MAX_PARSE_DOCS) {
|
|
@@ -199,7 +183,7 @@ export async function validateImportedPrdReferences(input) {
|
|
|
199
183
|
}
|
|
200
184
|
let hashResult;
|
|
201
185
|
try {
|
|
202
|
-
hashResult = await streamSha256AndBytes(absolutePath,
|
|
186
|
+
hashResult = await streamSha256AndBytes(absolutePath, parseable ? PREPARE_MAX_FILE_BYTES : undefined);
|
|
203
187
|
}
|
|
204
188
|
catch (error) {
|
|
205
189
|
if (error &&
|
|
@@ -239,7 +223,7 @@ export async function validateImportedPrdReferences(input) {
|
|
|
239
223
|
});
|
|
240
224
|
}
|
|
241
225
|
let content;
|
|
242
|
-
if (
|
|
226
|
+
if (parseable) {
|
|
243
227
|
totalParseBytes += hashResult.bytes;
|
|
244
228
|
if (totalParseBytes > PREPARE_MAX_TOTAL_BYTES) {
|
|
245
229
|
issues.push({
|
|
@@ -36,7 +36,6 @@ export function openConsoleAppData(options) {
|
|
|
36
36
|
drafts: path.join(repoNamespace, "drafts"),
|
|
37
37
|
operations: path.join(repoNamespace, "operations"),
|
|
38
38
|
confirmations: path.join(repoNamespace, "confirmations"),
|
|
39
|
-
dagReceipts: path.join(repoNamespace, "dag-receipts"),
|
|
40
39
|
staged: path.join(repoNamespace, "staged"),
|
|
41
40
|
assessments: path.join(repoNamespace, "assessments"),
|
|
42
41
|
interviews: path.join(repoNamespace, "interviews"),
|
|
@@ -48,7 +47,6 @@ export function openConsoleAppData(options) {
|
|
|
48
47
|
paths.drafts,
|
|
49
48
|
paths.operations,
|
|
50
49
|
paths.confirmations,
|
|
51
|
-
paths.dagReceipts,
|
|
52
50
|
paths.staged,
|
|
53
51
|
paths.assessments,
|
|
54
52
|
paths.interviews,
|