cli-blueprint 7.0.33 → 7.0.34

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.
@@ -0,0 +1,137 @@
1
+ import { createHash } from "node:crypto";
2
+ import { BLUEPRINT_OPERATION_INPUTS, blueprintSchemaFindings } from "./blueprint-schema.mjs";
3
+ import { buildBlueprintMachineTasks } from "./blueprint-task-graph.mjs";
4
+
5
+ const BP_RESPONSE_SCHEMA = "blueprint.skill.response/1.0";
6
+ const BP_REQUEST_SCHEMA = "blueprint.skill.request/1.0";
7
+ const BP_MANIFEST = "ARTIFACT-MANIFEST.json";
8
+ const BP_IR_ARTIFACT = "PROJECT-BLUEPRINT.json";
9
+ const BP_REPORT_MODE = "caller-supplied-test-evidence";
10
+ const blueprintDigest = (value) => createHash("sha256").update(value).digest("hex");
11
+ const blueprintSummary = (findings) => ({
12
+ p0: findings.filter((item) => item.severity === "P0").length,
13
+ p1: findings.filter((item) => item.severity === "P1").length,
14
+ p2: findings.filter((item) => item.severity === "P2").length,
15
+ p3: findings.filter((item) => item.severity === "P3").length, total: findings.length,
16
+ });
17
+ function bridgeFinding(ruleId, entityRef, message, evidence = {}) {
18
+ return { ruleId, severity: "P0", entityRef, message, evidence,
19
+ recommendedAction: "Provide the current compiled Blueprint hash and actual evidence for every criterion." };
20
+ }
21
+ function blueprintBlocked(request, findings) {
22
+ return { schemaVersion: BP_RESPONSE_SCHEMA, requestId: request.requestId, status: "blocked",
23
+ brainMode: null, requestedBrainMode: "ide", brainUsed: false, revision: null,
24
+ validation: { valid: false, guarantee: "blocked", findings, summary: blueprintSummary(findings) },
25
+ artifacts: [], questions: [], findingSummary: blueprintSummary(findings) };
26
+ }
27
+ function blueprintJsonArtifact(role, name, value) {
28
+ const content = `${JSON.stringify(value, null, 2)}\n`;
29
+ return { role, name, mediaType: "application/json", sha256: `sha256:${blueprintDigest(content)}`, content };
30
+ }
31
+ function compiledBlueprint(result) {
32
+ const artifacts = result.artifacts.filter((artifact) => artifact.name === BP_IR_ARTIFACT);
33
+ if (artifacts.length !== 1) throw new Error("Blueprint compiler must return exactly one IR artifact.");
34
+ const artifact = artifacts[0];
35
+ if (artifact.sha256 !== `sha256:${blueprintDigest(artifact.content)}`) throw new Error("Compiled Blueprint artifact digest mismatch.");
36
+ return { blueprint: JSON.parse(artifact.content), blueprintSha256: artifact.sha256 };
37
+ }
38
+ function addBlueprintMachineArtifact(result) {
39
+ const { blueprint, blueprintSha256 } = compiledBlueprint(result);
40
+ const machineTasks = buildBlueprintMachineTasks(blueprint, blueprintSha256, blueprintDigest);
41
+ const taskArtifact = blueprintJsonArtifact("implementation-tasks-json", "IMPLEMENTATION-TASKS.json", machineTasks);
42
+ const manifests = result.artifacts.filter((artifact) => artifact.name === BP_MANIFEST);
43
+ if (manifests.length !== 1) throw new Error("Blueprint compiler must return exactly one artifact manifest.");
44
+ const manifest = JSON.parse(manifests[0].content);
45
+ const { content, ...entry } = taskArtifact;
46
+ manifest.artifacts.push(entry);
47
+ const manifestArtifact = blueprintJsonArtifact(manifests[0].role, BP_MANIFEST, manifest);
48
+ return { ...result, machineTasks, artifacts: [...result.artifacts.filter((artifact) => artifact.name !== BP_MANIFEST),
49
+ taskArtifact, manifestArtifact] };
50
+ }
51
+ function criterionEvidenceFindings(results, expectedIds) {
52
+ const findings = [], seen = new Set();
53
+ results.forEach((result, index) => {
54
+ const entityRef = `input.results[${index}]`;
55
+ if (!expectedIds.has(result.criterionId)) findings.push(bridgeFinding("ACCEPTANCE_UNKNOWN_CRITERION", entityRef, "Unknown criterionId.", { criterionId: result.criterionId }));
56
+ if (seen.has(result.criterionId)) findings.push(bridgeFinding("ACCEPTANCE_DUPLICATE_CRITERION", entityRef, "A criterion must have exactly one result.", { criterionId: result.criterionId }));
57
+ seen.add(result.criterionId);
58
+ const evidenceIds = new Set();
59
+ result.evidence.forEach((evidence) => {
60
+ if (evidenceIds.has(evidence.evidenceId)) findings.push(bridgeFinding("ACCEPTANCE_DUPLICATE_EVIDENCE", entityRef, "Duplicate evidenceId within a criterion.", { evidenceId: evidence.evidenceId }));
61
+ evidenceIds.add(evidence.evidenceId);
62
+ });
63
+ });
64
+ return findings;
65
+ }
66
+ function reconcileBlueprintAcceptance(request, compiled) {
67
+ const { blueprint, blueprintSha256 } = compiledBlueprint(compiled);
68
+ const input = request.input, expectedIds = new Set(blueprint.acceptanceCriteria.map((criterion) => criterion.id));
69
+ const findings = criterionEvidenceFindings(input.results, expectedIds);
70
+ if (input.blueprintSha256 !== blueprintSha256) findings.push(bridgeFinding("ACCEPTANCE_BLUEPRINT_HASH", "input.blueprintSha256",
71
+ "The report does not reference this exact compiled Blueprint.", { expected: blueprintSha256, received: input.blueprintSha256 }));
72
+ const criteria = blueprint.acceptanceCriteria.map((criterion) => {
73
+ const matches = input.results.filter((result) => result.criterionId === criterion.id);
74
+ const evidence = matches.length === 1 ? matches[0].evidence : [];
75
+ const status = matches.length === 0 || evidence.length === 0 ? "missing"
76
+ : matches.length !== 1 || evidence.some((item) => item.exitCode !== 0) ? "failed" : "passed";
77
+ if (status !== "passed") findings.push(bridgeFinding("ACCEPTANCE_EVIDENCE_REQUIRED", `criteria.${criterion.id}`,
78
+ "Each criterion requires a unique result with nonempty, successful TestEvidence.", { criterionId: criterion.id, status }));
79
+ return { criterionId: criterion.id, statement: criterion.statement, nodeRefs: criterion.nodeRefs, status, evidence };
80
+ });
81
+ const passed = findings.length === 0;
82
+ const acceptanceReport = { schemaVersion: "blueprint.acceptance-report/1.0", blueprintId: blueprint.blueprintId,
83
+ revision: blueprint.revision, blueprintSha256, passed, verificationMode: BP_REPORT_MODE, executionVerified: false,
84
+ criteria, summary: { total: criteria.length, passed: criteria.filter((item) => item.status === "passed").length,
85
+ failed: criteria.filter((item) => item.status === "failed").length, missing: criteria.filter((item) => item.status === "missing").length },
86
+ findings };
87
+ return { ...compiled, status: passed ? "succeeded" : "blocked", acceptanceReport,
88
+ validation: { valid: passed, guarantee: passed ? "reported-evidence-structurally-verified" : "blocked", findings, summary: blueprintSummary(findings) },
89
+ artifacts: [blueprintJsonArtifact("acceptance-report", "ACCEPTANCE-REPORT.json", acceptanceReport)],
90
+ findingSummary: blueprintSummary(findings) };
91
+ }
92
+ function blueprintOperationCatalog(result) {
93
+ const known = new Map(result.operationSchemas.map((schema) => [schema.operation, schema]));
94
+ const operationSchemas = Object.entries(BLUEPRINT_OPERATION_INPUTS).map(([operation, inputSchema]) => {
95
+ if (operation === "acceptance-report") return { operation,
96
+ summary: "Recompile the referenced IR and reconcile every criterion with caller-supplied TestEvidence.",
97
+ input: inputSchema, inputSchema };
98
+ const original = known.get(operation);
99
+ if (!original) throw new Error(`Hermes capability is missing: ${operation}`);
100
+ return { ...original, input: inputSchema, inputSchema };
101
+ });
102
+ return { ...result, operationSchemas, capabilities: { ...result.capabilities,
103
+ operations: Object.keys(BLUEPRINT_OPERATION_INPUTS), machineTaskSchema: "swarm.project/1.0",
104
+ acceptanceReportSchema: "blueprint.acceptance-report/1.0", acceptanceEvidenceMode: BP_REPORT_MODE,
105
+ localRunnerOperations: [] } };
106
+ }
107
+ function blueprintEnvelopeFindings(request) {
108
+ if (!request || typeof request !== "object" || Array.isArray(request)) return [bridgeFinding("BLUEPRINT_REQUEST", "request", "Request must be an object.")];
109
+ const findings = [];
110
+ if (request.schemaVersion !== BP_REQUEST_SCHEMA) findings.push(bridgeFinding("BLUEPRINT_REQUEST_SCHEMA", "schemaVersion", "Unsupported request schema."));
111
+ if (typeof request.requestId !== "string" || !request.requestId.trim()) findings.push(bridgeFinding("BLUEPRINT_REQUEST_ID", "requestId", "requestId must be a nonempty string."));
112
+ if (typeof request.operation !== "string" || !Object.hasOwn(BLUEPRINT_OPERATION_INPUTS, request.operation)) findings.push(bridgeFinding("BLUEPRINT_OPERATION_UNSUPPORTED", "operation", "Unsupported public operation."));
113
+ return findings;
114
+ }
115
+ export function createBlueprintRuntime(coreRun) {
116
+ return async function runBlueprint(request, runtimeOptions) {
117
+ const envelopeFindings = blueprintEnvelopeFindings(request);
118
+ if (envelopeFindings.length) return blueprintBlocked(
119
+ { requestId: typeof request?.requestId === "string" ? request.requestId : null }, envelopeFindings);
120
+ const inputFindings = blueprintSchemaFindings(request.input, BLUEPRINT_OPERATION_INPUTS[request.operation]);
121
+ if (inputFindings.length) return blueprintBlocked(request, inputFindings);
122
+ if (request.operation === "acceptance-report") {
123
+ const compiled = await coreRun({ ...request, operation: "compile-inline", input: { blueprint: request.input.blueprint } }, runtimeOptions);
124
+ if (compiled.status !== "succeeded" || compiled.validation?.valid !== true) return compiled;
125
+ return reconcileBlueprintAcceptance(request, compiled);
126
+ }
127
+ const result = await coreRun(request, runtimeOptions);
128
+ if (result.status !== "succeeded") return result;
129
+ if (request.operation === "capabilities") return blueprintOperationCatalog(result);
130
+ if (request.operation === "help") {
131
+ const catalog = blueprintOperationCatalog({ ...result, operationSchemas: result.help.operations, capabilities: {} });
132
+ return { ...result, operationSchemas: catalog.operationSchemas, help: { ...result.help, operations: catalog.operationSchemas } };
133
+ }
134
+ if (request.operation === "compile-inline" && result.validation?.valid === true) return addBlueprintMachineArtifact(result);
135
+ return result;
136
+ };
137
+ }
@@ -0,0 +1,81 @@
1
+ // Public operation schemas supplement, and never replace, Hermes IR semantic validation.
2
+ const bpString = { type: "string", minLength: 1, pattern: "\\S" };
3
+ const bpArray = (items, minItems = 0) => ({ type: "array", items, minItems });
4
+ const bpObject = (properties, required = Object.keys(properties), additionalProperties = false) =>
5
+ ({ type: "object", properties, required, additionalProperties });
6
+ const bpRefs = bpArray(bpString);
7
+ const bpPort = bpObject({ name: bpString, exposed: { type: "boolean" } }, ["name"], true);
8
+ const bpNode = bpObject({
9
+ id: bpString, title: bpString, moduleId: bpString, entry: { type: "boolean" },
10
+ inputs: bpArray(bpPort), outputs: bpArray(bpPort), requirementRefs: bpRefs,
11
+ }, ["id", "title", "moduleId", "inputs", "outputs"], true);
12
+ const bpEdge = bpObject({
13
+ id: bpString, fromNodeId: bpString, toNodeId: bpString,
14
+ type: { enum: ["data", "control", "success", "error", "trace", "event", "approval", "recovery", "audit", "optional", "compensation"] },
15
+ fromOutput: bpString, toInput: bpString, allowCycle: { type: "boolean" }, loopGuard: bpString,
16
+ loopLimit: bpObject({ maxIterations: { type: "integer", minimum: 1 } }, ["maxIterations"], true),
17
+ }, ["id", "fromNodeId", "toNodeId", "type"], true);
18
+ export const BLUEPRINT_IR_INPUT_SCHEMA = bpObject({
19
+ schemaVersion: { const: "blueprint.ir/1.0" }, blueprintId: bpString, title: bpString,
20
+ revision: { type: "integer", minimum: 0 }, entryNodeId: bpString,
21
+ baseline: bpObject({ summary: bpString, facts: bpArray(bpObject({
22
+ id: bpString, statement: bpString,
23
+ status: { enum: ["confirmed", "inferred", "defaulted", "unknown", "conflicted", "rejected"] },
24
+ }, ["id", "statement", "status"], true)) }, ["summary", "facts"], true),
25
+ domains: bpArray(bpObject({ id: bpString, name: bpString, summary: bpString }, ["id", "name"], true)),
26
+ modules: bpArray(bpObject({ id: bpString, domainId: bpString, name: bpString }, ["id", "domainId", "name"], true)),
27
+ nodes: bpArray(bpNode, 1), edges: bpArray(bpEdge),
28
+ acceptanceCriteria: bpArray(bpObject({ id: bpString, statement: bpString, nodeRefs: bpRefs }, ["id", "statement", "nodeRefs"], true), 1),
29
+ }, undefined, true);
30
+ export const BLUEPRINT_EVIDENCE_SCHEMA = bpObject({
31
+ schemaVersion: { const: "cli.tax.test-evidence/1.0" }, evidenceId: bpString,
32
+ kind: { enum: ["test", "build", "lint", "security", "benchmark"] },
33
+ runner: { enum: ["local", "trusted-runner"] }, command: bpString, exitCode: { type: "integer" },
34
+ durationMs: { type: "number", minimum: 0 }, summary: bpString,
35
+ artifactSha256: { type: "string", pattern: "^[0-9a-f]{64}$" },
36
+ }, ["schemaVersion", "evidenceId", "kind", "runner", "command", "exitCode", "durationMs", "summary"], true);
37
+ const bpEmptyInput = bpObject({});
38
+ const bpCompileInput = bpObject({ blueprint: BLUEPRINT_IR_INPUT_SCHEMA });
39
+ export const BLUEPRINT_OPERATION_INPUTS = Object.freeze({
40
+ capabilities: bpEmptyInput, help: bpEmptyInput, intake: bpEmptyInput,
41
+ validate: bpCompileInput, "compile-inline": bpCompileInput,
42
+ "acceptance-report": bpObject({
43
+ blueprint: BLUEPRINT_IR_INPUT_SCHEMA,
44
+ blueprintSha256: { type: "string", pattern: "^sha256:[0-9a-f]{64}$" },
45
+ results: bpArray(bpObject({ criterionId: bpString, evidence: bpArray(BLUEPRINT_EVIDENCE_SCHEMA) })),
46
+ }),
47
+ });
48
+ export function blueprintSchemaFindings(value, schema, path = "input") {
49
+ const findings = [];
50
+ const reject = (message) => findings.push({
51
+ severity: "P0", ruleId: "BLUEPRINT_INPUT_SCHEMA", entityRef: path, message,
52
+ evidence: {}, recommendedAction: "Follow this operation's published inputSchema.",
53
+ });
54
+ const object = value !== null && typeof value === "object" && !Array.isArray(value);
55
+ const matches = !schema.type || (schema.type === "object" ? object
56
+ : schema.type === "array" ? Array.isArray(value)
57
+ : schema.type === "integer" ? Number.isInteger(value)
58
+ : typeof value === schema.type && (schema.type !== "number" || Number.isFinite(value)));
59
+ if (!matches) { reject(`Expected ${schema.type}.`); return findings; }
60
+ if (Object.hasOwn(schema, "const") && value !== schema.const) reject(`Expected ${schema.const}.`);
61
+ if (schema.enum && !schema.enum.includes(value)) reject("Value is not in the supported enum.");
62
+ if (typeof value === "string") {
63
+ if (schema.minLength !== undefined && value.length < schema.minLength) reject("String is too short.");
64
+ if (schema.pattern && !new RegExp(schema.pattern).test(value)) reject("String does not match the required pattern.");
65
+ }
66
+ if (typeof value === "number" && schema.minimum !== undefined && value < schema.minimum) reject("Number is below the minimum.");
67
+ if (Array.isArray(value)) {
68
+ if (schema.minItems !== undefined && value.length < schema.minItems) reject("Array has too few items.");
69
+ if (schema.items) value.forEach((item, index) => findings.push(...blueprintSchemaFindings(item, schema.items, `${path}[${index}]`)));
70
+ }
71
+ if (object) {
72
+ if (schema.required) for (const key of schema.required) {
73
+ if (!Object.hasOwn(value, key)) findings.push(...blueprintSchemaFindings(undefined, schema.properties[key], `${path}.${key}`));
74
+ }
75
+ for (const [key, item] of Object.entries(value)) {
76
+ if (schema.properties && Object.hasOwn(schema.properties, key)) findings.push(...blueprintSchemaFindings(item, schema.properties[key], `${path}.${key}`));
77
+ else if (schema.additionalProperties === false) reject(`Unsupported property: ${key}.`);
78
+ }
79
+ }
80
+ return findings;
81
+ }
@@ -0,0 +1,68 @@
1
+ // Condense all IR edge types into an acyclic implementation dependency graph.
2
+ function blueprintComponents(nodes, edges) {
3
+ const ids = nodes.map((node) => node.id).sort();
4
+ const forward = new Map(ids.map((id) => [id, []]));
5
+ const reverse = new Map(ids.map((id) => [id, []]));
6
+ for (const edge of edges) {
7
+ forward.get(edge.fromNodeId).push(edge.toNodeId);
8
+ reverse.get(edge.toNodeId).push(edge.fromNodeId);
9
+ }
10
+ const seen = new Set(), finish = [];
11
+ for (const id of ids) {
12
+ if (seen.has(id)) continue;
13
+ const stack = [{ id, expanded: false }];
14
+ while (stack.length) {
15
+ const current = stack.pop();
16
+ if (current.expanded) { finish.push(current.id); continue; }
17
+ if (seen.has(current.id)) continue;
18
+ seen.add(current.id);
19
+ stack.push({ id: current.id, expanded: true });
20
+ for (const next of forward.get(current.id)) if (!seen.has(next)) stack.push({ id: next, expanded: false });
21
+ }
22
+ }
23
+ const assigned = new Set(), components = [];
24
+ for (const id of finish.reverse()) {
25
+ if (assigned.has(id)) continue;
26
+ const component = [], stack = [id];
27
+ while (stack.length) {
28
+ const current = stack.pop();
29
+ if (assigned.has(current)) continue;
30
+ assigned.add(current); component.push(current);
31
+ for (const next of reverse.get(current)) if (!assigned.has(next)) stack.push(next);
32
+ }
33
+ components.push(component.sort());
34
+ }
35
+ return components.sort((left, right) => left[0] < right[0] ? -1 : left[0] > right[0] ? 1 : 0);
36
+ }
37
+ export function buildBlueprintMachineTasks(blueprint, blueprintSha256, digest) {
38
+ const components = blueprintComponents(blueprint.nodes, blueprint.edges);
39
+ const nodeById = new Map(blueprint.nodes.map((node) => [node.id, node]));
40
+ const nodeTaskIds = new Map(), taskIds = new Set();
41
+ const tasks = components.map((nodeRefs) => {
42
+ const taskId = `bp-${digest(JSON.stringify(nodeRefs)).slice(0, 40)}`;
43
+ if (taskIds.has(taskId)) throw new Error("Blueprint task identifier collision.");
44
+ taskIds.add(taskId);
45
+ nodeRefs.forEach((nodeId) => nodeTaskIds.set(nodeId, taskId));
46
+ return { taskId, title: nodeRefs.map((id) => nodeById.get(id).title).join(" / "),
47
+ status: "backlog", owner: null, dependsOn: [], nodeRefs,
48
+ acceptanceRefs: blueprint.acceptanceCriteria.filter((criterion) =>
49
+ criterion.nodeRefs.some((id) => nodeRefs.includes(id))).map((criterion) => criterion.id).sort(),
50
+ internalEdgeRefs: blueprint.edges.filter((edge) => nodeRefs.includes(edge.fromNodeId)
51
+ && nodeRefs.includes(edge.toNodeId)).map((edge) => edge.id).sort() };
52
+ });
53
+ const byId = new Map(tasks.map((task) => [task.taskId, task]));
54
+ for (const edge of blueprint.edges) {
55
+ const from = nodeTaskIds.get(edge.fromNodeId), to = nodeTaskIds.get(edge.toNodeId);
56
+ if (from !== to) byId.get(to).dependsOn.push(from);
57
+ }
58
+ for (const task of tasks) task.dependsOn = [...new Set(task.dependsOn)].sort();
59
+ return {
60
+ schemaVersion: "swarm.project/1.0", blueprintId: blueprint.blueprintId, revision: blueprint.revision,
61
+ blueprintSha256, entryTaskId: nodeTaskIds.get(blueprint.entryNodeId),
62
+ dependencyModel: "all-ir-edges-condensed", tasks,
63
+ acceptanceCriteria: blueprint.acceptanceCriteria.map((criterion) => ({
64
+ criterionId: criterion.id, statement: criterion.statement, nodeRefs: [...criterion.nodeRefs].sort(),
65
+ taskRefs: [...new Set(criterion.nodeRefs.map((id) => nodeTaskIds.get(id)))].sort(),
66
+ })).sort((left, right) => left.criterionId < right.criterionId ? -1 : left.criterionId > right.criterionId ? 1 : 0),
67
+ };
68
+ }
package/package.json CHANGED
@@ -9,7 +9,10 @@
9
9
  "broker.mjs",
