@guanlin4924/devflow-pi 0.1.15 → 0.1.17
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/package.json +1 -1
- package/plugins/devflow-core/extensions/devflow-core.ts +425 -54
- package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +3 -2
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_assessment.rb +458 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_brief.rb +256 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_constraints.rb +155 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_feasibility.rb +176 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +46 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +94 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +2 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/machine_evidence.rb +112 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +646 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/runtime_context_collector.rb +210 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/stale_check.rb +56 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/static_runtime_context_collector.rb +518 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +140 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/workload_constraint.rb +57 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +88 -2
- package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +3 -2
- package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +17 -1
- package/plugins/devflow-design/skills/workflow-architecture-feasibility/SKILL.md +37 -0
- package/plugins/devflow-design/skills/workflow-architecture-feasibility/agents/openai.yaml +4 -0
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +7 -2
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +6 -1
- package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +4 -0
- package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +4 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +4 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +4 -0
- package/src/cli.ts +2 -1
- package/src/runtime.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync } from "node:fs";
|
|
3
|
-
import { mkdir,
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
@@ -20,9 +20,17 @@ type ActiveExecutionContract = {
|
|
|
20
20
|
contractId: string;
|
|
21
21
|
feature: string;
|
|
22
22
|
step: string;
|
|
23
|
+
stateRevision?: number;
|
|
24
|
+
graphRevision?: number;
|
|
23
25
|
allowedPaths: string[];
|
|
24
26
|
allowedAgents: string[];
|
|
27
|
+
allowedTools: string[];
|
|
28
|
+
checkerIds: string[];
|
|
29
|
+
toolBudget: number;
|
|
30
|
+
frozenHash: string;
|
|
31
|
+
contractPath?: string;
|
|
25
32
|
acceptance: string[];
|
|
33
|
+
allowedGraphDelta: string[];
|
|
26
34
|
onUnknown: "block_and_escalate";
|
|
27
35
|
};
|
|
28
36
|
|
|
@@ -198,7 +206,7 @@ const CONTROL_PATHS = [
|
|
|
198
206
|
".planning/artifacts/index.yaml",
|
|
199
207
|
];
|
|
200
208
|
|
|
201
|
-
const CONTROL_PREFIXES = [".planning/workflows/", ".planning/contracts/"];
|
|
209
|
+
const CONTROL_PREFIXES = [".planning/workflows/", ".planning/contracts/", ".planning/evidence/"];
|
|
202
210
|
|
|
203
211
|
const REQUIRED_EXTENSION_TOOLS = [
|
|
204
212
|
"devflow_doctor",
|
|
@@ -206,6 +214,10 @@ const REQUIRED_EXTENSION_TOOLS = [
|
|
|
206
214
|
"devflow_bootstrap",
|
|
207
215
|
"devflow_transition",
|
|
208
216
|
"devflow_artifact",
|
|
217
|
+
"devflow_machine_evidence",
|
|
218
|
+
"devflow_requirement_graph",
|
|
219
|
+
"devflow_evidence",
|
|
220
|
+
"devflow_contract",
|
|
209
221
|
"devflow_recovery",
|
|
210
222
|
"devflow_skill_script",
|
|
211
223
|
"devflow_context_sync",
|
|
@@ -276,6 +288,9 @@ const REQUIRED_SCRIPTS = [
|
|
|
276
288
|
"workflow-orchestrator/scripts/inspect_state.rb",
|
|
277
289
|
"workflow-orchestrator/scripts/advance_state.rb",
|
|
278
290
|
"workflow-orchestrator/scripts/sync_artifacts.rb",
|
|
291
|
+
"workflow-orchestrator/scripts/requirement_graph.rb",
|
|
292
|
+
"workflow-orchestrator/scripts/machine_evidence.rb",
|
|
293
|
+
"workflow-orchestrator/scripts/execution_contract.rb",
|
|
279
294
|
"workflow-code-index/scripts/code_index.py",
|
|
280
295
|
"workflow-context-sync/scripts/context_sync_cursor.py",
|
|
281
296
|
];
|
|
@@ -380,6 +395,19 @@ function safeControlPath(cwd: string, raw: string | undefined, kind: "state" | "
|
|
|
380
395
|
return relative;
|
|
381
396
|
}
|
|
382
397
|
|
|
398
|
+
function isCanonicalRequirementGraphPath(filePath: string): boolean {
|
|
399
|
+
return /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:governance|evidence|definitions|behavior-graph|requirement-graph|slices|acceptance|traceability|graph\.meta)\.json$/u.test(filePath)
|
|
400
|
+
|| /^\.planning\/requirements\/[^/\\\x00-\x1F\x7F]+\/(?:context\/[^/\\]+\.json|control-events\.jsonl)$/u.test(filePath);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function isCoreOwnedGovernancePath(filePath: string): boolean {
|
|
404
|
+
// 规范需求图谱只能由 Core 的 devflow_requirement_graph writer 写入;the
|
|
405
|
+
// same ownership extends to its views, machine evidence, and audit records.
|
|
406
|
+
return isCanonicalRequirementGraphPath(filePath)
|
|
407
|
+
|| /^\.planning\/evidence\/[^/\\\x00-\x1F\x7F]+\/[^/\\]+\.machine\.json$/u.test(filePath)
|
|
408
|
+
|| /^\.planning\/contracts\/[^/\\\x00-\x1F\x7F]+\/(?:EC-[A-Fa-f0-9]+\.json|control-events\.jsonl)$/u.test(filePath);
|
|
409
|
+
}
|
|
410
|
+
|
|
383
411
|
function resolveIntelSkillRoot(cwd: string): string | undefined {
|
|
384
412
|
if (isEmbeddedRuntime()) {
|
|
385
413
|
const packaged = path.join(resolvePluginRoot(), "devflow-intel", "skills");
|
|
@@ -641,10 +669,10 @@ function isShellControlMutation(toolName: string, input: unknown): boolean {
|
|
|
641
669
|
const command = String((input as Record<string, unknown>).command ?? (input as Record<string, unknown>).cmd ?? "");
|
|
642
670
|
if (!command) return false;
|
|
643
671
|
const invokesWriter =
|
|
644
|
-
/workflow-orchestrator\/scripts\/(?:advance_state|init_state|init_workflow|sync_artifacts)\.rb[^\n]*--write/.test(
|
|
672
|
+
/workflow-orchestrator\/scripts\/(?:advance_state|init_state|init_workflow|sync_artifacts|requirement_graph|machine_evidence|execution_contract)\.rb[^\n]*--write/.test(
|
|
645
673
|
command,
|
|
646
674
|
);
|
|
647
|
-
const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item));
|
|
675
|
+
const namesControlPath = [...CONTROL_PATHS, ...CONTROL_PREFIXES].some(item => command.includes(item)) || /\.planning\/requirements\//.test(command);
|
|
648
676
|
const looksLikeWrite = /(?:^|[;&|]\s*)(?:rm|mv|cp|install|truncate)\b|(?:^|\s)(?:tee|sed\s+-i|perl\s+-i)\b|>{1,2}/.test(
|
|
649
677
|
command,
|
|
650
678
|
);
|
|
@@ -668,6 +696,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
668
696
|
let sessionId = randomUUID();
|
|
669
697
|
let controlState: SessionControlState = initialControlState();
|
|
670
698
|
let activeContract: ActiveExecutionContract | undefined;
|
|
699
|
+
let contractAgentCalls = 0;
|
|
671
700
|
|
|
672
701
|
function setControlState(ctx: { ui: { setStatus(key: string, text: string | undefined): void } }, state: SessionControlState) {
|
|
673
702
|
controlState = state;
|
|
@@ -686,18 +715,14 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
686
715
|
return contract.allowedPaths.some(root => filePath === root || filePath.startsWith(`${root}/`));
|
|
687
716
|
}
|
|
688
717
|
|
|
689
|
-
async function
|
|
690
|
-
|
|
691
|
-
const
|
|
692
|
-
const
|
|
693
|
-
const
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const temporary = `${destination}.${randomUUID()}.tmp`;
|
|
698
|
-
await writeFile(temporary, payload, "utf8");
|
|
699
|
-
await rename(temporary, destination);
|
|
700
|
-
return path.relative(cwd, destination).replaceAll("\\", "/");
|
|
718
|
+
async function contractIsFresh(cwd: string, contract: ActiveExecutionContract): Promise<boolean> {
|
|
719
|
+
const state = parseJsonResult(await runRuby(cwd, "workflow-orchestrator/scripts/inspect_state.rb", ["--json"]));
|
|
720
|
+
const stateRevision = typeof state?.revision === "number" ? state.revision : undefined;
|
|
721
|
+
const graphResult = parseJsonResult(await runRuby(cwd, "workflow-orchestrator/scripts/requirement_graph.rb", ["inspect", "--feature", contract.feature, "--json"]));
|
|
722
|
+
const graph = graphResult?.graph;
|
|
723
|
+
const graphRevision = graph && typeof graph === "object" && typeof (graph as Record<string, unknown>).revision === "number"
|
|
724
|
+
? (graph as Record<string, number>).revision : undefined;
|
|
725
|
+
return stateRevision === contract.stateRevision && graphRevision === contract.graphRevision;
|
|
701
726
|
}
|
|
702
727
|
|
|
703
728
|
async function collectProjectInitialization(ctx: {
|
|
@@ -1079,6 +1104,14 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1079
1104
|
|
|
1080
1105
|
pi.on("tool_call", async (event, ctx) => {
|
|
1081
1106
|
const toolName = String(event.toolName).toLowerCase();
|
|
1107
|
+
if (activeContract && controlState.phase === "executing" && !toolName.startsWith("devflow_")) {
|
|
1108
|
+
if (!activeContract.allowedTools.includes(toolName)) {
|
|
1109
|
+
return { block: true, reason: `Active contract ${activeContract.contractId} does not allow tool: ${toolName}` };
|
|
1110
|
+
}
|
|
1111
|
+
if (!(await contractIsFresh(ctx.cwd, activeContract))) {
|
|
1112
|
+
return { block: true, reason: `Active contract ${activeContract.contractId} is stale against STATE or graph revision; synthesize a new contract before continuing.` };
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1082
1115
|
if (toolName === "task") {
|
|
1083
1116
|
const requested = requestedTaskAgents(event.input);
|
|
1084
1117
|
if (requested.missing) {
|
|
@@ -1109,8 +1142,12 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1109
1142
|
controlState.phase === "executing" &&
|
|
1110
1143
|
!requested.names.every(agent => activeContract?.allowedAgents.includes(agent))
|
|
1111
1144
|
) {
|
|
1112
|
-
|
|
1145
|
+
return { block: true, reason: `Active contract ${activeContract.contractId} only allows agents: ${activeContract.allowedAgents.join(", ")}` };
|
|
1146
|
+
}
|
|
1147
|
+
if (activeContract && controlState.phase === "executing" && contractAgentCalls >= activeContract.toolBudget) {
|
|
1148
|
+
return { block: true, reason: `Active contract ${activeContract.contractId} exhausted its fixed agent budget (${activeContract.toolBudget}).` };
|
|
1113
1149
|
}
|
|
1150
|
+
if (activeContract && controlState.phase === "executing") contractAgentCalls += 1;
|
|
1114
1151
|
} else if (
|
|
1115
1152
|
controlState.phase === "planning" &&
|
|
1116
1153
|
toolName !== "devflow_contract" &&
|
|
@@ -1129,6 +1166,16 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1129
1166
|
"Devflow 状态迁移只能由运行时在用户明确批准已展示的步骤或 checkpoint 后执行;LLM 不得自行推进。",
|
|
1130
1167
|
};
|
|
1131
1168
|
}
|
|
1169
|
+
if (
|
|
1170
|
+
!toolName.startsWith("devflow_") &&
|
|
1171
|
+
["write", "edit", "apply_patch", "notebook_edit", "bash", "shell", "exec_command"].includes(toolName) &&
|
|
1172
|
+
inputPaths(event.input, ctx.cwd).some(isCoreOwnedGovernancePath)
|
|
1173
|
+
) {
|
|
1174
|
+
return {
|
|
1175
|
+
block: true,
|
|
1176
|
+
reason: "规范需求图谱、派生视图、机器 Evidence 与合同审计只能由 Core writer 写入;Skill 只能提交候选 delta 或 agent_claim。",
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1132
1179
|
if (toolName.startsWith("devflow_")) return;
|
|
1133
1180
|
if (
|
|
1134
1181
|
activeContract &&
|
|
@@ -1234,6 +1281,319 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1234
1281
|
}
|
|
1235
1282
|
});
|
|
1236
1283
|
|
|
1284
|
+
pi.registerTool({
|
|
1285
|
+
name: "devflow_machine_evidence",
|
|
1286
|
+
label: "Devflow Machine Evidence",
|
|
1287
|
+
description: "Collect Core-owned evidence from the current workflow step's actual required outputs and graph revision.",
|
|
1288
|
+
parameters: z.object({ feature: z.string().min(1), step: z.string().min(1) }),
|
|
1289
|
+
approval: "write",
|
|
1290
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1291
|
+
return renderResult(
|
|
1292
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/machine_evidence.rb", ["--feature", params.feature, "--step", params.step, "--write"]),
|
|
1293
|
+
{ feature: params.feature, step: params.step },
|
|
1294
|
+
);
|
|
1295
|
+
},
|
|
1296
|
+
});
|
|
1297
|
+
|
|
1298
|
+
pi.registerTool({
|
|
1299
|
+
name: "devflow_requirement_graph",
|
|
1300
|
+
label: "Devflow Requirement Graph",
|
|
1301
|
+
description: "Submit bounded requirement-graph candidates or inspect/validate the Core-owned canonical graph.",
|
|
1302
|
+
parameters: z.object({
|
|
1303
|
+
action: z.enum(["propose", "inspect", "validate", "context", "confirm"]),
|
|
1304
|
+
feature: z.string().min(1),
|
|
1305
|
+
expectedRevision: z.number().int().min(0).optional(),
|
|
1306
|
+
sliceId: z.string().min(1).optional(),
|
|
1307
|
+
nodeId: z.string().min(1).optional(),
|
|
1308
|
+
evidence: z.object({ cp_record: z.unknown().optional(), evidence_ref: z.unknown().optional() }).optional(),
|
|
1309
|
+
write: z.boolean().optional(),
|
|
1310
|
+
read: z.boolean().optional(),
|
|
1311
|
+
delta: z.object({
|
|
1312
|
+
op: z.enum(["add", "link", "update"]),
|
|
1313
|
+
kind: z.enum(["source", "code", "term", "object", "behavior", "requirement", "rule", "boundary", "slice", "risk", "decision", "acceptance", "api", "workload_shape", "workload_constraint"]).optional(),
|
|
1314
|
+
id: z.string().min(1).optional(),
|
|
1315
|
+
data: z.record(z.string(), z.unknown()).optional(),
|
|
1316
|
+
from: z.string().min(1).optional(),
|
|
1317
|
+
to: z.string().min(1).optional(),
|
|
1318
|
+
relation: z.string().min(1).optional(),
|
|
1319
|
+
propagation: z.enum(["one_hop", "full"]).optional(),
|
|
1320
|
+
}).optional(),
|
|
1321
|
+
}),
|
|
1322
|
+
approval: (args) => args.action === "propose" || args.action === "confirm" ? "write" : "read",
|
|
1323
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1324
|
+
const args = [params.action, "--feature", params.feature, "--json"];
|
|
1325
|
+
if (params.action === "context") {
|
|
1326
|
+
if (!params.sliceId) return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "context requires sliceId" });
|
|
1327
|
+
args.push("--slice-id", params.sliceId);
|
|
1328
|
+
if (params.write) args.push("--write");
|
|
1329
|
+
if (params.read) args.push("--read");
|
|
1330
|
+
}
|
|
1331
|
+
if (params.action === "propose") {
|
|
1332
|
+
if (params.expectedRevision === undefined || !params.delta) {
|
|
1333
|
+
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "propose requires expectedRevision and delta" });
|
|
1334
|
+
}
|
|
1335
|
+
args.push("--write", "--expected-revision", String(params.expectedRevision), "--delta-json", JSON.stringify(params.delta));
|
|
1336
|
+
if (activeContract?.allowedGraphDelta?.length) args.push("--allowed-kinds", activeContract.allowedGraphDelta.join(","));
|
|
1337
|
+
}
|
|
1338
|
+
if (params.action === "confirm") {
|
|
1339
|
+
if (params.expectedRevision === undefined || !params.nodeId || !params.evidence) {
|
|
1340
|
+
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "confirm requires expectedRevision, nodeId and evidence" });
|
|
1341
|
+
}
|
|
1342
|
+
args.push("--write", "--expected-revision", String(params.expectedRevision), "--node-id", params.nodeId, "--evidence-json", JSON.stringify(params.evidence));
|
|
1343
|
+
const inspected = parseJsonResult(
|
|
1344
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/requirement_graph.rb", ["inspect", "--feature", params.feature, "--json"]),
|
|
1345
|
+
);
|
|
1346
|
+
const node = Array.isArray(inspected?.graph?.nodes)
|
|
1347
|
+
? inspected.graph.nodes.find((candidate: unknown) => typeof candidate === "object" && candidate !== null && (candidate as { id?: unknown }).id === params.nodeId) as { kind?: unknown; data?: unknown } | undefined
|
|
1348
|
+
: undefined;
|
|
1349
|
+
const architectureDecision = node?.kind === "decision" && typeof node?.data === "object" && node.data !== null && (node.data as { decision_type?: unknown }).decision_type === "architecture_option_comparison";
|
|
1350
|
+
const architectureRisk = node?.kind === "risk" && typeof node?.data === "object" && node.data !== null && (node.data as { risk_type?: unknown }).risk_type === "architecture_option_risk";
|
|
1351
|
+
if (node?.kind === "workload_shape" || node?.kind === "workload_constraint" || architectureDecision || architectureRisk) {
|
|
1352
|
+
const preview = JSON.stringify(node.data ?? {}, null, 2);
|
|
1353
|
+
const decisionDialog = architectureDecision;
|
|
1354
|
+
const riskDialog = architectureRisk;
|
|
1355
|
+
const confirmed = await ctx.ui.confirm(
|
|
1356
|
+
decisionDialog ? "确认架构方案取舍" : riskDialog ? "确认架构风险处理" : "确认工作负载事实或缺口",
|
|
1357
|
+
[
|
|
1358
|
+
`Feature:${params.feature}`,
|
|
1359
|
+
`节点:${params.nodeId}`,
|
|
1360
|
+
`类型:${decisionDialog ? "架构方案比较" : riskDialog ? "架构风险处理" : node.kind === "workload_shape" ? "工作负载形态" : "工作负载约束"}`,
|
|
1361
|
+
decisionDialog
|
|
1362
|
+
? "确认的含义是:你已核对候选方案、证据与取舍,并接受其中 recommendation_option_id 作为本次架构选择。"
|
|
1363
|
+
: riskDialog
|
|
1364
|
+
? "确认的含义是:你已核对失败模式、影响、处理方式、负责人和复核条件;它不代表生产风险从此消失。"
|
|
1365
|
+
: "确认的含义仅是:该事实或缺口、来源与责任归属已被你核对;它不代表容量已经满足,也不会替你选择实现方案。",
|
|
1366
|
+
"待确认内容:",
|
|
1367
|
+
preview.length > 8_000 ? `${preview.slice(0, 8_000)}\n…(内容过长,已截断)` : preview,
|
|
1368
|
+
].join("\n\n"),
|
|
1369
|
+
);
|
|
1370
|
+
if (!confirmed) return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "User declined workload confirmation; node remains candidate." });
|
|
1371
|
+
args.push("--confirmed-interactively");
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return renderResult(
|
|
1375
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/requirement_graph.rb", args),
|
|
1376
|
+
{ action: params.action, feature: params.feature },
|
|
1377
|
+
);
|
|
1378
|
+
},
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
pi.registerTool({
|
|
1382
|
+
name: "devflow_runtime_context",
|
|
1383
|
+
label: "Devflow Runtime Context",
|
|
1384
|
+
description: "Collect or inspect the P0 repository-declared dependency snapshot and its coverage.",
|
|
1385
|
+
parameters: z.object({
|
|
1386
|
+
action: z.enum(["collect", "inspect"]),
|
|
1387
|
+
}),
|
|
1388
|
+
approval: (args) => args.action === "collect" ? "write" : "read",
|
|
1389
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1390
|
+
return renderResult(
|
|
1391
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/runtime_context_collector.rb", [params.action, "--json"]),
|
|
1392
|
+
{ action: params.action },
|
|
1393
|
+
);
|
|
1394
|
+
},
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
pi.registerTool({
|
|
1398
|
+
name: "devflow_static_runtime_context",
|
|
1399
|
+
label: "Devflow Static Runtime Context",
|
|
1400
|
+
description: "Collect or inspect the P1 static evidence snapshot. It records only repository declarations and references, never production health or capacity.",
|
|
1401
|
+
parameters: z.object({
|
|
1402
|
+
action: z.enum(["collect", "inspect"]),
|
|
1403
|
+
}),
|
|
1404
|
+
approval: (args) => args.action === "collect" ? "write" : "read",
|
|
1405
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1406
|
+
return renderResult(
|
|
1407
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/static_runtime_context_collector.rb", [params.action, "--json"]),
|
|
1408
|
+
{ action: params.action },
|
|
1409
|
+
);
|
|
1410
|
+
},
|
|
1411
|
+
});
|
|
1412
|
+
|
|
1413
|
+
pi.registerTool({
|
|
1414
|
+
name: "devflow_architecture_assessment",
|
|
1415
|
+
label: "Devflow Architecture Assessment",
|
|
1416
|
+
description: "Confirm an immutable delta set through an explicit user dialog, then assess only that Core-owned manifest against a pinned runtime-context snapshot.",
|
|
1417
|
+
parameters: z.object({
|
|
1418
|
+
action: z.enum(["inspect", "assess", "write-manifest"]),
|
|
1419
|
+
feature: z.string().min(1),
|
|
1420
|
+
baseSnapshotRevision: z.number().int().min(0).optional(),
|
|
1421
|
+
deltaSetId: z.string().min(1).optional(),
|
|
1422
|
+
deltaJson: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
1423
|
+
sourceBundleRevision: z.string().min(1).optional(),
|
|
1424
|
+
expectedAssessmentRevision: z.number().int().min(0).optional(),
|
|
1425
|
+
write: z.boolean().optional(),
|
|
1426
|
+
}),
|
|
1427
|
+
approval: (args) => args.action === "write-manifest" || args.write ? "write" : "read",
|
|
1428
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1429
|
+
const args = [params.action, "--feature", params.feature, "--json"];
|
|
1430
|
+
if (params.action === "inspect") {
|
|
1431
|
+
if (params.deltaSetId) args.push("--delta-set-id", params.deltaSetId);
|
|
1432
|
+
return renderResult(
|
|
1433
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_assessment.rb", args),
|
|
1434
|
+
{ action: params.action, feature: params.feature },
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
if (!params.deltaSetId) {
|
|
1438
|
+
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "deltaSetId is required" });
|
|
1439
|
+
}
|
|
1440
|
+
args.push("--delta-set-id", params.deltaSetId);
|
|
1441
|
+
if (params.action === "assess") {
|
|
1442
|
+
if (params.baseSnapshotRevision === undefined) {
|
|
1443
|
+
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "assess requires baseSnapshotRevision and deltaSetId; deltas and manifest are Core-owned" });
|
|
1444
|
+
}
|
|
1445
|
+
args.push("--base-snapshot-revision", String(params.baseSnapshotRevision));
|
|
1446
|
+
if (params.write) {
|
|
1447
|
+
const inspected = parseJsonResult(await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_assessment.rb", ["inspect", "--feature", params.feature, "--delta-set-id", params.deltaSetId, "--json"]));
|
|
1448
|
+
const currentRevision = typeof inspected?.current_assessment_revision === "number" ? inspected.current_assessment_revision : undefined;
|
|
1449
|
+
if (currentRevision === undefined) {
|
|
1450
|
+
return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "Unable to read the current assessment revision for CAS." });
|
|
1451
|
+
}
|
|
1452
|
+
args.push("--write", "--expected-assessment-revision", String(currentRevision));
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
if (params.action === "write-manifest") {
|
|
1456
|
+
if (!params.sourceBundleRevision || !params.deltaJson?.length) {
|
|
1457
|
+
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "write-manifest requires deltaSetId, sourceBundleRevision and a non-empty deltaJson" });
|
|
1458
|
+
}
|
|
1459
|
+
const preview = params.deltaJson.map((rawDelta, index) => {
|
|
1460
|
+
const delta = rawDelta as Record<string, unknown>;
|
|
1461
|
+
const source = delta.source && typeof delta.source === "object" ? delta.source as Record<string, unknown> : {};
|
|
1462
|
+
return [
|
|
1463
|
+
`${index + 1}. ID:${String(delta.id ?? "(缺失)")}`,
|
|
1464
|
+
`依赖:${String(delta.subject ?? "(缺失)")}`,
|
|
1465
|
+
`来源:${String(source.locator ?? "(缺失)")}`,
|
|
1466
|
+
`来源 revision:${String(source.repository_revision ?? "(缺失)")}`,
|
|
1467
|
+
`内容哈希:${String(source.input_hash ?? "(缺失)")}`,
|
|
1468
|
+
].join("\n");
|
|
1469
|
+
}).join("\n\n");
|
|
1470
|
+
const confirmed = await ctx.ui.confirm(
|
|
1471
|
+
"确认架构评估 Delta 集合",
|
|
1472
|
+
[
|
|
1473
|
+
`Feature:${params.feature}`,
|
|
1474
|
+
`Delta 集合:${params.deltaSetId}`,
|
|
1475
|
+
`需求/方案 revision:${params.sourceBundleRevision}`,
|
|
1476
|
+
"确认后 Core 会创建不可覆盖的 manifest;后续评估只能读取这一份确认内容。",
|
|
1477
|
+
"请核对以下依赖、来源与哈希:",
|
|
1478
|
+
preview,
|
|
1479
|
+
].join("\n\n"),
|
|
1480
|
+
);
|
|
1481
|
+
if (!confirmed) return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "User declined delta-set confirmation; no manifest was written." });
|
|
1482
|
+
args.push("--source-bundle-revision", params.sourceBundleRevision, "--delta-json", JSON.stringify(params.deltaJson), "--confirmed-interactively", "--write");
|
|
1483
|
+
}
|
|
1484
|
+
return renderResult(
|
|
1485
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_assessment.rb", args),
|
|
1486
|
+
{ action: params.action, feature: params.feature },
|
|
1487
|
+
);
|
|
1488
|
+
},
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
pi.registerTool({
|
|
1492
|
+
name: "devflow_workload_constraint",
|
|
1493
|
+
label: "Devflow Workload Constraint",
|
|
1494
|
+
description: "Validate a workload constraint (NFR) against its source-status schema.",
|
|
1495
|
+
parameters: z.object({
|
|
1496
|
+
constraintJson: z.record(z.string(), z.unknown()),
|
|
1497
|
+
}),
|
|
1498
|
+
approval: "read",
|
|
1499
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1500
|
+
return renderResult(
|
|
1501
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/workload_constraint.rb", ["validate", "--constraint-json", JSON.stringify(params.constraintJson), "--json"]),
|
|
1502
|
+
{},
|
|
1503
|
+
);
|
|
1504
|
+
},
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
pi.registerTool({
|
|
1508
|
+
name: "devflow_architecture_constraint",
|
|
1509
|
+
label: "Devflow Architecture Constraint",
|
|
1510
|
+
description: "Validate candidate workload shape or workload constraint inputs without assigning confirmation, decisions, or gate status.",
|
|
1511
|
+
parameters: z.object({
|
|
1512
|
+
action: z.enum(["validate-shape", "validate-constraint"]),
|
|
1513
|
+
shapeJson: z.record(z.string(), z.unknown()).optional(),
|
|
1514
|
+
constraintJson: z.record(z.string(), z.unknown()).optional(),
|
|
1515
|
+
}),
|
|
1516
|
+
approval: "read",
|
|
1517
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1518
|
+
if (params.action === "validate-shape" && !params.shapeJson) {
|
|
1519
|
+
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "validate-shape requires shapeJson" });
|
|
1520
|
+
}
|
|
1521
|
+
if (params.action === "validate-constraint" && !params.constraintJson) {
|
|
1522
|
+
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "validate-constraint requires constraintJson" });
|
|
1523
|
+
}
|
|
1524
|
+
const args = [params.action, "--json"];
|
|
1525
|
+
if (params.shapeJson) args.push("--shape-json", JSON.stringify(params.shapeJson));
|
|
1526
|
+
if (params.constraintJson) args.push("--constraint-json", JSON.stringify(params.constraintJson));
|
|
1527
|
+
return renderResult(
|
|
1528
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_constraints.rb", args),
|
|
1529
|
+
{ action: params.action },
|
|
1530
|
+
);
|
|
1531
|
+
},
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
pi.registerTool({
|
|
1535
|
+
name: "devflow_architecture_feasibility",
|
|
1536
|
+
label: "Devflow Architecture Feasibility",
|
|
1537
|
+
description: "Read confirmed workload facts and a pinned static-context snapshot to derive an advisory architecture assessment. It never selects an option or claims production capacity.",
|
|
1538
|
+
parameters: z.object({
|
|
1539
|
+
feature: z.string().min(1),
|
|
1540
|
+
}),
|
|
1541
|
+
approval: "read",
|
|
1542
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1543
|
+
return renderResult(
|
|
1544
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_feasibility.rb", ["assess", "--feature", params.feature, "--json"]),
|
|
1545
|
+
{ feature: params.feature },
|
|
1546
|
+
);
|
|
1547
|
+
},
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
pi.registerTool({
|
|
1551
|
+
name: "devflow_architecture_brief",
|
|
1552
|
+
label: "Devflow Architecture Brief",
|
|
1553
|
+
description: "Inspect or regenerate the Core-derived architecture brief from the current graph and static-context snapshot. The brief is a view, never an agent-authored source of truth.",
|
|
1554
|
+
parameters: z.object({
|
|
1555
|
+
action: z.enum(["inspect", "render"]),
|
|
1556
|
+
feature: z.string().min(1),
|
|
1557
|
+
}),
|
|
1558
|
+
approval: (args) => args.action === "render" ? "write" : "read",
|
|
1559
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1560
|
+
if (params.action === "inspect") {
|
|
1561
|
+
return renderResult(
|
|
1562
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_brief.rb", ["inspect", "--feature", params.feature, "--json"]),
|
|
1563
|
+
{ action: params.action, feature: params.feature },
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
const inspected = parseJsonResult(
|
|
1567
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_brief.rb", ["inspect", "--feature", params.feature, "--json"]),
|
|
1568
|
+
);
|
|
1569
|
+
const currentRevision = typeof inspected?.brief_revision === "number" ? inspected.brief_revision : undefined;
|
|
1570
|
+
if (currentRevision === undefined) {
|
|
1571
|
+
return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "Unable to read the current architecture brief revision for CAS." });
|
|
1572
|
+
}
|
|
1573
|
+
return renderResult(
|
|
1574
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_brief.rb", ["render", "--feature", params.feature, "--write", "--expected-brief-revision", String(currentRevision), "--json"]),
|
|
1575
|
+
{ action: params.action, feature: params.feature },
|
|
1576
|
+
);
|
|
1577
|
+
},
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
pi.registerTool({
|
|
1581
|
+
name: "devflow_stale_check",
|
|
1582
|
+
label: "Devflow Stale Check",
|
|
1583
|
+
description: "Check field-level staleness of consumed evidence against the current snapshot.",
|
|
1584
|
+
parameters: z.object({
|
|
1585
|
+
consumedRevision: z.number().int().min(0),
|
|
1586
|
+
evidencePaths: z.array(z.string().min(1)),
|
|
1587
|
+
}),
|
|
1588
|
+
approval: "read",
|
|
1589
|
+
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1590
|
+
return renderResult(
|
|
1591
|
+
await runRuby(ctx.cwd, "workflow-orchestrator/scripts/stale_check.rb", ["check", "--consumed-revision", String(params.consumedRevision), "--evidence-paths", JSON.stringify(params.evidencePaths), "--json"]),
|
|
1592
|
+
{},
|
|
1593
|
+
);
|
|
1594
|
+
},
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1237
1597
|
pi.registerTool({
|
|
1238
1598
|
name: "devflow_evidence",
|
|
1239
1599
|
label: "Devflow Evidence Pack",
|
|
@@ -1254,7 +1614,10 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1254
1614
|
if (!pack || typeof pack !== "object" || Array.isArray(pack)) return ["pack must be an object"];
|
|
1255
1615
|
const record = pack as Record<string, unknown>;
|
|
1256
1616
|
const errors: string[] = [];
|
|
1257
|
-
|
|
1617
|
+
const legacySchema = record.schemaVersion === 1;
|
|
1618
|
+
if (record.schemaVersion !== 2 && record.schemaVersion !== 1) errors.push("schemaVersion must equal 2 (or 1 during compatibility period)");
|
|
1619
|
+
if (record.origin !== "agent_claim") errors.push("origin must equal agent_claim");
|
|
1620
|
+
if (Object.prototype.hasOwnProperty.call(record, "passed")) errors.push("agent claims cannot declare passed");
|
|
1258
1621
|
for (const key of ["facts", "unknowns", "risks"]) {
|
|
1259
1622
|
if (!Array.isArray(record[key])) errors.push(`${key} must be an array`);
|
|
1260
1623
|
}
|
|
@@ -1272,7 +1635,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1272
1635
|
return { content: [{ type: "text" as const, text: JSON.stringify(parsed, null, 2) }], details: { action: params.action, path: path.relative(ctx.cwd, destination), errors } };
|
|
1273
1636
|
}
|
|
1274
1637
|
if (!params.pack) return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "pack is required for create" }, { action: params.action });
|
|
1275
|
-
const pack = { schemaVersion:
|
|
1638
|
+
const pack = { schemaVersion: 2, taskId, createdAt: new Date().toISOString(), ...params.pack, origin: "agent_claim", evidenceClass: "non_gating_claim" };
|
|
1276
1639
|
const errors = validatePack(pack);
|
|
1277
1640
|
if (errors.length > 0) return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: errors.join("; ") }, { action: params.action, errors });
|
|
1278
1641
|
await mkdir(root, { recursive: true });
|
|
@@ -1284,26 +1647,19 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1284
1647
|
pi.registerTool({
|
|
1285
1648
|
name: "devflow_contract",
|
|
1286
1649
|
label: "Devflow Execution Contract",
|
|
1287
|
-
description: "
|
|
1650
|
+
description: "Inspect or activate a Core-synthesized ExecutionContract v2. Paths, agents, checker IDs, and budgets are fixed by the packaged policy, never model input.",
|
|
1288
1651
|
parameters: z.object({
|
|
1289
|
-
action: z.enum(["status", "activate"]),
|
|
1290
|
-
contractId: z.string().min(1).optional(),
|
|
1652
|
+
action: z.enum(["status", "inspect", "activate"]),
|
|
1291
1653
|
feature: z.string().min(1).optional(),
|
|
1292
1654
|
step: z.string().min(1).optional(),
|
|
1293
|
-
allowedPaths: z.array(z.string().min(1)).default([]),
|
|
1294
|
-
allowedAgents: z.array(z.enum(["implementation-worker", "verification-runner"])).default([]),
|
|
1295
|
-
acceptance: z.array(z.string().min(1)).default([]),
|
|
1296
1655
|
}),
|
|
1297
1656
|
approval: "write",
|
|
1298
1657
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
1299
1658
|
const input = params as {
|
|
1300
|
-
action: "status" | "activate";
|
|
1659
|
+
action: "status" | "inspect" | "activate";
|
|
1301
1660
|
contractId?: string;
|
|
1302
1661
|
feature?: string;
|
|
1303
1662
|
step?: string;
|
|
1304
|
-
allowedPaths: string[];
|
|
1305
|
-
allowedAgents: string[];
|
|
1306
|
-
acceptance: string[];
|
|
1307
1663
|
};
|
|
1308
1664
|
if (input.action === "status") {
|
|
1309
1665
|
return {
|
|
@@ -1311,47 +1667,51 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1311
1667
|
details: { activeContract },
|
|
1312
1668
|
};
|
|
1313
1669
|
}
|
|
1314
|
-
if (
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
try {
|
|
1322
|
-
allowedPaths = input.allowedPaths.map(candidate => {
|
|
1323
|
-
const relative = normalizedProjectPath(candidate, ctx.cwd);
|
|
1324
|
-
if (!relative || relative.startsWith(".planning/")) throw new Error(`Invalid contract path: ${candidate}`);
|
|
1325
|
-
return relative.replace(/\/$/, "");
|
|
1326
|
-
});
|
|
1327
|
-
} catch (error) {
|
|
1328
|
-
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: error instanceof Error ? error.message : String(error) });
|
|
1329
|
-
}
|
|
1670
|
+
if (!input.feature || !input.step) return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "feature and step are required; Core derives all control fields." });
|
|
1671
|
+
const inspected = await runRuby(ctx.cwd, "workflow-orchestrator/scripts/execution_contract.rb", ["inspect", "--feature", input.feature, "--step", input.step, "--json"]);
|
|
1672
|
+
const payload = parseJsonResult(inspected);
|
|
1673
|
+
const raw = payload?.contract;
|
|
1674
|
+
if (!controlPlaneSucceeded(inspected) || !raw || typeof raw !== "object") return renderResult(inspected, { action: input.action });
|
|
1675
|
+
const record = raw as Record<string, unknown>;
|
|
1676
|
+
const strings = (key: string): string[] => Array.isArray(record[key]) ? record[key].filter((item): item is string => typeof item === "string") : [];
|
|
1330
1677
|
const contract: ActiveExecutionContract = {
|
|
1331
|
-
contractId:
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
allowedPaths:
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
onUnknown: "block_and_escalate",
|
|
1678
|
+
contractId: String(record.contract_id ?? ""), feature: String(record.feature ?? ""), step: String(record.step ?? ""),
|
|
1679
|
+
stateRevision: typeof record.state_revision === "number" ? record.state_revision : undefined,
|
|
1680
|
+
graphRevision: typeof record.graph_revision === "number" ? record.graph_revision : undefined,
|
|
1681
|
+
allowedPaths: strings("allowed_paths"), allowedAgents: strings("allowed_agents"), allowedTools: strings("allowed_tools"), checkerIds: strings("checker_ids"),
|
|
1682
|
+
toolBudget: typeof record.tool_budget === "number" ? record.tool_budget : 0,
|
|
1683
|
+
frozenHash: String(record.frozen_hash ?? ""), acceptance: strings("acceptance"), allowedGraphDelta: strings("allowed_graph_delta"), onUnknown: "block_and_escalate",
|
|
1338
1684
|
};
|
|
1685
|
+
if (!contract.contractId || !contract.frozenHash) return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "Core returned an invalid execution contract." });
|
|
1686
|
+
if (input.action === "inspect") return { content: [{ type: "text" as const, text: JSON.stringify(contract, null, 2) }], details: { contract } };
|
|
1687
|
+
if (controlState.phase !== "planning") return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "Contracts may only be activated from the planning phase." });
|
|
1339
1688
|
const confirmed = await ctx.ui.confirm(
|
|
1340
1689
|
"激活 Devflow 执行合同",
|
|
1341
1690
|
[
|
|
1342
1691
|
`合同:${contract.contractId}`,
|
|
1343
1692
|
`Feature:${contract.feature}`,
|
|
1344
1693
|
`步骤:${contract.step}`,
|
|
1694
|
+
`STATE revision:${contract.stateRevision ?? "未初始化"}`,
|
|
1695
|
+
`图谱 revision:${contract.graphRevision ?? 0}`,
|
|
1345
1696
|
`可写路径:${contract.allowedPaths.join(", ")}`,
|
|
1346
1697
|
`允许 Agent:${contract.allowedAgents.join(", ")}`,
|
|
1347
|
-
|
|
1698
|
+
`固定检查器:${contract.checkerIds.join(", ") || "无"}`,
|
|
1699
|
+
`冻结哈希:${contract.frozenHash}`,
|
|
1348
1700
|
"确认后低成本模型只能在以上范围执行;未知项必须阻塞升级。",
|
|
1349
1701
|
].join("\n"),
|
|
1350
1702
|
);
|
|
1351
1703
|
if (!confirmed) return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "User declined execution contract activation." });
|
|
1352
1704
|
try {
|
|
1353
|
-
const
|
|
1705
|
+
const createArgs = ["create", "--feature", input.feature, "--step", input.step, "--write", "--json"];
|
|
1706
|
+
if (contract.stateRevision !== undefined) createArgs.push("--expected-state-revision", String(contract.stateRevision));
|
|
1707
|
+
if (contract.graphRevision !== undefined) createArgs.push("--expected-graph-revision", String(contract.graphRevision));
|
|
1708
|
+
const written = await runRuby(ctx.cwd, "workflow-orchestrator/scripts/execution_contract.rb", createArgs);
|
|
1709
|
+
if (!controlPlaneSucceeded(written)) return renderResult(written, { action: input.action });
|
|
1710
|
+
const writtenPayload = parseJsonResult(written);
|
|
1711
|
+
const destination = typeof writtenPayload?.path === "string" ? writtenPayload.path : ".planning/contracts/";
|
|
1712
|
+
contract.contractPath = destination;
|
|
1354
1713
|
activeContract = contract;
|
|
1714
|
+
contractAgentCalls = 0;
|
|
1355
1715
|
setControlState(ctx, executingState(`合同执行:${contract.contractId}`));
|
|
1356
1716
|
return {
|
|
1357
1717
|
content: [{ type: "text" as const, text: `Execution contract activated: ${destination}` }],
|
|
@@ -1483,6 +1843,17 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1483
1843
|
if (!params.step) {
|
|
1484
1844
|
return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "step is required" });
|
|
1485
1845
|
}
|
|
1846
|
+
const snapshot = parseJsonResult(await runRuby(ctx.cwd, "workflow-orchestrator/scripts/inspect_state.rb", ["--json"]));
|
|
1847
|
+
const feature = snapshot?.snapshot && typeof snapshot.snapshot === "object" ? (snapshot.snapshot as Record<string, unknown>).feature : undefined;
|
|
1848
|
+
if (typeof feature === "string" && feature) {
|
|
1849
|
+
const evidenceArgs = ["--feature", feature, "--step", params.step, "--write"];
|
|
1850
|
+
if (activeContract?.feature === feature && activeContract.step === params.step && activeContract.contractPath) evidenceArgs.push("--contract", activeContract.contractPath);
|
|
1851
|
+
const evidence = await runRuby(ctx.cwd, "workflow-orchestrator/scripts/machine_evidence.rb", evidenceArgs);
|
|
1852
|
+
const evidencePayload = parseJsonResult(evidence);
|
|
1853
|
+
if (!controlPlaneSucceeded(evidence) || evidencePayload?.complete !== true) {
|
|
1854
|
+
return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "machine evidence collection failed or required outputs are incomplete" }, { action: params.action, evidence: evidence.stdout });
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1486
1857
|
args.push("--complete-step", params.step);
|
|
1487
1858
|
} else if (params.action === "confirm-checkpoint") {
|
|
1488
1859
|
args.push("--confirm-checkpoint");
|