@tea-agent/loop-agent 0.16.6-beta.0 → 0.16.6
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 +6 -3
- package/dist/executors/dag-pi-executor.js +4 -44
- package/dist/executors/pi-sdk-executor.js +3 -1
- package/dist/executors/shell-write-guard.js +30 -0
- package/dist/worker/observe/spec-evidence.js +2 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +0 -77
- package/dist/workflows/dag/frontend-project-capability.js +7 -7
- package/dist/workflows/dag/frontend-risk.js +1 -1
- package/dist/workflows/dag/init-hybrid.js +5 -48
- package/dist/workflows/dag/types.js +0 -1
- package/docs/templates/agent-dag.schema.json +0 -5
- package/package.json +1 -1
- package/skills/frontend-design-review/SKILL.md +4 -4
- package/skills/frontend-design-review/references/review-checklist.md +3 -3
- package/skills/frontend-implementation/SKILL.md +1 -1
- package/skills/frontend-implementation/references/code-standards.md +1 -1
- package/skills/frontend-implementation/references/design-spec.md +9 -9
- package/skills/frontend-implementation/references/node-contracts.md +5 -5
- package/skills/frontend-review/SKILL.md +1 -1
- package/skills/frontend-review/references/review-findings.md +2 -2
- package/skills/frontend-verification/SKILL.md +2 -2
- package/skills/frontend-verification/references/verification-checklist.md +2 -2
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,15 +9,18 @@
|
|
|
9
9
|
|
|
10
10
|
### 修复
|
|
11
11
|
|
|
12
|
-
- 前端实现计划/修订节点会注入当前包内权威 `frontend-implementation-contract-v1` Schema 与固定 source binding,避免模型猜测字段导致契约门禁失败。
|
|
13
|
-
- 前端 Mock 策略节点的 canonical 输出会把首条 `MOCK_STRATEGY:` 协议行提升为第一行,避免解释性前言触发 `first-non-empty` 门禁误判。
|
|
14
|
-
- 前端规范回退目录统一为本地 `openspec/`,DAG 能力发现、提示词、Skill 与验证证据检查不再查找大小写不一致的旧目录名。
|
|
15
12
|
- 后端测试复合 Shell pipeline 现在与普通 Shell 节点共享 Git write guard;即使命令退出成功,只要越过 `read-only`、`allowedPaths` 或 `forbiddenPaths` 边界,节点仍会 fail-closed。
|
|
16
13
|
- Observe 现使用实际的 repair safety 节点,并只在修复节点真正开始执行后计入一次 attempt;条件跳过不再误报已修复,安全门禁失败会显示为 `rejected`。
|
|
17
14
|
- Pi SDK 对缺少响应 ID 的累计 Token 生命周期事件改为取本次执行最大快照,避免同一响应的匿名 usage 被重复累加。
|
|
18
15
|
- Pi SDK 执行长推理或大段结构化输出时不再把高频流式增量事件无界累积到内存;同一响应在多个生命周期事件中重复出现的 Token 用量只统计一次,避免 `Invalid string length` 和成本数据虚高。
|
|
19
16
|
- 后端测试复合执行节点继续保持 clean environment、失败分类和 fail-closed outcome,并为 initial/final Result、repair eligibility、traceability 与 Observe 投影保留结构化运行证据。
|
|
20
17
|
|
|
18
|
+
## [0.16.6] - 2026-07-20
|
|
19
|
+
|
|
20
|
+
### 修复
|
|
21
|
+
|
|
22
|
+
- write guard 忽略 `__pycache__` / `.pytest_cache` 等 ephemeral 解释器缓存,避免 backend-test 生成节点本地冒烟 pytest 时被误拦;并明确 generate-pytest 节点不负责执行测试。
|
|
23
|
+
|
|
21
24
|
## [0.16.5] - 2026-07-20
|
|
22
25
|
|
|
23
26
|
### 修复
|
|
@@ -272,7 +272,7 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
|
|
|
272
272
|
persona,
|
|
273
273
|
step,
|
|
274
274
|
});
|
|
275
|
-
const mapped = mapPiResultToDagNodeResult(result
|
|
275
|
+
const mapped = mapPiResultToDagNodeResult(result);
|
|
276
276
|
if (!isWriteTask) {
|
|
277
277
|
return mapped;
|
|
278
278
|
}
|
|
@@ -315,57 +315,17 @@ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
|
|
|
315
315
|
durationMs: mapped.durationMs || Date.now() - started,
|
|
316
316
|
};
|
|
317
317
|
}
|
|
318
|
-
export function mapPiResultToDagNodeResult(result
|
|
319
|
-
const assistantText = canonicalizeProtocolFirstLine(result.assistantText, firstProtocolLine);
|
|
318
|
+
export function mapPiResultToDagNodeResult(result) {
|
|
320
319
|
return {
|
|
321
320
|
ok: result.ok,
|
|
322
|
-
stdout: assistantText || result.stdout,
|
|
321
|
+
stdout: result.assistantText || result.stdout,
|
|
323
322
|
stderr: result.stderr,
|
|
324
323
|
failureCategory: result.failureCategory,
|
|
325
324
|
durationMs: result.durationMs,
|
|
326
|
-
assistantText,
|
|
325
|
+
assistantText: result.assistantText,
|
|
327
326
|
backend: result.backend,
|
|
328
327
|
sdkAttempted: result.sdkAttempted,
|
|
329
328
|
tokensUsed: result.tokensUsed,
|
|
330
329
|
parsedEvents: result.parsedEvents,
|
|
331
330
|
};
|
|
332
331
|
}
|
|
333
|
-
function canonicalizeProtocolFirstLine(assistantText, firstProtocolLine) {
|
|
334
|
-
if (!assistantText || !firstProtocolLine)
|
|
335
|
-
return assistantText;
|
|
336
|
-
const lines = assistantText.split(/\r?\n/);
|
|
337
|
-
let protocolIndex = -1;
|
|
338
|
-
let protocolLine = "";
|
|
339
|
-
for (const [index, line] of lines.entries()) {
|
|
340
|
-
const normalized = normalizeProtocolLine(line);
|
|
341
|
-
if (normalized.startsWith(firstProtocolLine)) {
|
|
342
|
-
protocolIndex = index;
|
|
343
|
-
protocolLine = normalized;
|
|
344
|
-
break;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (protocolIndex < 0)
|
|
348
|
-
return assistantText;
|
|
349
|
-
if (protocolIndex === 0) {
|
|
350
|
-
return [protocolLine, ...lines.slice(1)].join("\n");
|
|
351
|
-
}
|
|
352
|
-
const before = lines.slice(0, protocolIndex);
|
|
353
|
-
const after = lines.slice(protocolIndex + 1);
|
|
354
|
-
while (before.at(-1)?.trim() === "" &&
|
|
355
|
-
after.at(0)?.trim() === "") {
|
|
356
|
-
after.shift();
|
|
357
|
-
}
|
|
358
|
-
const bodyLines = [...before, ...after];
|
|
359
|
-
while (bodyLines.at(0)?.trim() === "")
|
|
360
|
-
bodyLines.shift();
|
|
361
|
-
while (bodyLines.at(-1)?.trim() === "")
|
|
362
|
-
bodyLines.pop();
|
|
363
|
-
return bodyLines.length > 0
|
|
364
|
-
? `${protocolLine}\n\n${bodyLines.join("\n")}`
|
|
365
|
-
: protocolLine;
|
|
366
|
-
}
|
|
367
|
-
function normalizeProtocolLine(line) {
|
|
368
|
-
const trimmed = line.trim();
|
|
369
|
-
const emphasized = trimmed.match(/^(\*{1,3})\s*(.*?)\s*\1$/);
|
|
370
|
-
return (emphasized?.[2] ?? trimmed).trim();
|
|
371
|
-
}
|
|
@@ -308,7 +308,9 @@ export async function executeSingleSdkAttempt(options) {
|
|
|
308
308
|
return;
|
|
309
309
|
const line = serializeSessionEvent(event);
|
|
310
310
|
stdoutLines.push(line);
|
|
311
|
-
sessionEventAppender
|
|
311
|
+
if (sessionEventAppender) {
|
|
312
|
+
sessionEventAppender.append(line, event);
|
|
313
|
+
}
|
|
312
314
|
});
|
|
313
315
|
const filePrefix = options.attachedFiles.map((file) => `@${file}`).join(' ');
|
|
314
316
|
const promptMessage = filePrefix
|
|
@@ -30,6 +30,33 @@ export function pathsChangedDuringRun(before, after) {
|
|
|
30
30
|
return Array.from(changed).sort();
|
|
31
31
|
}
|
|
32
32
|
const DAG_RUNS_PREFIX = ".harness/dag-runs/";
|
|
33
|
+
/**
|
|
34
|
+
* Ephemeral interpreter/tool caches that writers may create while drafting tests.
|
|
35
|
+
* They are not product evidence and must not fail exclusive write guards.
|
|
36
|
+
* Intentional report/product writes remain governed by writePolicy/writeSet.
|
|
37
|
+
*/
|
|
38
|
+
export function isEphemeralToolCachePath(filePath) {
|
|
39
|
+
const normalized = normalizePath(filePath);
|
|
40
|
+
if (!normalized)
|
|
41
|
+
return false;
|
|
42
|
+
if (normalized === ".pytest_cache" || normalized.startsWith(".pytest_cache/")) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
if (normalized === ".mypy_cache" || normalized.startsWith(".mypy_cache/")) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (normalized === ".ruff_cache" || normalized.startsWith(".ruff_cache/")) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (normalized === ".coverage" || normalized.startsWith(".coverage.")) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (/(?:^|\/)__pycache__(?:\/|$)/.test(normalized))
|
|
55
|
+
return true;
|
|
56
|
+
if (/\.(?:pyc|pyo)$/.test(normalized))
|
|
57
|
+
return true;
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
33
60
|
/** MVP post-run guard: only paths that changed during the shell node are checked. */
|
|
34
61
|
export function validateShellWriteGuard(input) {
|
|
35
62
|
const violations = [];
|
|
@@ -42,6 +69,9 @@ export function validateShellWriteGuard(input) {
|
|
|
42
69
|
if (filePath.startsWith(DAG_RUNS_PREFIX)) {
|
|
43
70
|
continue;
|
|
44
71
|
}
|
|
72
|
+
if (isEphemeralToolCachePath(filePath)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
45
75
|
if (matchesAnyPattern(filePath, forbiddenPaths)) {
|
|
46
76
|
violations.push(filePath);
|
|
47
77
|
continue;
|
|
@@ -18,7 +18,7 @@ const KB_CONNECTOR_TOOLS = new Set([
|
|
|
18
18
|
]);
|
|
19
19
|
/**
|
|
20
20
|
* Pattern for detecting spec-related files:
|
|
21
|
-
* -
|
|
21
|
+
* - openSpec/** files
|
|
22
22
|
* - *.spec.md / *.spec.ts / *.spec.tsx
|
|
23
23
|
* - project-specs/**
|
|
24
24
|
* - design-spec.md, code-standards.md, review-checklist.md, etc.
|
|
@@ -27,6 +27,7 @@ const KB_CONNECTOR_TOOLS = new Set([
|
|
|
27
27
|
*/
|
|
28
28
|
const SPEC_FILE_PATTERNS = [
|
|
29
29
|
/openspec\//i,
|
|
30
|
+
/\/openSpec\//i,
|
|
30
31
|
/\/project-specs\//i,
|
|
31
32
|
/\/spec\//i,
|
|
32
33
|
/\.spec\.(md|tsx?|jsx?)$/i,
|
|
@@ -1,86 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
2
|
import { readFile } from "node:fs/promises";
|
|
4
3
|
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
4
|
import { z } from "zod";
|
|
7
5
|
import { writeDagRunJsonArtifact } from "../../infrastructure/harness/artifact-store.js";
|
|
8
|
-
import { findPackageRoot } from "../../shared/package-metadata.js";
|
|
9
6
|
export const FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID = "frontend-implementation-contract-v1";
|
|
10
|
-
/**
|
|
11
|
-
* Load the canonical frontend-implementation-contract-v1 JSON Schema from the
|
|
12
|
-
* installed loop-agent package docs/templates/ path. Package-root discovery
|
|
13
|
-
* works from both the source module and the compiled dist module without
|
|
14
|
-
* relying on CommonJS globals in the ESM runtime.
|
|
15
|
-
*
|
|
16
|
-
* Validation is fail-closed: missing file, malformed JSON, mismatched $id,
|
|
17
|
-
* missing additionalProperties: false, or incomplete top-level required keys
|
|
18
|
-
* all throw before any DAG prompt is assembled.
|
|
19
|
-
*/
|
|
20
|
-
export function loadFrontendImplementationContractJsonSchema(startDir = path.dirname(fileURLToPath(import.meta.url))) {
|
|
21
|
-
const packageRoot = findPackageRoot(startDir);
|
|
22
|
-
if (!packageRoot) {
|
|
23
|
-
throw new Error(`cannot locate loop-agent package root from ${path.resolve(startDir)}`);
|
|
24
|
-
}
|
|
25
|
-
const schemaPath = path.join(packageRoot, "docs", "templates", "frontend-implementation-contract.schema.json");
|
|
26
|
-
let content;
|
|
27
|
-
try {
|
|
28
|
-
content = readFileSync(schemaPath, "utf-8");
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
throw new Error(`cannot load frontend-implementation-contract.schema.json from current loop-agent package at ${schemaPath}: ${error.code ?? String(error)}`);
|
|
32
|
-
}
|
|
33
|
-
let parsed;
|
|
34
|
-
try {
|
|
35
|
-
parsed = JSON.parse(content);
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
throw new Error(`frontend-implementation-contract.schema.json is not valid JSON: ${error.message}`);
|
|
39
|
-
}
|
|
40
|
-
if (parsed === null || typeof parsed !== "object") {
|
|
41
|
-
throw new Error("frontend-implementation-contract.schema.json root is not a JSON object");
|
|
42
|
-
}
|
|
43
|
-
const schema = parsed;
|
|
44
|
-
if (schema.$id !== FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID) {
|
|
45
|
-
throw new Error(`schema $id mismatch: expected ${FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID}, got ${String(schema.$id)}`);
|
|
46
|
-
}
|
|
47
|
-
if (schema.additionalProperties !== false) {
|
|
48
|
-
throw new Error("schema must have additionalProperties: false at top level");
|
|
49
|
-
}
|
|
50
|
-
const expectedRequired = [
|
|
51
|
-
"schemaVersion",
|
|
52
|
-
"sourceBinding",
|
|
53
|
-
"riskLevel",
|
|
54
|
-
"targets",
|
|
55
|
-
"requirements",
|
|
56
|
-
"uiStates",
|
|
57
|
-
"interactions",
|
|
58
|
-
"mockApi",
|
|
59
|
-
"designEvidence",
|
|
60
|
-
"verificationTargets",
|
|
61
|
-
"evidenceGaps",
|
|
62
|
-
];
|
|
63
|
-
const actualRequired = Array.isArray(schema.required) ? schema.required : [];
|
|
64
|
-
const missing = expectedRequired.filter((key) => !actualRequired.includes(key));
|
|
65
|
-
if (missing.length > 0) {
|
|
66
|
-
throw new Error(`schema required fields missing: ${missing.join(", ")}`);
|
|
67
|
-
}
|
|
68
|
-
const properties = schema.properties && typeof schema.properties === "object"
|
|
69
|
-
? schema.properties
|
|
70
|
-
: {};
|
|
71
|
-
const missingProperties = expectedRequired.filter((key) => !Object.hasOwn(properties, key));
|
|
72
|
-
if (missingProperties.length > 0) {
|
|
73
|
-
throw new Error(`schema properties missing: ${missingProperties.join(", ")}`);
|
|
74
|
-
}
|
|
75
|
-
const schemaVersion = properties.schemaVersion;
|
|
76
|
-
const mockApi = properties.mockApi;
|
|
77
|
-
const mockApiProperties = mockApi?.properties;
|
|
78
|
-
const productionDefaultOff = mockApiProperties?.productionDefaultOff;
|
|
79
|
-
if (schemaVersion?.const !== 1 || productionDefaultOff?.const !== true) {
|
|
80
|
-
throw new Error("schema fixed values are incomplete: schemaVersion.const must be 1 and mockApi.productionDefaultOff.const must be true");
|
|
81
|
-
}
|
|
82
|
-
return JSON.stringify(parsed);
|
|
83
|
-
}
|
|
84
7
|
const id = z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/);
|
|
85
8
|
const safePath = z
|
|
86
9
|
.string()
|
|
@@ -32,8 +32,8 @@ function allDeps(pkg) {
|
|
|
32
32
|
function hasDep(deps, name) {
|
|
33
33
|
return Object.hasOwn(deps, name);
|
|
34
34
|
}
|
|
35
|
-
async function
|
|
36
|
-
const root = path.join(repoRoot, "
|
|
35
|
+
async function listOpenSpec(repoRoot) {
|
|
36
|
+
const root = path.join(repoRoot, "openSpec");
|
|
37
37
|
if (!(await exists(root)))
|
|
38
38
|
return [];
|
|
39
39
|
const out = [];
|
|
@@ -70,7 +70,7 @@ export function buildAdapterGuidance(capability) {
|
|
|
70
70
|
"## Frontend project capability (generation-time)",
|
|
71
71
|
`Framework: ${capability.framework}${capability.frameworkVersion ? `@${capability.frameworkVersion}` : ""}`,
|
|
72
72
|
`Evidence: ${capability.evidencePaths.join(", ") || "(none)"}`,
|
|
73
|
-
"Rules:
|
|
73
|
+
"Rules: openSpec/** and task sources outrank adapter tips; do not invent APIs for unknown versions; lockfile-only is not enough.",
|
|
74
74
|
];
|
|
75
75
|
if (capability.framework === "react") {
|
|
76
76
|
lines.push("React adapter: prefer function components + hooks; reuse existing Testing Library / Vitest patterns; do not introduce new state libs without authorization.");
|
|
@@ -276,14 +276,14 @@ export async function discoverFrontendProjectCapability(repoRoot) {
|
|
|
276
276
|
router = router ?? "next-router";
|
|
277
277
|
if (hasDep(deps, "vue-router"))
|
|
278
278
|
router = "vue-router";
|
|
279
|
-
const
|
|
279
|
+
const openSpec = await listOpenSpec(repoRoot);
|
|
280
280
|
const designEvidence = {
|
|
281
|
-
normativePaths:
|
|
281
|
+
normativePaths: openSpec,
|
|
282
282
|
advisoryPaths: [],
|
|
283
283
|
conflicts: [],
|
|
284
284
|
};
|
|
285
|
-
if (
|
|
286
|
-
evidencePaths.push(...
|
|
285
|
+
if (openSpec.length)
|
|
286
|
+
evidencePaths.push(...openSpec.slice(0, 5));
|
|
287
287
|
const base = {
|
|
288
288
|
schemaVersion: 1,
|
|
289
289
|
framework,
|
|
@@ -22,7 +22,6 @@ import { buildBackendTestEffectiveResultSelectorShellSnippet, buildBackendTestRe
|
|
|
22
22
|
import { buildBackendTestOutcomeGateShellSnippet } from "./backend-test-result-contract.js";
|
|
23
23
|
import { classifyFrontendRisk, } from "./frontend-risk.js";
|
|
24
24
|
import { discoverFrontendProjectCapability, } from "./frontend-project-capability.js";
|
|
25
|
-
import { FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID, loadFrontendImplementationContractJsonSchema, } from "./frontend-implementation-contract.js";
|
|
26
25
|
const REQUIREMENT_FILE = "需求.md";
|
|
27
26
|
const CONSTRAINT_FILE = "执行约束.md";
|
|
28
27
|
const REFERENCE_DIRECTORY = "references";
|
|
@@ -1356,11 +1355,10 @@ function buildFrontendMockAssessNode(sources, sourceContext, mockContextBlock, f
|
|
|
1356
1355
|
allowedPaths: readOnlyPaths,
|
|
1357
1356
|
forbiddenPaths,
|
|
1358
1357
|
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
1359
|
-
|
|
1360
|
-
outputContract: "Plain Markdown whose first line is MOCK_STRATEGY: native|browser-intercept|request-adapter|not-needed|blocked, followed by Mock Decision, API Contract Evidence, Specification Evidence, Service Evidence, Backend Readiness, Selection Evidence, Endpoint / Fixture Matrix, Activation, Target Files, Production Safety, Verification Plan, Real Integration Gap, and Blocking Issues. No file writes.",
|
|
1358
|
+
outputContract: "Plain Markdown whose first non-empty line is MOCK_STRATEGY: native|browser-intercept|request-adapter|not-needed|blocked, followed by Mock Decision, API Contract Evidence, Specification Evidence, Service Evidence, Backend Readiness, Selection Evidence, Endpoint / Fixture Matrix, Activation, Target Files, Production Safety, Verification Plan, Real Integration Gap, and Blocking Issues. No file writes.",
|
|
1361
1359
|
subtask_prompt: [
|
|
1362
1360
|
"Perform read-only Mock assessment and select one safe frontend data strategy.",
|
|
1363
|
-
"The first line must be exactly one of: MOCK_STRATEGY: native, MOCK_STRATEGY: browser-intercept, MOCK_STRATEGY: request-adapter, MOCK_STRATEGY: not-needed, or MOCK_STRATEGY: blocked.
|
|
1361
|
+
"The first non-empty line must be exactly one of: MOCK_STRATEGY: native, MOCK_STRATEGY: browser-intercept, MOCK_STRATEGY: request-adapter, MOCK_STRATEGY: not-needed, or MOCK_STRATEGY: blocked.",
|
|
1364
1362
|
"Prefer an existing native Mock facility. Use browser-intercept only with an existing browser/e2e harness. When no Mock exists but the API layer is writable, use request-adapter by adding a minimal reversible adapter/DI seam within the approved writeSet; the real adapter must remain the production default.",
|
|
1365
1363
|
autoMaySkipMissingMock
|
|
1366
1364
|
? "Auto mode may skip Mock when no project Mock capability is confirmed. Select not-needed with positive evidence from contract/scout that no project Mock capability is confirmed, continue without adding Mock files or dependencies, run the fixed verification entrypoints, and record any unproved real API behavior in Real Integration Gap. Do not block solely because no project Mock capability, browser interception harness, or request adapter exists."
|
|
@@ -1618,7 +1616,7 @@ function resolveFrontendCapabilityContextBlock(sources) {
|
|
|
1618
1616
|
if (capability) {
|
|
1619
1617
|
parts.push("", capability.adapterGuidance);
|
|
1620
1618
|
if (capability.designEvidence.normativePaths.length > 0) {
|
|
1621
|
-
parts.push(`
|
|
1619
|
+
parts.push(`openSpec normative candidates: ${capability.designEvidence.normativePaths.slice(0, 12).join(", ")}`);
|
|
1622
1620
|
}
|
|
1623
1621
|
parts.push(`A11y capability: ${capability.a11y.status}` +
|
|
1624
1622
|
(capability.a11y.tools.length
|
|
@@ -1751,46 +1749,6 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1751
1749
|
allowedPaths: taskConfig.allowedPaths,
|
|
1752
1750
|
complexity: taskConfig.complexity,
|
|
1753
1751
|
});
|
|
1754
|
-
const frontendSourceBinding = buildDagSourceBinding(sources);
|
|
1755
|
-
const frontendContractSchemaBlock = (() => {
|
|
1756
|
-
const schema = loadFrontendImplementationContractJsonSchema();
|
|
1757
|
-
const requirement = frontendSourceBinding.sources.find((source) => source.kind === "requirement");
|
|
1758
|
-
if (!requirement) {
|
|
1759
|
-
throw new Error("frontend implementation contract context requires a bound requirement source");
|
|
1760
|
-
}
|
|
1761
|
-
const referencePaths = frontendSourceBinding.sources
|
|
1762
|
-
.filter((s) => s.kind === "reference")
|
|
1763
|
-
.map((s) => s.path);
|
|
1764
|
-
const fixedFields = {
|
|
1765
|
-
schemaVersion: 1,
|
|
1766
|
-
sourceBinding: {
|
|
1767
|
-
taskId: frontendSourceBinding.taskId,
|
|
1768
|
-
requirementPath: requirement.path,
|
|
1769
|
-
requirementSha256: requirement.sha256,
|
|
1770
|
-
referencePaths,
|
|
1771
|
-
requirementIds: frontendSourceBinding.requirementIds,
|
|
1772
|
-
},
|
|
1773
|
-
riskLevel: frontendRisk.selectedRisk,
|
|
1774
|
-
targets: { files: implementPaths.writeSet },
|
|
1775
|
-
};
|
|
1776
|
-
return [
|
|
1777
|
-
`## ${FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID} JSON Schema (authoritative; do not guess fields)`,
|
|
1778
|
-
schema,
|
|
1779
|
-
"",
|
|
1780
|
-
"## Fixed contract fields (deterministic; copy exactly and do not modify)",
|
|
1781
|
-
JSON.stringify(fixedFields),
|
|
1782
|
-
"",
|
|
1783
|
-
"## Forbidden fields (these are NOT in the schema; do not emit)",
|
|
1784
|
-
"- schemaId",
|
|
1785
|
-
"- targetFiles",
|
|
1786
|
-
"- requirementCoverage",
|
|
1787
|
-
"",
|
|
1788
|
-
"## Critical rules",
|
|
1789
|
-
"- verificationTargets is a TOP-LEVEL required array",
|
|
1790
|
-
"- uiStates items use name/applicable/expectedBehavior/implementationTargets/verificationTargetIds/notApplicableReason",
|
|
1791
|
-
"- mockApi.productionDefaultOff must always be true (including strategy: not-needed)",
|
|
1792
|
-
].join("\n");
|
|
1793
|
-
})();
|
|
1794
1752
|
const sourceContext = [
|
|
1795
1753
|
buildSourceContextBlock(sources),
|
|
1796
1754
|
capabilityContextBlock,
|
|
@@ -1799,7 +1757,7 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1799
1757
|
.join("\n\n");
|
|
1800
1758
|
const hasMockVerifyCommands = (taskConfig.frontendMock?.verifyCommands.length ?? 0) > 0 ||
|
|
1801
1759
|
mockCapability.verifyCommands.length > 0;
|
|
1802
|
-
const requirementIds =
|
|
1760
|
+
const requirementIds = buildDagSourceBinding(sources).requirementIds;
|
|
1803
1761
|
const requirementCoverageInstruction = requirementIds.length > 0
|
|
1804
1762
|
? `Include a Requirement Coverage section that lists every exact source identifier: ${requirementIds.join(", ")}. Preserve each identifier verbatim and map it to concrete implementation and verification steps.`
|
|
1805
1763
|
: "";
|
|
@@ -1958,7 +1916,6 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1958
1916
|
fixedVerificationContext,
|
|
1959
1917
|
sourceContext,
|
|
1960
1918
|
mockContextBlock,
|
|
1961
|
-
frontendContractSchemaBlock,
|
|
1962
1919
|
].join("\n\n"),
|
|
1963
1920
|
},
|
|
1964
1921
|
{
|
|
@@ -2035,7 +1992,6 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
2035
1992
|
"Read-only: do not modify code, docs, artifacts, or repository files. This node revises the plan only.",
|
|
2036
1993
|
"End the response with exactly one fenced json object conforming to frontend-implementation-contract-v1. Bind it to the supplied task sources; map every requirement and applicable UI state to concrete implementation and verification targets or an explicit blocking evidence gap. Do not include secrets or unsafe paths.",
|
|
2037
1994
|
sourceContext,
|
|
2038
|
-
frontendContractSchemaBlock,
|
|
2039
1995
|
].join("\n\n"),
|
|
2040
1996
|
},
|
|
2041
1997
|
...(requirementIds.length > 0
|
|
@@ -3052,6 +3008,7 @@ function buildGenerateBackendPytestNode(sources) {
|
|
|
3052
3008
|
"- If a test filename exists, add suffix: test_order.py → test_order_01.py",
|
|
3053
3009
|
"- Do NOT re-read source documents — use reviewed cases under testcase/md/ and upstream analyze-inputs-pi output only",
|
|
3054
3010
|
"- Read existing conftest.py/pytest.ini to understand conventions, but do NOT modify them",
|
|
3011
|
+
"- Do NOT execute pytest/python -m pytest or npm test in this node; initial/final execution is owned by dedicated shell nodes. Local smoke runs create __pycache__/.pytest_cache and are unnecessary here.",
|
|
3055
3012
|
].join("\n\n"),
|
|
3056
3013
|
};
|
|
3057
3014
|
}
|
|
@@ -306,7 +306,6 @@ export const dagTaskSchema = z.object({
|
|
|
306
306
|
shell: dagShellConfigSchema.optional(),
|
|
307
307
|
static: dagStaticConfigSchema.optional(),
|
|
308
308
|
outputContract: z.string().optional(),
|
|
309
|
-
firstProtocolLine: z.string().min(1).optional(),
|
|
310
309
|
allowedPaths: z.array(z.string()).optional().default([]),
|
|
311
310
|
forbiddenPaths: z.array(z.string()).optional().default([]),
|
|
312
311
|
decisionGate: dagDecisionGateSchema.optional(),
|
|
@@ -389,11 +389,6 @@
|
|
|
389
389
|
"type": "string",
|
|
390
390
|
"minLength": 1
|
|
391
391
|
},
|
|
392
|
-
"firstProtocolLine": {
|
|
393
|
-
"type": "string",
|
|
394
|
-
"minLength": 1,
|
|
395
|
-
"description": "Optional protocol prefix whose first matching Pi assistant-output line is promoted to the canonical first line. Missing matches are not synthesized."
|
|
396
|
-
},
|
|
397
392
|
"allowedPaths": {
|
|
398
393
|
"type": "array",
|
|
399
394
|
"items": { "type": "string", "minLength": 1 },
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@ references:
|
|
|
12
12
|
For first/final design review nodes. Read the checklist, then audit contract, scout,
|
|
13
13
|
mock strategy, plan/revision, task constraints/bounds, and traceable design evidence. The knowledge-
|
|
14
14
|
base connector is TODO: never invent results. If absent/failed/unmatched, require
|
|
15
|
-
`<repoRoot>/
|
|
15
|
+
`<repoRoot>/openSpec/**` search/read evidence before repo conventions.
|
|
16
16
|
|
|
17
17
|
## Verdict Contract
|
|
18
18
|
|
|
@@ -27,7 +27,7 @@ remaining, incomplete, or newly introduced gaps.
|
|
|
27
27
|
|
|
28
28
|
- Any criterion lacks implementation/verification; UI states lack reasons; a
|
|
29
29
|
dependency lacks permission; confirmed primitives/rules are ignored; design claims
|
|
30
|
-
lack knowledge-base or required `
|
|
30
|
+
lack knowledge-base or required `openSpec/` evidence; paths cross write bounds;
|
|
31
31
|
commands are missing/non-deterministic; or interaction, responsive, accessibility,
|
|
32
32
|
data, or failure behavior requires guessing.
|
|
33
33
|
- `MOCK_STRATEGY: blocked`; missing permitted target paths, endpoint/schema-to-fixture
|
|
@@ -35,7 +35,7 @@ remaining, incomplete, or newly introduced gaps.
|
|
|
35
35
|
inline fake data; commented real requests; Mock-on production defaults; test-only
|
|
36
36
|
production imports; or Mock evidence reported as real integration.
|
|
37
37
|
|
|
38
|
-
Knowledge-base absence is advisory if relevant `
|
|
38
|
+
Knowledge-base absence is advisory if relevant `openSpec/` rules were searched/read
|
|
39
39
|
and applied. Block skipped fallback, unresolved conflict, or unresolved UI decisions.
|
|
40
40
|
|
|
41
41
|
## Method And Output
|
|
@@ -48,7 +48,7 @@ Advisory, and never edit files.
|
|
|
48
48
|
Run `grep`/`find`, then explicit `read` calls for applicable specs and checklist.
|
|
49
49
|
Only successful paired reads count as “已读取规范文件”; summaries do not. List each
|
|
50
50
|
read path/section in `Checked Items`. If the connector is unavailable, search/read
|
|
51
|
-
`
|
|
51
|
+
`openSpec/` before accepting repository conventions.
|
|
52
52
|
|
|
53
53
|
```markdown
|
|
54
54
|
VERDICT: pass
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
## Project Fit
|
|
10
10
|
|
|
11
11
|
- Reuse components, hooks, API helpers, mocks, schemas, router patterns, tokens, and theme rules.
|
|
12
|
-
- Cite knowledge-base or `
|
|
12
|
+
- Cite knowledge-base or `openSpec/`; failed/empty knowledge queries must search `<repoRoot>/openSpec/**`.
|
|
13
13
|
- Record source status, query terms, paths/headings, conflicts, authorized deps, and allowed paths.
|
|
14
14
|
|
|
15
15
|
## Interaction / Quality
|
|
@@ -35,6 +35,6 @@
|
|
|
35
35
|
|
|
36
36
|
## Verdict Matrix
|
|
37
37
|
|
|
38
|
-
- Request revision for coverage gaps, unsafe scope, unauthorized deps, unresolved required interaction, missing required verification, skipped `
|
|
39
|
-
- Knowledge-base unavailable but relevant `
|
|
38
|
+
- Request revision for coverage gaps, unsafe scope, unauthorized deps, unresolved required interaction, missing required verification, skipped `openSpec/` fallback, unsafe/missing Mock strategy, or Mock evidence presented as real integration.
|
|
39
|
+
- Knowledge-base unavailable but relevant `openSpec/` rules applied is advisory only.
|
|
40
40
|
- Optional cleanup that cannot affect acceptance is advisory.
|
|
@@ -23,7 +23,7 @@ Read all required references before running any listed frontend node.
|
|
|
23
23
|
## Source And Evidence Rules
|
|
24
24
|
|
|
25
25
|
Use task sources/references, constraints, then `task.json`. Follow `design-spec.md`:
|
|
26
|
-
knowledge base; `<repoRoot>/
|
|
26
|
+
knowledge base; `<repoRoot>/openSpec/**` after unavailable/failed/empty; then repo
|
|
27
27
|
evidence. Cite tight paths/symbols, label gaps/conflicts, and never invent APIs,
|
|
28
28
|
rules, commands, or retrievals. Scout/planners locate and explicitly read applicable
|
|
29
29
|
specs; only successful paired reads count. Lockfile-only, fixture-only, or unread
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Discover rules from task constraints, `design-spec.md` source order, config, code,
|
|
4
4
|
tests, manifests, and generated types. After knowledge-base failure, applicable
|
|
5
|
-
`<repoRoot>/
|
|
5
|
+
`<repoRoot>/openSpec/**` rules are normative. Preferences are not rules, and docs do
|
|
6
6
|
not override installed APIs without an explicit compatibility decision.
|
|
7
7
|
|
|
8
8
|
## Discover And Cite
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
## Required Source Sequence
|
|
4
4
|
|
|
5
5
|
1. Attempt the configured component/design knowledge-base query first.
|
|
6
|
-
2. If unavailable, failed, timed out, or unmatched, recursively search the project root's exact `
|
|
6
|
+
2. If unavailable, failed, timed out, or unmatched, recursively search the project root's exact `openSpec/` directory.
|
|
7
7
|
3. Treat relevant matches as the current project's specification for this run.
|
|
8
8
|
4. Only then use component source, tokens, stories, tests, and pages as non-normative repository fallback.
|
|
9
9
|
|
|
10
|
-
Never skip `
|
|
10
|
+
Never skip `openSpec/` directly to neighboring-code conventions. Report source
|
|
11
11
|
conflicts instead of combining them. Explicit task requirements remain the contract;
|
|
12
|
-
flag conflicts with knowledge-base or `
|
|
12
|
+
flag conflicts with knowledge-base or `openSpec/` rules.
|
|
13
13
|
|
|
14
14
|
## Knowledge Base Connection — TODO
|
|
15
15
|
|
|
@@ -17,11 +17,11 @@ Request format is undecided. TODO: define connector/owner, namespaces, secret-fr
|
|
|
17
17
|
auth, query fields, result identity/version/time, and failure behavior.
|
|
18
18
|
|
|
19
19
|
Attempt only a connector actually available in the execution environment. Otherwise
|
|
20
|
-
record `not-configured` and run the `
|
|
20
|
+
record `not-configured` and run the `openSpec/` fallback; never invent a connection.
|
|
21
21
|
|
|
22
|
-
## `
|
|
22
|
+
## `openSpec/` Fallback Procedure
|
|
23
23
|
|
|
24
|
-
- Confirm whether `<repoRoot>/
|
|
24
|
+
- Confirm whether `<repoRoot>/openSpec/` exists and enumerate its files recursively.
|
|
25
25
|
- Read indexes first, then search names/content using task, route, component, interaction, theme, token, and state terms.
|
|
26
26
|
- Read relevant matches in context; do not treat a filename-only hit as a rule.
|
|
27
27
|
- Record search terms, inspected/matched paths, headings or tight line ranges, applied rules, and conflicts.
|
|
@@ -29,9 +29,9 @@ record `not-configured` and run the `openspec/` fallback; never invent a connect
|
|
|
29
29
|
|
|
30
30
|
## Retrieval Evidence
|
|
31
31
|
|
|
32
|
-
Record source as `knowledge-base`, `
|
|
32
|
+
Record source as `knowledge-base`, `openSpec fallback`, `repository fallback`, or
|
|
33
33
|
`unavailable`. Knowledge-base evidence includes query, source ID/version/time, rules,
|
|
34
|
-
and conflicts. `
|
|
34
|
+
and conflicts. `openSpec fallback` includes terms, paths/headings/lines, rules, and conflicts.
|
|
35
35
|
|
|
36
36
|
## Rules To Retrieve Or Discover
|
|
37
37
|
|
|
@@ -42,5 +42,5 @@ and conflicts. `openspec fallback` includes terms, paths/headings/lines, rules,
|
|
|
42
42
|
|
|
43
43
|
- Reuse confirmed primitives unless a new pattern is authorized.
|
|
44
44
|
- Define applicable states and responsive behavior before implementation.
|
|
45
|
-
- Cite knowledge-base or `
|
|
45
|
+
- Cite knowledge-base or `openSpec/` evidence for component/token choices; label weaker repository fallback.
|
|
46
46
|
- Make deviations and unresolved gaps explicit.
|
|
@@ -5,11 +5,11 @@ Pre-write nodes are read-only. Preserve IDs, labels, commands, language, require
|
|
|
5
5
|
## Core nodes
|
|
6
6
|
|
|
7
7
|
- **`frontend-contract-pi`**: `Scope`, `Non-goals`, `Acceptance Criteria`, `UI States`, `Target Runtime Environment`, `Risks`, `Verification Expectations`. No guessed requirements.
|
|
8
|
-
- **`frontend-scout-pi`**: routes, components, tokens, data/API/Mock, scripts, tests, assets. Fact vs inference vs gap. Knowledge base first; else search+read `<repoRoot>/
|
|
9
|
-
- **`frontend-mock-assess-pi` + gate**:
|
|
8
|
+
- **`frontend-scout-pi`**: routes, components, tokens, data/API/Mock, scripts, tests, assets. Fact vs inference vs gap. Knowledge base first; else search+read `<repoRoot>/openSpec/**` before repo fallback. Output stack, routes, components, styling, conventions, state/data, test entry points, reuse, risks.
|
|
9
|
+
- **`frontend-mock-assess-pi` + gate**: first non-empty line
|
|
10
10
|
`MOCK_STRATEGY: native|browser-intercept|request-adapter|not-needed|blocked`
|
|
11
|
-
|
|
12
|
-
- **`frontend-plan-pi` + design loop**: AC → steps, in-bound files, UI states, reuse, deps, activation/rollback, frozen verify entrypoints, real-integration gap. First gate: `VERDICT: pass|request-revision`. Pass may emit `PASS_NO_REVISION_NEEDED`; else full corrected plan without invented evidence. Final review rechecks plan/findings/revision/assessment/Mock safety. Only final `VERDICT: pass` authorizes writes; failure → replan/rerun (not dev-fix).
|
|
11
|
+
Prefer native Mock; browser intercept only with existing e2e; request-adapter only for reversible local preview. Default `auto` may select `not-needed` when contract/scout evidence confirms no project Mock capability, without adding Mock files/deps, while keeping real requests default and recording the Real Integration Gap. Other `not-needed` cases need positive no-remote/stable-backend evidence; invalid when `frontendMock.policy=required`. `blocked` for missing/conflicting contracts, unsafe paths/deps, unread specs, production-default-on, unverifiable entrypoints. Output Mock Decision, API/spec/service evidence, backend readiness, selection evidence, endpoint/fixture matrix, activation, targets, production safety, verification plan, real-integration gap, blocking issues. Never invent fields, store secrets, comment real requests, import test mocks into production, or treat Mock as real integration. Gate uses `first-non-empty` only; never authorizes writes. Unsafe required contracts → no writer.
|
|
12
|
+
- **`frontend-plan-pi` + design loop**: AC → steps, in-bound files, UI states, reuse, deps, activation/rollback, frozen verify entrypoints, real-integration gap. First gate: `VERDICT: pass|request-revision`. Pass may emit `PASS_NO_REVISION_NEEDED`; else full corrected plan without invented evidence. Final review rechecks plan/findings/revision/assessment/Mock safety. Only final `VERDICT: pass` authorizes writes; failure → replan/rerun (not dev-fix).
|
|
13
13
|
- **`frontend-implement-pi`**: sole exclusive writer. Stay in `writeSet`; real requests default-on; Mock reversible, dev/test-only, production-off. Atomic handler/intercept/adapter with consumer+tests. Stop on forbidden paths or guesses. Output changed files, behavior, UI states, styling notes, verification attempted, residual risks. Optional mock-verify when frozen; static+behavior always; behavior must prove page consumption. Skipped-Mock `not-needed` keeps real integration pending unless the real backend path has fresh evidence.
|
|
14
14
|
|
|
15
15
|
## Contract / trace / stages (M1–M2)
|
|
@@ -24,4 +24,4 @@ static/behavior/trace may `nonZeroExitPolicy: record`. Assess → `contracts/fro
|
|
|
24
24
|
|
|
25
25
|
## Risk & capability (M4–M6)
|
|
26
26
|
|
|
27
|
-
Deterministic risk (no model); high-risk beats small; supervised never small. Small may drop first design gate + plan-revision; contract shell retargets to `frontend-plan-pi`. Capability seed injects adapters;
|
|
27
|
+
Deterministic risk (no model); high-risk beats small; supervised never small. Small may drop first design gate + plan-revision; contract shell retargets to `frontend-plan-pi`. Capability seed injects adapters; openSpec/task sources outrank. A11y: static/component tools only when present; Browser a11y always not-run.
|
|
@@ -37,7 +37,7 @@ required check, forbidden write, or unmet acceptance criterion forces revision.
|
|
|
37
37
|
and no false real-integration claim. `not-needed` needs applicable real/no-remote
|
|
38
38
|
evidence, or an explicit default-auto skipped-Mock rationale with the Real
|
|
39
39
|
Integration Gap preserved when no project Mock capability is confirmed.
|
|
40
|
-
- Component/design claims require traceable knowledge-base evidence or, after connection/query failure or no match, relevant `<repoRoot>/
|
|
40
|
+
- Component/design claims require traceable knowledge-base evidence or, after connection/query failure or no match, relevant `<repoRoot>/openSpec/**` evidence. The connector format is TODO; never claim a query or fallback search without evidence. Execute explicit `grep`/`find` to locate spec files and `read` to load them before referencing their rules. Only successful `read` tool calls are observable as "已读取规范文件" in the spec-evidence inspector.
|
|
41
41
|
- Treat shell exit status as authoritative. Do not edit files.
|
|
42
42
|
|
|
43
43
|
## Evidence And Output
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- Cite tight file locations, exact commands/results, or named DAG artifacts.
|
|
12
12
|
- Never invent evidence; name the missing check. An implementation summary is not the actual diff.
|
|
13
13
|
- Failed required static/behavior verification is at least Important unless proven unrelated.
|
|
14
|
-
- A knowledge-base claim records connector/query, source ID/version, and retrieval time. If absent, failed, or unmatched, review evidence must show `<repoRoot>/
|
|
14
|
+
- A knowledge-base claim records connector/query, source ID/version, and retrieval time. If absent, failed, or unmatched, review evidence must show `<repoRoot>/openSpec/**` search terms and matched paths/headings; label `openSpec fallback`, `repository fallback`, or `unavailable` accurately.
|
|
15
15
|
|
|
16
16
|
## Review Sequence
|
|
17
17
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
6. Check component/design evidence, responsive/accessibility behavior, dependencies, and maintenance fit when applicable.
|
|
26
26
|
7. Classify findings and derive the verdict mechanically.
|
|
27
27
|
|
|
28
|
-
Skipping the required `
|
|
28
|
+
Skipping the required `openSpec/` search after knowledge-base failure is Important
|
|
29
29
|
when component/design compliance affects acceptance or implementation choices.
|
|
30
30
|
|
|
31
31
|
Use one issue per finding:
|
|
@@ -24,9 +24,9 @@ verdict/findings, and required browser, visual, manual, or knowledge evidence.
|
|
|
24
24
|
- Mock-backed behavior proves frontend rendering and state transitions only. It never
|
|
25
25
|
proves backend readiness, transport compatibility, or real API integration.
|
|
26
26
|
- Unavailable commands remain gaps.
|
|
27
|
-
- Resolve design evidence via knowledge base, then `<repoRoot>/
|
|
27
|
+
- Resolve design evidence via knowledge base, then `<repoRoot>/openSpec/**` after
|
|
28
28
|
failure/no match. Its connector format remains TODO; never invent it. An applied
|
|
29
|
-
`
|
|
29
|
+
`openSpec fallback` is available project evidence.
|
|
30
30
|
- Separate Mock service/handler checks from page consumption and record the
|
|
31
31
|
dev/test-only boundary; handler tests alone do not prove page use.
|
|
32
32
|
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
## Design And Component Evidence
|
|
14
14
|
|
|
15
|
-
- Claims cite knowledge-base retrieval or `<repoRoot>/
|
|
15
|
+
- Claims cite knowledge-base retrieval or `<repoRoot>/openSpec/**` fallback.
|
|
16
16
|
- Evidence records query/source/time or fallback search terms, paths, headings, and applied rules.
|
|
17
|
-
- Relevant `
|
|
17
|
+
- Relevant `openSpec/` matches satisfy source availability; missing both sources blocks explicit compliance or required design decisions.
|
|
18
18
|
|
|
19
19
|
## Status
|
|
20
20
|
|
|
@@ -120,7 +120,7 @@ contract-pi → scout-src ∥ scout-tests → plan-pi → write-set-audit-pi
|
|
|
120
120
|
| `authority-surface-audit-pi` + `authority-surface-gate-shell` | 可选 permission/state/tool-exposure audit;仅 authority signal 或显式 `authority-surface-audit` marker 时插入;gate 仅接受 `VERDICT: pass` |
|
|
121
121
|
| `review-pi` + `review-verdict-recovery-pi` + `review-gate-shell` | Critical/Important → `request-revision`;recovery 只规范化 VERDICT 协议(不得从自然语言猜 pass);gate 只认 `review-verdict-recovery-pi` 的 `VERDICT: pass` |
|
|
122
122
|
|
|
123
|
-
**Verdict gate contract(`shell.verdictGate`)**:声明 `fromNodeId`、`accept[]`、可选 `label`、可选 `lineMode`。runner 展开为一条 shell command,从 injected current run directory 读 `$HARNESS_DAG_RUN_DIR/<fromNodeId>.json`,对 extracted `assistantText ?? stdout` verdict line 与 `accept[]` exact-match。默认 `lineMode` 为 `first-non-empty`
|
|
123
|
+
**Verdict gate contract(`shell.verdictGate`)**:声明 `fromNodeId`、`accept[]`、可选 `label`、可选 `lineMode`。runner 展开为一条 shell command,从 injected current run directory 读 `$HARNESS_DAG_RUN_DIR/<fromNodeId>.json`,对 extracted `assistantText ?? stdout` verdict line 与 `accept[]` exact-match。默认 `lineMode` 为 `first-non-empty` 以兼容;supervised gate 用 `first-verdict-line` 选 Pi 在 preamble 或常见整行 Markdown emphasis(如 `**VERDICT: pass**`)后第一条 normalized `VERDICT:` line。勿用 `result.summary.md`、grep VERDICT、latest-active-run discovery 或 multi-command stateful gate。`--strict-governance` 对 anti-pattern fail。supervisor 仍为 `executor: pi` 上的 `role: supervisor`。
|
|
124
124
|
|
|
125
125
|
**Repair artifact gate contract(`shell.repairArtifactGate`)**:声明 `fromNodeId`(supervisor artifact 节点)与 `repairNodeId`(承接修订的 Pi 修复节点)。runner **不再**按节点名(历史 `repair-cursor` / `repair-pi`)猜测 repair 节点:显式 `repairNodeId` 必须存在、直接 `depends_on` gate、且是受治理 Pi writer(`executor: pi`、`toolProfile: write`、`writePolicy: exclusive`、`allowedPaths`+`writeSet` 非空且 `writeSet` 不与 `forbiddenPaths` 冲突)。新生成的 supervised DAG 总是写入 `repairNodeId`;旧 DAG 缺失时只在能唯一、安全地推导出下游 Pi writer 时兼容,零个或多个候选、或候选不满足契约都在执行前 fail closed。validation 覆盖存在性、直接下游、writer 属性与路径边界。
|
|
126
126
|
|