10
10
  "README.md",
11
11
  "skill/SKILL.md",
12
- "skill/skill.json"
12
+ "skill/skill.json",
13
+ "blueprint-schema.mjs",
14
+ "blueprint-task-graph.mjs",
15
+ "blueprint-bridge.mjs"
13
16
  ],
14
17
  "license": "UNLICENSED",
15
18
  "name": "cli-blueprint",
@@ -18,5 +21,5 @@
18
21
  "url": "https://github.com/88208555/blueprint-clitax.git"
19
22
  },
20
23
  "type": "module",
21
- "version": "7.0.33"
24
+ "version": "7.0.34"
22
25
  }
package/skill/SKILL.md CHANGED
@@ -5,7 +5,7 @@ description: '把一个目标编译为可执行、可验证、可追溯的工程
5
5
 
6
6
  # Blueprint Skill
7
7
 
8
- Package version: v7.0.33
8
+ Package version: v7.0.34
9
9
 
10
10
  远端 Hermes 编译器版本:0.4.0(独立于 npm 包版本)
11
11
 
@@ -34,7 +34,8 @@ POST JSON to the endpoint with an `input` wrapper:
34
34
  - `help`: return the usage guide, operation catalog, and request examples.
35
35
  - `intake`: return the questions the IDE must ask the user before building the Blueprint.
36
36
  - `validate`: deterministically validate a Blueprint object conforming to `blueprint.ir/1.0`.
37
- - `compile-inline`: validate and compile a Blueprint, returning the generated artifacts inline.
37
+ - `compile-inline`: validate and compile a Blueprint, returning artifacts and a deterministic Swarm machine task package inline.
38
+ - `acceptance-report`: recompile the referenced Blueprint and reconcile every acceptance criterion with supplied TestEvidence.
38
39
 
39
40
  ## Required flow
40
41
 
@@ -44,6 +45,8 @@ POST JSON to the endpoint with an `input` wrapper:
44
45
  3. Do not compile a Blueprint until all required questions are answered.
45
46
  4. Build a Blueprint conforming to `blueprint.ir/1.0`, then call `validate`.
46
47
  5. Fix every validation finding until the report is green, then call `compile-inline` and save the artifacts.
48
+ 6. Bind `response.machineTasks` to Swarm `validate-json.input.project`; bind `response.machineTasks.tasks` to the first `dispatch.input.tasks`. Pass each real Swarm response's complete tasks array to the next mutation. There is no separate Swarm import operation.
49
+ 7. After actual execution, call `acceptance-report` with the same Blueprint, its original `machineTasks.blueprintSha256`, and one evidence result for every criterion. A reported pass checks structure and coverage; execution provenance remains the caller's responsibility.
47
50
 
48
51
  ## Official catalog hops
49
52
 
@@ -248,9 +251,51 @@ After `capabilities`, read `officialCatalog`. Default allowlist is official skil
248
251
  | B1 | 结构校验与可修复 Finding | 已实现 | `evidence` 与 `recommendedAction` 已由远端 Hermes 0.4.0 返回。 |
249
252
  | B2 | 增量规划/修订 | 部分实现 | IR 支持调用方维护 `revision`;服务端不保存蓝图,也没有增量更新操作。 |
250
253
  | B3 | 业务模板库与粗粒度模式 | 规划中 | 当前没有模板操作,`template` 与 `coarseMode` 均不是受支持输入。 |
251
- | B4 | 验收回传、开放问题闭环、Validator 桥接 | 规划中 | 当前没有 `acceptance-report`、`answer-questions` Validator 桥接操作。 |
254
+ | B4 | 验收回传、开放问题闭环、Validator 桥接 | 部分实现 | `acceptance-report` 已逐项核对共享 TestEvidence;`answer-questions` 与自动调用 Validator 仍未实现。 |
252
255
 
253
- 只调用 `capabilities` 返回的五个操作。不要根据规划中条目构造请求,也不要把 npm 包版本 `v7.0.33` 与远端 Hermes 编译器版本 `0.4.0` 混为一谈。
256
+ 只调用 `capabilities` 返回的六个操作。不要根据规划中条目构造请求,也不要把 npm 包版本 `v7.0.34` 与远端 Hermes 编译器版本 `0.4.0` 混为一谈。
257
+
258
+
259
+ ## 机器任务与验收回传
260
+
261
+ `capabilities.operationSchemas[].inputSchema` 与 `help.operationSchemas[].inputSchema` 是可执行的 JSON Schema;公开操作拒绝未知输入字段。IR 内的扩展字段交给 Hermes 的既有语义校验,结构 Schema 不替代节点、边、追溯与环规则。
262
+
263
+ 成功的 `compile-inline` 同时返回 `machineTasks` 和同内容的 `IMPLEMENTATION-TASKS.json`。后者也登记于 `ARTIFACT-MANIFEST.json`。机器包格式为 `swarm.project/1.0`,含 `blueprintId`、`revision`、`blueprintSha256`、`entryTaskId`、`tasks` 与 `acceptanceCriteria`。
264
+
265
+ - `blueprintSha256` 是原 `PROJECT-BLUEPRINT.json` 工件的 `sha256:<64hex>`,调用方必须原样传递。
266
+ - `tasks` 包含 `taskId/title/status/owner/dependsOn/nodeRefs/acceptanceRefs/internalEdgeRefs`;初始状态为 `backlog`,`owner` 为 `null`。
267
+ - 任务依赖采用所有 IR 连线的保守顺序。显式有界循环的强连通节点合并为一个实现任务,组内边保存在 `internalEdgeRefs` 并可追溯原 IR;跨组边成为 `dependsOn`,不会生成 Swarm 依赖环。
268
+ - 任务 ID 为 `bp-` 加排序后的 `nodeRefs` JSON 的 SHA-256 前 40 位,节点顺序改变不会重编号。验收项 `criterionId` 原样保留 IR 的 `acceptanceCriteria.id`;`taskRefs` 指向对应机器任务。
269
+ - 该包表达实现计划,不证明代码已执行。具体执行步骤由调用方显式定义,不根据文案 `nextStep` 猜测执行。
270
+
271
+ `acceptance-report.input`:
272
+
273
+ ```json
274
+ {
275
+ "blueprint": "<原 blueprint.ir/1.0 对象>",
276
+ "blueprintSha256": "<原 PROJECT-BLUEPRINT.json 的 sha256:...>",
277
+ "results": [
278
+ {
279
+ "criterionId": "<原验收项 id>",
280
+ "evidence": [
281
+ {
282
+ "schemaVersion": "cli.tax.test-evidence/1.0",
283
+ "evidenceId": "<实际执行证据 id>",
284
+ "kind": "test",
285
+ "runner": "local",
286
+ "command": "<实际执行命令>",
287
+ "exitCode": 0,
288
+ "durationMs": 12,
289
+ "summary": "<实际执行摘要>",
290
+ "artifactSha256": "<可选,小写 64 位裸 SHA>"
291
+ }
292
+ ]
293
+ }
294
+ ]
295
+ }
296
+ ```
297
+
298
+ 以上占位文字仅为字段说明,不能作为执行证据提交。每个验收项必须恰好一条结果,证据数组非空且全部退出码为 0。未知或重复验收项、缺失或重复证据、错误格式、非零退出码以及蓝图哈希不一致,均返回 `blocked`。返回 `ACCEPTANCE-REPORT.json` 与逐项 `acceptanceReport.criteria`,并明确 `verificationMode=caller-supplied-test-evidence`、`executionVerified=false`;本地 CLI、受信执行器的日志与产物才是实际执行来源。
254
299
 
255
300
  ## 受限调用与自动评价闭环
256
301
 
package/skill/skill.json CHANGED
@@ -6,5 +6,5 @@
6
6
  "name": "blueprint",
7
7
  "schemaVersion": "blueprint.skill.request/1.0",
8
8
  "type": "Skill",
9
- "version": "v7.0.33"
9
+ "version": "v7.0.34"
10
10
  }