abelien-harness 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +6 -6
  2. package/package.json +2 -1
  3. package/skills/abelien-algorithm/SKILL.md +14 -6
  4. package/skills/abelien-algorithm/domain.json +59 -5
  5. package/skills/abelien-algorithm/references/research-policy.md +11 -3
  6. package/skills/abelien-algorithm/references/selection-service-contract.md +50 -0
  7. package/skills/abelien-algorithm/schemas/algorithm-bindings.v1.json +414 -0
  8. package/skills/abelien-algorithm/schemas/algorithm-candidates.v1.json +192 -0
  9. package/skills/abelien-algorithm/scripts/candidates.mjs +119 -0
  10. package/skills/abelien-algorithm/scripts/evidence.mjs +69 -0
  11. package/skills/abelien-algorithm/scripts/finalize-selection.mjs +64 -7
  12. package/skills/abelien-algorithm/scripts/lane-state.mjs +27 -0
  13. package/skills/abelien-algorithm/scripts/research.mjs +203 -0
  14. package/skills/abelien-guide/SKILL.md +2 -2
  15. package/skills/abelien-harness/references/workflow-spec.md +1 -3
  16. package/skills/abelien-harness/scripts/ab.mjs +42 -18
  17. package/skills/abelien-workspace/SKILL.md +9 -16
  18. package/skills/abelien-workspace/assets/journey-stage-plan.json +2 -2
  19. package/skills/abelien-workspace/assets/lane-plan.json +36 -0
  20. package/skills/abelien-workspace/domain.json +23 -2
  21. package/skills/abelien-workspace/references/hardware-journey-orchestration.md +3 -3
  22. package/skills/abelien-workspace/references/harness-binding.md +23 -17
  23. package/skills/abelien-workspace/references/journey-stage-plan.md +185 -0
  24. package/skills/abelien-workspace/references/user-journey.md +11 -7
  25. package/skills/abelien-workspace/scripts/lanes-next.mjs +38 -0
  26. package/skills/abelien-workspace/workflows/definitions/0852e4949f5a6246880c1a26bf5ccaa42f8cda06b960e7c7ce234a0a176878f9.json +0 -229
  27. package/skills/abelien-workspace/workflows/definitions/1179e62586cf6d88c6820022a933f719b3b4eb3c0df0856fb7a0cc6341a9d630.json +0 -1
  28. package/skills/abelien-workspace/workflows/definitions/1a9afef5463f53e58222b3476c0002aa9703b5ced16f531e3224beb14a28f9a3.json +0 -229
  29. package/skills/abelien-workspace/workflows/definitions/32ed16198839c06c36b43acbada5ba74403e37b2d351058bd7ad3d5269f76722.json +0 -1
  30. package/skills/abelien-workspace/workflows/definitions/399ef372790b7330d0ef88ce5096e7f69943d4793de97b2ed6c25165a332612f.json +0 -230
  31. package/skills/abelien-workspace/workflows/definitions/748a70482fb2f775f6e5fd942c7873534cf3e59614c77381f812a9710d479743.json +0 -1
  32. package/skills/abelien-workspace/workflows/definitions/b780bf280d61596b43eb6dbc6a417cf8df1ec9a7ae79a6de10f048da9ed58729.json +0 -229
  33. package/skills/abelien-workspace/workflows/definitions/c1b5c6c653f0c4761d51efee923aafbbeea05512e7f35c60f7f7d36ec49fc1d1.json +0 -229
  34. package/skills/abelien-workspace/workflows/definitions/df147960a3f7c4d631f66a6b91a7891f8a37d25a50322cf09bdc9304195963b0.json +0 -1
  35. package/skills/abelien-workspace/workflows/user-journey-hardware-algorithm.json +0 -230
  36. package/skills/abelien-workspace/workflows/user-journey.json +0 -391
@@ -64,6 +64,32 @@ export function createStore(workdir) {
64
64
  const logFile = path.join(dirs.log, "operations.jsonl");
65
65
 
66
66
  const readIndex = () => readJson(indexFile);
67
+ // Index writes are read-modify-write. Two ab processes on one workdir (a host running a parallel lane next to the
68
+ // main line, P2-03) would otherwise lose each other's entries, so every index write holds a short cross-process lock.
69
+ const lockFile = path.join(dirs.store, ".index.lock");
70
+ const pause = ms => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
71
+ const alive = pid => { try { process.kill(pid, 0); return true; } catch (error) { return error.code === "EPERM"; } };
72
+ function withIndexLock(task) {
73
+ const deadline = Date.now() + 10000;
74
+ for (let attempt = 0; ; attempt++) {
75
+ let fd;
76
+ try { fd = fs.openSync(lockFile, "wx", 0o600); } catch (error) {
77
+ if (error.code !== "EEXIST") throw error;
78
+ // Index writes take milliseconds: a lock whose holder is gone, or older than 30 s, was left by a crash.
79
+ let stale = false;
80
+ try {
81
+ const owner = JSON.parse(fs.readFileSync(lockFile, "utf8"));
82
+ stale = !Number.isSafeInteger(owner.pid) || !alive(owner.pid) || Date.now() - owner.at > 30000;
83
+ } catch { try { stale = Date.now() - fs.statSync(lockFile).mtimeMs > 30000; } catch { /* already released */ } }
84
+ if (stale) { fs.rmSync(lockFile, { force: true }); continue; }
85
+ if (Date.now() > deadline) throw fail("STORE_LOCKED", "resource index is held by another process; retry the command");
86
+ pause(Math.min(50, 5 * (attempt + 1)));
87
+ continue;
88
+ }
89
+ try { fs.writeSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() })); } finally { fs.closeSync(fd); }
90
+ try { return task(); } finally { fs.rmSync(lockFile, { force: true }); }
91
+ }
92
+ }
67
93
  const appendLog = entry =>
68
94
  fs.appendFileSync(logFile, canonicalJson({ ts: new Date().toISOString(), ...entry }) + "\n");
69
95
  const blobFile = digest => path.join(dirs.blobs, `${digest}.json`);
@@ -78,6 +104,9 @@ export function createStore(workdir) {
78
104
  function put(kind, localId, body) {
79
105
  if (!KIND_RE.test(kind)) throw fail("INVALID_KIND", `bad kind: ${kind}`);
80
106
  if (typeof localId !== "string" || !localId) throw fail("INVALID_ID", "localId must be a non-empty string");
107
+ return withIndexLock(() => putLocked(kind, localId, body));
108
+ }
109
+ function putLocked(kind, localId, body) {
81
110
  const id = `${kind}:${localId}`;
82
111
  const digest = sha256Hex(canonicalJson(body));
83
112
  const now = new Date().toISOString();
@@ -116,6 +145,9 @@ export function createStore(workdir) {
116
145
  return ref;
117
146
  }
118
147
  function setMachineState(id, machineName, state) {
148
+ return withIndexLock(() => setMachineStateLocked(id, machineName, state));
149
+ }
150
+ function setMachineStateLocked(id, machineName, state) {
119
151
  const index = readIndex();
120
152
  const entry = index.resources[id];
121
153
  if (!entry) throw fail("INSTANCE_NOT_FOUND", `machine instance not found: ${id}`, { id });
@@ -135,6 +167,9 @@ export function createStore(workdir) {
135
167
  .map(([id, e]) => assemble(id, e, readJson(blobFile(e.digest)).body));
136
168
  }
137
169
  function remove_(id) {
170
+ return withIndexLock(() => removeLocked(id));
171
+ }
172
+ function removeLocked(id) {
138
173
  const index = readIndex();
139
174
  if (!index.resources[id]) return false;
140
175
  delete index.resources[id];
@@ -170,7 +205,7 @@ export function createStore(workdir) {
170
205
  // doctor must report the shipped package version, never a frozen constant (0.2.3 drifted across
171
206
  // six releases). The npm tree always ships package.json at the skill root; standalone skill copies
172
207
  // fall back to FALLBACK_VERSION, which scripts/release-check.mjs pins to package.json.
173
- const FALLBACK_VERSION = "0.8.0";
208
+ const FALLBACK_VERSION = "0.11.0";
174
209
  export let VERSION = FALLBACK_VERSION;
175
210
  try {
176
211
  VERSION = String(JSON.parse(fs.readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version || FALLBACK_VERSION);
@@ -355,7 +390,7 @@ export async function main(argv, opts = {}) {
355
390
  io.out.write(canonicalJson({ ok: true, data: { usage: USAGE_TEXT } }) + "\n");
356
391
  return 0;
357
392
  }
358
- const data = await dispatch(argv, { io, workdir, env, fetchImpl: opts.fetchImpl, spawnImpl: opts.spawnImpl, modulePorts: opts.modulePorts, readEvidence: opts.readEvidence, verifyCompletion: opts.verifyCompletion });
393
+ const data = await dispatch(argv, { io, workdir, env, fetchImpl: opts.fetchImpl, spawnImpl: opts.spawnImpl, modulePorts: opts.modulePorts, readEvidence: opts.readEvidence });
359
394
  io.out.write(canonicalJson({ ok: true, data }) + "\n");
360
395
  return 0;
361
396
  } catch (e) {
@@ -539,7 +574,7 @@ async function moduleAdapter(a, ctx) {
539
574
  throw fail("ADAPTER_MODULE", `module adapter export not found: ${exportName}`, { path: a.path, export: exportName });
540
575
  }
541
576
  try {
542
- return await handler({ input: ctx.input ?? {}, env: ctx.env ?? {}, fetchImpl: ctx.fetchImpl ?? fetch, readResource: ctx.readResource, readEvidence: ctx.readEvidence, readBlob: ctx.readBlob, writeBlob: ctx.writeBlob, ports: ctx.ports, checkpoint: ctx.checkpoint });
577
+ return await handler({ input: ctx.input ?? {}, env: ctx.env ?? {}, fetchImpl: ctx.fetchImpl ?? fetch, readResource: ctx.readResource, readEvidence: ctx.readEvidence, readBlob: ctx.readBlob, writeBlob: ctx.writeBlob, listResources: ctx.listResources, ports: ctx.ports, checkpoint: ctx.checkpoint });
543
578
  } catch (e) {
544
579
  // Node 系统错误(EACCES、ENOENT 等)的 message 带绝对路径。stdout 只留 code 与 syscall;
545
580
  // 原文仅在显式 AB_DEBUG=1 时写到 stderr。错误码不变,结构仍是 {code,message}。
@@ -623,7 +658,7 @@ export async function runCapability(deps, capability, args = {}) {
623
658
  if (typeof deps.onCheckpoint === "function") deps.onCheckpoint(checkpointResource);
624
659
  return checkpointResource;
625
660
  } : undefined;
626
- const baseCtx = { readBlob: ref => deps.store.getContent(ref), writeBlob: hit.cap.checkpoint === true ? value => deps.store.putContent(value) : undefined, input: args.input ?? {}, resource, env: deps.env ?? {}, fetchImpl: deps.fetchImpl, readResource, readEvidence, ports: deps.modulePorts?.[capability], checkpoint };
661
+ const baseCtx = { readBlob: ref => deps.store.getContent(ref), listResources: kind => deps.store.list({ kind }), writeBlob: hit.cap.checkpoint === true ? value => deps.store.putContent(value) : undefined, input: args.input ?? {}, resource, env: deps.env ?? {}, fetchImpl: deps.fetchImpl, readResource, readEvidence, ports: deps.modulePorts?.[capability], checkpoint };
627
662
  const adapterInput = hit.cap.input?.from ? dotGet(baseCtx, hit.cap.input.from) : baseCtx.input;
628
663
  const decl = resolveAdapter(hit.d, hit.cap.adapter);
629
664
  const result = await runAdapter(decl, { ...baseCtx, input: adapterInput });
@@ -809,9 +844,8 @@ function validateWorkflow(def, name) {
809
844
  if (s.executor === "agent" && s.replySchema !== undefined && (typeof s.replySchema !== "object" || s.replySchema === null || Array.isArray(s.replySchema))) {
810
845
  issues.push(`step ${s.id}: replySchema must be an object`);
811
846
  }
812
- if (s.completionGuard !== undefined && (s.executor !== "agent" || typeof s.completionGuard !== "string" || !s.completionGuard.trim())) {
813
- issues.push(`step ${s.id}: completionGuard requires an agent step and a nonempty guard name`);
814
- }
847
+ // Product completion (adoption, confirmation, delivery) belongs to the host, never to a workflow step.
848
+ if (s.completionGuard !== undefined) issues.push(`step ${s.id}: completionGuard was removed; product completion is decided by the host, not by a workflow`);
815
849
  if (s.completeWhen !== undefined && (s.executor !== "cli" || typeof s.completeWhen?.path !== "string" || !Object.hasOwn(s.completeWhen, "equals") || !s.completionResult)) issues.push(`step ${s.id}: completeWhen requires a CLI step, path, equals and completionResult`);
816
850
  if (s.executor !== "cli" && s.executor !== "agent") issues.push(`step ${s.id}: executor must be cli|agent`);
817
851
  }
@@ -1005,15 +1039,6 @@ export function createWorkflowStore(workdir) {
1005
1039
  // 引步之前比对定义:cursor 在两份序列上必须指向同一步,否则不落盘、不执行。
1006
1040
  assertDefinitionMatches(instance, def);
1007
1041
  const step = instance.steps[instance.cursor];
1008
- const guard = def.steps[instance.cursor].completionGuard;
1009
- if (guard) {
1010
- if (typeof deps.verifyCompletion !== "function") throw fail("WORKFLOW_COMPLETION_REQUIRED", "trusted host completion verifier is unavailable", { guard });
1011
- const original = canonicalJson(instance);
1012
- const verified = await deps.verifyCompletion({ guard, instance: JSON.parse(original), result: JSON.parse(canonicalJson(result)) });
1013
- if (verified?.ok !== true) throw fail("WORKFLOW_COMPLETION_REQUIRED", "trusted host did not verify completion", { guard });
1014
- if (canonicalJson(load(runId)) !== original) throw fail("WORKFLOW_STATE", "workflow changed during completion verification");
1015
- step.completionReceipt = verified.receipt ?? null;
1016
- }
1017
1042
  step.result = result;
1018
1043
  step.status = "done";
1019
1044
  step.finishedAt = nowIso();
@@ -1171,7 +1196,6 @@ async function workflowCmd(pos, flags, deps) {
1171
1196
  env: deps.env,
1172
1197
  fetchImpl: deps.fetchImpl,
1173
1198
  readEvidence: deps.readEvidence,
1174
- verifyCompletion: deps.verifyCompletion,
1175
1199
  modulePorts: deps.modulePorts,
1176
1200
  });
1177
1201
  const wfs = createWorkflowStore(deps.workdir);
@@ -1236,7 +1260,7 @@ if (isMain) {
1236
1260
  // 语义零变化;main() 若异常拒绝,未处理拒绝与原 TLA 拒绝同为打印后 exit 1。
1237
1261
  main(process.argv.slice(2)).then(async code => {
1238
1262
  // process.exit() 不等待管道异步写刷盘:单行 JSON payload 较大时(实测 ~8KB 起截断,
1239
- // 如 user-journey 的 status/advance 回执),超出首笔写入的部分会被静默丢弃。
1263
+ // 如硬件 workflow 的 status/advance 回执),超出首笔写入的部分会被静默丢弃。
1240
1264
  // 追加一次空写作为刷盘屏障——流按写入顺序回调,空写回调触发即代表此前全部字节
1241
1265
  // 已交给 OS——随后再退出;写错误(如 EPIPE)仍由回调吞掉,保持既有静默语义。
1242
1266
  await Promise.all([
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: abelien-workspace
3
- description: Abelien 作品旅程与业务契约:维护 PRD 定义与逐轮条目登记、A1–C 推进条件、可选交互细化与收尾成果体验、作品上下文、采用与版本、检查、同版确认及交付恢复规则,提供 user-journey 工作流入口。用于判断作品如何推进或启动、恢复创作;guide 按本包规则调度,clarify 提供需求建议,实际写入由已接产品服务完成。
3
+ description: Abelien 作品旅程与业务契约:维护 PRD 定义与逐轮条目登记、A1–C 推进条件、可选交互细化与收尾成果体验、作品上下文、采用与版本、检查、同版确认及交付恢复规则,提供按意图推进的阶段计划(workspace.journey-next)。用于判断作品如何推进或启动、恢复创作;guide 按本包规则调度,clarify 提供需求建议,实际写入由已接产品服务完成。
4
4
  license: MIT
5
5
  compatibility:
6
6
  - claude-code
@@ -14,9 +14,9 @@ compatibility:
14
14
 
15
15
  ## 读取入口
16
16
 
17
- - 判断阶段、修改回流或确认资格:先核对 OSS CAD 优先和Studio 产品要求与宿主版本边界,再读[完整用户旅程](references/user-journey.md),这是产品规则的唯一正文;交互细化邀请见 §4.1,点击“随时收尾”后的成果体验、仿真/Three.js 行为演示与 PRD 入口见 §4.2。
17
+ - 判断阶段、修改回流或确认资格:先核对 OSS CAD 优先和Studio 产品要求与宿主版本边界,再读[完整用户旅程](references/user-journey.md),这是产品规则的唯一正文(A1 通过之后到交付首版的逐阶段规则在阶段计划,见下文);交互细化邀请见 §4.1,点击“随时收尾”后的成果体验、仿真/Three.js 行为演示与 PRD 入口见 §4.2。
18
18
  - 核对作品上下文、需求建议与采用结果:读[领域模型与职责交接](references/domain-model.md)。
19
- - 启动、恢复、调用子流程或提交结果:读[调用与能力映射](references/harness-binding.md),并读取实际 workflow 定义。
19
+ - 推进作品、恢复、调用领域流程或提交结果:读[调用与能力映射](references/harness-binding.md),领域 workflow 另读其实际定义。
20
20
  - 在已采用设计上做 CAD 局部修改或候选返工:按[调用与能力映射](references/harness-binding.md#adopted-design-revision-loop)使用受信 Studio broker;候选和采用版本必须分开。
21
21
  - 初次建立或随需求变更维护 PRD:读[PRD 定义与逐轮维护契约](references/prd-contract.md),按[模板快照](assets/prd-template.json)定位字段与稳定行 ID。
22
22
 
@@ -26,20 +26,13 @@ compatibility:
26
26
 
27
27
  ## 产品入口
28
28
 
29
- [user-journey.json](workflows/user-journey.json) 中的 `journey` 对应本包完整旅程,供 Agent 阅读。其单个 `drive-product-journey` 步骤由使用 guide 的宿主 Agent 跨轮驱动。
30
-
31
- ```bash
32
- node <skills目录>/abelien-harness/scripts/ab.mjs --workdir <作品工作目录> workflow run user-journey --input '{"prompt":"..."}'
33
- # 新创作才启动;已有运行先按调用说明恢复
34
- # awaiting_agent@drive-product-journey:继续在同一步骤内消费宿主输入与真实任务
35
- ```
29
+ 作品推进没有 workflow:宿主持有作品事实、版本与任务,任何宿主(Web、Claude Code、Codex)都按同一套规则推进。A1 之前与 A1–C 的判定读[完整用户旅程](references/user-journey.md);A1 通过之后到交付首版的阶段与逐阶段规则由 `workspace.journey-next` 给出(见下节);领域内的多步流程(如硬件选型 `user-journey-hardware`)各跑自己的 workflow。逐步做法见[调用与能力映射](references/harness-binding.md#host-driven-journey)。
36
30
 
37
31
  - 阶段、采用、版本及确认条件以本包规则为准。clarify 的结构校验和领域阶段成功不替代作品确认条件。
38
- - 当前引擎是顺序 workflow v1,不解释 `journey` 分支,也不校验 `input.optional`、`replySchema` 或 B3。业务规则由 Agent 按真实证据执行;产品服务仍须提供实际写入与验证契约。
39
32
  - 必要问题的工具等待与回答结果见[卡片调用契约](../../docs/architecture/question-card.md):宿主持久原调用后结束执行,独立答卡请求接上原 tool 结果,不构造 User 答案;真实用户原文仍保留 user 来源。
40
- - 普通进度、必要问题和等待任务保持 `awaiting_agent`。只有达到本包交接条件才 advance;具体修改按作品规则回流。
41
- - 收尾/返回编辑是同一作品的展示模式切换;成果体验中的 machine states 只驱动行为演示,不替代 A1–C、workflow 状态或确认条件。
42
- - 初始 input 只是启动快照。后续需求、采用方案、版本、决定与任务从宿主/产品回读,缺接口就说明缺口。
33
+ - 首版展示、普通进度、必要问题和等待任务时停止本轮、说明下一步,等新事实到来再判断;不为“保持位置”启动或挂起任何 run。
34
+ - 收尾/返回编辑是同一作品的展示模式切换;成果体验中的 machine states 只驱动行为演示,不替代 A1–C 或确认条件。
35
+ - 需求、采用方案、版本、决定与任务每轮从宿主/产品回读,缺接口就说明缺口。
43
36
 
44
37
  ## 阶段计划与 journey-next
45
38
 
@@ -50,7 +43,7 @@ node <skills目录>/abelien-harness/scripts/ab.mjs --workdir <作品工作目录
50
43
  --input '{"intent":"restore","artifacts":{"reference_final":true}}'
51
44
  ```
52
45
 
53
- 它是纯计算,返回下一阶段、目标、`directives` 与 `actions`。宿主先跑完自己的守卫,再把 `directives` 原样附在自己的执行说明(工具名、字段)之后;`actions` 是该阶段允许的动作(取自计划的 `action_catalog`),宿主把它们映射成自己的工具,只放行这些。restore 默认锁定外形;锁定时,CAD 阶段的规则会写明不得放大外壳;未锁定时只允许在 appearance 认可的外观容差内调整外壳,超出容差换件或交用户取舍。意图的划分见 [意图路由](../../docs/architecture/intent-routing.md)。阶段计划是[完整用户旅程](references/user-journey.md)中 A1 通过之后到交付首版这一段(及其旁路)的机读派生,规则正文以 user-journey.md 为准;本能力在[能力映射](references/harness-binding.md#当前能力映射)中登记。
46
+ 它是纯计算,返回下一阶段、目标、`directives` 与 `actions`。宿主先跑完自己的守卫,再把 `directives` 原样附在自己的执行说明(工具名、字段)之后;`actions` 是该阶段允许的动作(取自计划的 `action_catalog`),宿主把它们映射成自己的工具,只放行这些。restore 默认锁定外形;锁定时,CAD 阶段的规则会写明不得放大外壳;未锁定时只允许在 appearance 认可的外观容差内调整外壳,超出容差换件或交用户取舍。意图的划分见 [意图路由](../../docs/architecture/intent-routing.md)。阶段计划是 A1 通过之后到交付首版这一段(及其旁路)阶段顺序、目标、规则与允许动作的唯一来源;「[阶段计划一览](references/journey-stage-plan.md)」由 `npm run render:journey-plan` 从它生成;[完整用户旅程](references/user-journey.md#journey-stage-plan)维护 A1–C 判定与产品规则,两者不得冲突。本能力在[能力映射](references/harness-binding.md#当前能力映射)中登记。
54
47
 
55
48
  本包定义作品事实之间的关系,不表示已实现完整的产品后台。现有可用能力和未接入项集中维护在[能力映射](references/harness-binding.md),不使用旧 `workspace.*` mock 顶替。
56
49
 
@@ -60,4 +53,4 @@ node <skills目录>/abelien-harness/scripts/ab.mjs --workdir <作品工作目录
60
53
 
61
54
  ## 已移除的旧样例
62
55
 
63
- `user-journey-mock`、其 8 个 `workspace.*` mock 能力和 draft/quote 样例 FSM 已移除;保留真实硬件阶段使用的 draft 资源类型。现行产品入口不生成假 PRD、报价或确认。旧运行及评估报告原样保留,仅查看原始 status;不要对旧 25 步实例取新版 HTTL 或 advance。恢复方法见调用说明。
56
+ `user-journey-mock`、其 8 个 `workspace.*` mock 能力和 draft/quote 样例 FSM 已移除;作品推进 workflow `user-journey`(单步 `drive-product-journey` 与其 `journey` 判定契约)和跨领域的 `user-journey-hardware-algorithm` 也已退役,规则只在旅程正文与阶段计划中维护。保留真实硬件阶段使用的 draft 资源类型。现行产品入口不生成假 PRD、报价或确认。旧运行及评估报告原样保留,仅查看原始 status;不要对旧实例取 HTTL 或 advance。处理方法见调用说明。
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": "abelien.journey-stage-plan.v1",
3
- "plan_version": "2026-10-14.1",
4
- "description": "按意图的作品旅程阶段计划。orders 是自动推进的主线;side 阶段由宿主守卫按真实结果触发。每个阶段的 directives 是领域规则(宿主中立),宿主把它们附在自己的执行说明(工具名、字段)之后,不改写、不删减。每个阶段的 actions 是该阶段允许的动作,取自 action_catalog(宿主中立的动作目录),按意图不同时写在 action_variants;宿主把动作映射为自己的工具,自动阶段只放行这些工具和宿主自己的只读工具,不另维护工具白名单。旅程规则的唯一正文是 references/user-journey.md,本计划是其机读派生:orders 对应 A1 通过之后到交付首版(§2.4、§5),side 对应首版前后由真实结果触发的旁路(§5.3、§6、§7);两处不一致时以正文为准,改正文后同步本计划并升 plan_version。",
3
+ "plan_version": "2026-10-14.3",
4
+ "description": "按意图的作品旅程阶段计划,是 A1 通过之后到交付首版这一段(及其旁路)的阶段顺序、阶段目标、领域规则与允许动作的唯一来源;Web、Claude Code、Codex 等所有宿主都经 workspace.journey-next 读取,不另维护阶段顺序或工具白名单。orders 是自动推进的主线;side 阶段由宿主守卫按真实结果触发。每个阶段的 directives 是领域规则(宿主中立),宿主把它们附在自己的执行说明(工具名、字段)之后,不改写、不删减。每个阶段的 actions 是该阶段允许的动作,取自 action_catalog(宿主中立的动作目录),按意图不同时写在 action_variants;宿主把动作映射为自己的工具,自动阶段只放行这些工具和宿主自己的只读工具。references/journey-stage-plan.md(阶段计划一览)由 tools/render-journey-plan.mjs 从本计划生成;references/user-journey.md 维护 A1–C 判定与产品规则,本计划不得与之冲突。改动本计划须升 plan_version、在 test/journey-next.test.mjs 的 HISTORY 追加一行,并重新生成一览。",
5
5
  "artifacts": ["reference_final", "concept_image", "hardware", "appearance_baseline", "cad_candidate", "review"],
6
6
  "orders": {
7
7
  "create": ["reference", "concept", "hardware", "real_image", "cad", "review"],
@@ -0,0 +1,36 @@
1
+ {
2
+ "plan_version": "2026-10-14.2",
3
+ "description": "与主线阶段并行的车道。宿主在每个自动阶段开跑时调用 workspace.lanes-next,按返回的步骤在同一 run 里并发执行,不阻塞主线;车道结果不反向约束主线(2026-10-12 碰头会:算法依赖本阶段冻结,不约束硬件选型)。",
4
+ "artifacts": ["algorithm_proposed", "hardware", "algorithm_selection"],
5
+ "lanes": {
6
+ "algorithm": {
7
+ "starts_with": ["hardware", "real_image"],
8
+ "steps": {
9
+ "research": {
10
+ "goal": "需求明确后立即检索每个 reaction 可用的实现,与硬件选型同时进行",
11
+ "needs": [],
12
+ "produces": "algorithm_proposed",
13
+ "directives": [
14
+ "只凭本次需求的 reaction、limits 和落点检索,不等硬件,也不猜硬件型号;离线、隐私、延迟、费用等硬约束从一开始就用来筛选。",
15
+ "先看宿主给出的库内已适配算法,能满足 reaction 的优先;库外再联网,顺序是实现官方资料(模型卡、SDK、发布记录)→ Hugging Face → 成熟云服务官方文档;博客、论坛与聚合文章只用来发现线索。",
16
+ "每个 reaction 用 1–2 条英文查询调用 algorithm.research(purpose=implementation,查询带 reaction_id),读正文后再判断,不凭检索摘要下结论。",
17
+ "用 algorithm.candidates 为每个 reaction 提交 1–3 个候选,格式按 abelien-algorithm 的 algorithm-candidates.v1 schema:implementation_ref 是简短名称(不带网址和说明,说明写 summary),implementation_version 只写来源写明的确定版本,没写就填 null;needs 按原文记录,没写的填 null;quotes 逐字取自本次研究读到的正文,同一份资料可以同时支撑几个 reaction。违反硬约束的不提交,确实找不到的写进 unresolved 并说明原因。",
18
+ "算法结果不反向约束硬件选型,也不阻塞首版 CAD;只记录资源需求,供硬件登记后绑定。"
19
+ ]
20
+ },
21
+ "bind": {
22
+ "goal": "硬件登记后,把每个 reaction 绑定到已选型号上的一个实现并复核兼容",
23
+ "needs": ["algorithm_proposed", "hardware"],
24
+ "produces": "algorithm_selection",
25
+ "directives": [
26
+ "读取已登记的 hardware-selection(id 与 revision)和已提议的候选;硬件尚未 verified 也照常绑定,结果会保持待确认。",
27
+ "先为主控等相关型号检索厂商官方资料:algorithm.research(purpose=hardware,查询带 component_id),看 SDK、runtime、算子支持、内存与外设接口。",
28
+ "每个 reaction 恰好选一个已提议的候选(implementation_ref 照抄),绑定到具体 component_id;逐项核对 io、runtime、dependencies、network、license、resources 和每条硬约束,各写 pass/conflict/unknown、理由与来源。格式按 algorithm-bindings.v1 schema:sources 只写 research_id 与 page_id 指向的正文页(search_snippet 不算),url、访问时间与哈希由 Harness 回填;source_type 按发布方选(所选型号厂商 hardware_vendor、实现官方 implementation_official、模型卡 huggingface、云服务官方 cloud_vendor),分销商、博客不是一手来源。",
29
+ "用 algorithm.finalize(operation=bind,proposed_id 为已提议候选)提交;证据不足就如实写 unknowns/blocking_unknowns,结果保持 needs_confirmation,不编造通过。",
30
+ "只有换硬件(算力或接口变化)才重新绑定;需求的 reaction 或 limits 变化才重新研究。"
31
+ ]
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "abelien-workspace",
3
- "version": "1.7.0",
4
- "description": "作品旅程、PRD 与业务契约;user-journey 由 Agent 驱动,draft 供真实硬件阶段使用;不声明假 PRD、确认、报价或支付能力",
3
+ "version": "2.0.0",
4
+ "description": "作品旅程、PRD 与业务契约;作品推进由宿主按 workspace.journey-next 的阶段计划进行,不是 workflow;draft 供真实硬件阶段使用;不声明假 PRD、确认、报价或支付能力",
5
5
  "resources": [
6
6
  "draft"
7
7
  ],
@@ -28,6 +28,27 @@
28
28
  "completed"
29
29
  ]
30
30
  }
31
+ },
32
+ "workspace.lanes-next": {
33
+ "description": "给出当前自动阶段旁边要并发执行的车道步骤(目前是算法车道:research 与硬件选型同时开始,硬件登记后 bind)及该步骤的领域规则;纯计算,不联网、不写资源。规则来自 assets/lane-plan.json",
34
+ "adapter": {
35
+ "type": "module",
36
+ "path": "scripts/lanes-next.mjs",
37
+ "export": "lanesNext"
38
+ },
39
+ "input": {
40
+ "from": "input"
41
+ },
42
+ "resultContract": {
43
+ "nonEmptyStrings": [
44
+ "plan_version",
45
+ "intent",
46
+ "stage"
47
+ ],
48
+ "arrays": [
49
+ "lanes"
50
+ ]
51
+ }
31
52
  }
32
53
  },
33
54
  "machines": {},
@@ -6,7 +6,7 @@
6
6
 
7
7
  - 没有已验证硬件方案、要形成正式初选:运行 `user-journey-hardware`。Agent 先做需求到三级槽位分类,再调用同级 `abelien-hardware-selection` 的 `hardware.select`;只有返回的 `hardware-selection` 通过门禁后才算完成正式硬件阶段。
8
8
  - 只需要研究 OSS 候选、暂不形成正式初选:运行 `user-journey-hardware-candidates`。Agent 先调用 `hardware.search` 从 OSS 分类索引读取 Top-K 最新候选 Wiki,再比较 `adapt_system` 或 `assemble_new` 并由 `hardware.finalize` 保存 `hardware-proposal`;这个提案不能通过正式硬件门,也不能直接开始算法阶段。
9
- - 同一次任务还要完成算法选型:运行 `user-journey-hardware-algorithm`。硬件阶段门通过后,当前 run 自动进入算法研究和 `algorithm.finalize`,无需由前端另起第二个 workflow。
9
+ - 同一次任务还要完成算法选型:硬件阶段门通过后,由宿主另起 `user-journey-algorithm`(同级 `abelien-algorithm`),输入已验证硬件与完整 reactions。硬件与算法是两个领域,不在同一个 run 里串接。
10
10
  - 已有已验证方案,用户明确修改其中一项:运行 `user-journey-hardware-revise`。输入只包含当前 draft ID、基线选型 ID/版本和最新变更请求;不要把整段对话塞入硬件服务。
11
11
  - 用户改变了产品目标或大量功能,而不是局部部件:形成新的需求版本,再运行新建流程,不把它伪装成局部修订。
12
12
 
@@ -16,13 +16,13 @@ OSS Wiki 读取由 `abelien-hardware` 的本地 module adapter 实现,通用
16
16
 
17
17
  ## 完整智能体
18
18
 
19
- 完整产品智能体仍可使用“会话路由器 + 多个有阶段门的 workflow”;`user-journey-hardware-algorithm` 是硬件与算法这两个顺序阶段的组合入口,不取代局部修订等独立流程。每阶段读取上游已验证资源并产出版本化资源;变更后只把依赖图中受影响的下游阶段标为 stale。例如电池尺寸变化通常使供电和结构验证失效,算力/接口未变化时不应重跑算法选型。
19
+ 作品推进由宿主持有,按 `workspace.journey-next` 取阶段,再按阶段运行各领域自己的 workflow;workflow 只承载单一领域内的多步流程,不跨领域串接,也不替宿主判定作品完成(`test/workflow-scope.test.mjs` 把这条钉成门禁)。每阶段读取上游已验证资源并产出版本化资源;变更后只把依赖图中受影响的下游阶段标为 stale。例如电池尺寸变化通常使供电和结构验证失效,算力/接口未变化时不应重跑算法选型。
20
20
 
21
21
  待内核确有跨 workflow 调用、条件边和有界重试需求时,再设计 workflow v2;必须同时定义最大迭代次数、每次迭代记录、幂等键、取消语义和恢复行为。在这些执行语义落地前,由前端 Agent 根据终态和资源状态选择下一 workflow,不能把业务路由伪装成已由内核保证。
22
22
 
23
23
  ## Library review and online supplement
24
24
 
25
- All three new hardware definitions now share `review-hardware-candidates` -> `hardware.web-search` -> `extract-web-hardware` -> `hardware.web-merge` -> the existing comparison and formal select/proposal finalizer. Empty suitable-library unresolved lists cause no network calls. Every zero/all-unsuitable slot requires search, retaining user requirements and quantity. Persisted evidence is present in the extraction pause context. Formal select uses `merge-hardware-web.resource.id` and rereads original recall/evidence; Web preflight must provide these original completed-step resources from the same workdir. Network candidates stay needs_confirmation and envelope-only, with source/engineering unknowns. See [architecture](../../../docs/architecture/hardware-online-fallback.md). Existing running definitions are not edited; drift protection remains.
25
+ Both new-selection hardware definitions (`user-journey-hardware` and `user-journey-hardware-candidates`) share `review-hardware-candidates` -> `hardware.web-search` -> `extract-web-hardware` -> `hardware.web-merge` -> the existing comparison and formal select/proposal finalizer. Empty suitable-library unresolved lists cause no network calls. Every zero/all-unsuitable slot requires search, retaining user requirements and quantity. Persisted evidence is present in the extraction pause context. Formal select uses `merge-hardware-web.resource.id` and rereads original recall/evidence; Web preflight must provide these original completed-step resources from the same workdir. Network candidates stay needs_confirmation and envelope-only, with source/engineering unknowns. See [architecture](../../../docs/architecture/hardware-online-fallback.md). Existing running definitions are not edited; drift protection remains.
26
26
 
27
27
  Prismer Parse pending holds the original search-hardware-web cursor as awaiting_external; no extract/merge/select runs. The controlled {runId} host resumes the same frozen args/resource with `workflow resume`; task progress uses fixed original-task GET, received synchronous POST results are reused and only undispatched original-plan operations may first POST. Unknown paid effects/failed terminal runs cannot restart. Running SIGKILL snapshots require same-ID latest-checkpoint/source/plan/fence-owner verification; see the hardware evidence architecture for exact fields.
28
28
 
@@ -11,7 +11,7 @@ node "$AB" --workdir "$WD" doctor
11
11
  node "$AB" --workdir "$WD" domain list
12
12
  node "$AB" --workdir "$WD" capability list
13
13
  node "$AB" --workdir "$WD" workflow list
14
- # 已有 runId:先取原始 status,识别新入口、旧实例或子流程
14
+ # 已有 runId:先取原始 status,识别当前硬件运行还是已退役的旧实例
15
15
  node "$AB" --workdir "$WD" workflow status <runId>
16
16
  # 确认为当前定义的 awaiting_agent 步骤后,才读取 HTTL
17
17
  node "$AB" --workdir "$WD" workflow status <runId> --httl
@@ -30,13 +30,13 @@ skills/
30
30
  abelien-workspace/SKILL.md 作品业务契约入口
31
31
  abelien-workspace/references/ 完整旅程、PRD 契约、领域模型与本调用说明
32
32
  abelien-workspace/domain.json 作品域、真实硬件阶段 draft 资源与 workspace.journey-next 声明
33
- abelien-workspace/assets/ 按意图的阶段计划 journey-stage-plan.json(机读,派生自 user-journey.md)
33
+ abelien-workspace/assets/ 按意图的阶段计划 journey-stage-plan.json(自动段阶段与逐阶段规则的唯一来源)
34
34
  abelien-workspace/scripts/ journey-next.mjs(阶段计划解释器,纯计算)
35
- abelien-workspace/workflows/ 已可执行的线性工作流
35
+ abelien-workspace/workflows/ 硬件领域内的线性工作流;作品推进不是 workflow
36
36
  abelien-hardware-selection/SKILL.md 真实硬件阶段的分类与服务指引
37
37
  ```
38
38
 
39
- guide 与 clarify 自身不声明新资源、适配器或实体状态机,故不增加空 `domain.json`。clarify 正文与共用表达规则自 2026-10-11 起以 harness 仓为唯一编辑源,直接在本包修改,不再从 abelienwebapplication 同步。它作为 `abelien-*` bundle 随安装器分发,真实调用复用已发现域包。workspace 维护 A1–C 业务条件,guide 按这些条件组织调用和对话,clarify 仅提供需求建议。完整 A/B/C 回流由 `user-journey` 中的 `drive-product-journey` Agent 步骤承载。JSON 的 `journey` 只供 Agent 阅读;当前引擎顺序执行 `steps`,不解释该契约里的条件分支或 jump。
39
+ guide 与 clarify 自身不声明新资源、适配器或实体状态机,故不增加空 `domain.json`。clarify 正文与共用表达规则自 2026-10-11 起以 harness 仓为唯一编辑源,直接在本包修改,不再从 abelienwebapplication 同步。它作为 `abelien-*` bundle 随安装器分发,真实调用复用已发现域包。workspace 维护 A1–C 业务条件,guide 按这些条件组织调用和对话,clarify 仅提供需求建议。作品推进不由 workflow 承载:宿主持有作品事实、版本与任务,A1 通过之后的自动段按 `workspace.journey-next` 取阶段与规则,A1–C 的判定按[完整旅程](user-journey.md);领域内的多步流程(硬件选型等)各跑自己的 workflow,每条只调用一个领域的能力,不替宿主判定作品完成。
40
40
 
41
41
  | 需要的事实 | 读取位置/含义 |
42
42
  | --- | --- |
@@ -55,11 +55,11 @@ stdout 的 `ok:true` 表示命令取得了回执。workflow 调用还必须检
55
55
 
56
56
  | 用户旅程所需能力 | 当前 Harness 对应与限制 |
57
57
  | --- | --- |
58
- | 按意图的下一阶段与阶段规则 | `workspace.journey-next`(本包 `domain.json` 唯一的 capability,`ab capability run workspace.journey-next --input '{"intent":"restore","artifacts":{"reference_final":true}}'`):读取[阶段计划](../assets/journey-stage-plan.json),返回下一阶段、`goal`、`directives`、`actions`(该阶段允许的宿主中立动作,宿主据此放行自己的工具)、`order`、`completed`、`locks` 与 `plan_version`。纯计算,不联网、不写资源、不读宿主状态;不判断 A1–C,也不代替宿主的结算、计费、预算、保底与幂等守卫。`stage` 只接受本意图的主线阶段与 `side` 旁路阶段,`artifacts`/`locks` 不用时省略、不传 null。阶段计划派生自[完整旅程](user-journey.md),规则以旅程正文为准 |
58
+ | 按意图的下一阶段与阶段规则 | `workspace.journey-next`(本包 `domain.json` 唯一的 capability,`ab capability run workspace.journey-next --input '{"intent":"restore","artifacts":{"reference_final":true}}'`):读取[阶段计划](../assets/journey-stage-plan.json),返回下一阶段、`goal`、`directives`、`actions`(该阶段允许的宿主中立动作,宿主据此放行自己的工具)、`order`、`completed`、`locks` 与 `plan_version`。纯计算,不联网、不写资源、不读宿主状态;不判断 A1–C,也不代替宿主的结算、计费、预算、保底与幂等守卫。`stage` 只接受本意图的主线阶段与 `side` 旁路阶段,`artifacts`/`locks` 不用时省略、不传 null。阶段计划是这一段阶段顺序与逐阶段规则的唯一来源,Web、Claude Code、Codex 都经本能力读取;「[阶段计划一览](journey-stage-plan.md)」由它生成 |
59
59
  | clarify 需求整理 | 同级 `abelien-clarify`:以当前操作和同版上下文换取 `result / questions / note`,不是 CLI capability,不写资源;结构可用其 `scripts/check_result.py` 校验。共用表达规则在同级 guide 的 references,由 guide 读一次注入。已移除旧 workspace.brief,不提供假澄清 |
60
60
  | 硬件候选检索与提案 | `user-journey-hardware-candidates` → `abelien-hardware`:`hardware.search`(OSS 检索)→ agent 比较整机与零部件候选 → `hardware.finalize`(保存 `hardware-proposal`)。提案不创建正式 `hardware-selection` 门,也不能直接开始算法阶段 |
61
- | 正式硬件选型 | `user-journey-hardware` / `user-journey-hardware-algorithm` → `abelien-hardware-selection` → `hardware.select`;本地模块回读真实召回并校验 Agent 判断,返回 hardware-selection;证据不足不可用候选提案代替 |
62
- | 算法与行为实现 | `user-journey-hardware-algorithm`(正式硬件过门后同 run 继续)或独立 `user-journey-algorithm` → `abelien-algorithm`:agent 按官方资料优先联网研究,`algorithm.finalize` 本地保存。两者的 `reactions` 输入取 clarify Requirements 的 `reaction[]` 原样传入 |
61
+ | 正式硬件选型 | `user-journey-hardware` → `abelien-hardware-selection` → `hardware.select`;本地模块回读真实召回并校验 Agent 判断,返回 hardware-selection;证据不足不可用候选提案代替 |
62
+ | 算法与行为实现 | 正式硬件过门后,宿主另行启动 `user-journey-algorithm` → `abelien-algorithm`:agent 按官方资料优先联网研究,`algorithm.finalize` 本地保存。输入为已验证硬件与 clarify Requirements 的 `reaction[]` 原样;硬件与算法不在同一个 run 里串接 |
63
63
  | 外观/ID editing 与视觉审查 | 同级 `abelien-appearance`:维护轮廓、比例、CMF、部位与参考匹配目标,实际查看 structure 的本版渲染。没有注册 `appearance.*` 产品 capability;宿主须具备真实图片读取/编辑能力,不能把旧外观 mock 当成现行实现 |
64
64
  | 结构与装配 | 同级 `abelien-structure`:空间约束、CAD复用、Build123d与几何检查。通用CLI未注册structure.*产品能力;Web宿主另有隔离Worker和structure_submit/status/cancel,生成与基础检查不代表视觉/完整装配/采用 |
65
65
  | 图生 3D | 同级 `abelien-3dgen` 的真实 provider 命令通常供 appearance 按需调用,再把候选网格交 structure 作为工程参考;生成网格不等于参数化 CAD、视觉 pass、结构采用或装配验证 |
@@ -69,32 +69,38 @@ stdout 的 `ok:true` 表示命令取得了回执。workflow 调用还必须检
69
69
  | 可选交互邀请卡片 | 按旅程 §4.1 呈现继续细化/先收尾两个选项;宿主须支持非阻塞操作选择,不能直接套用必要 question 的 pending 门禁或完成 handoff 前提。尚未绑定时不声称已弹卡,不虚构 kind/action key |
70
70
  | 产品问题工具接续与引导 | 见 [Agent Card 协议](../../../docs/architecture/question-card.md):clarify/ACL 不是工具。宿主原生 `publish_agent_card(kind=question)` 持久等待,用户独立 HTTP 提交后以原调用的 tool 结果接续,不包装成 User 答案。2026-10-12 本地 Web v3 接线与 Harness/外部宿主分别核验;当前 Harness broker 尚未注册,handoff 不在本地注册表,不能据文档调用 `ab capability` 或声称受控导航已实现。只在真实完成且宿主已接导航时选择注册 action key,由用户点击 |
71
71
  | 逐轮 PRD 条目维护 | 通用CLI不内置产品写库;Web从已持久facts派生现行七块模板PRD/BOM、来源/假设/未知,不能可靠映射的实现保留同版附录。缺宿主时只交接待登记差异 |
72
- | 版本采用、确认及 PRD 导出 | Web product_adopt只接真实同版候选,确认仅用户版本绑定concept命令;租约事务保存私有不可变快照、授权JSON/Markdown导出,主旅程通过可信verifyCompletion核对回执。原生CLI缺宿主仍失败关闭;旧workspace.getPrd/confirmPlan不可替代 |
72
+ | 版本采用、确认及 PRD 导出 | Web product_adopt只接真实同版候选,确认仅用户版本绑定concept命令;租约事务保存私有不可变快照、授权JSON/Markdown导出,确认与交付回执由宿主核对,不经任何 workflow。原生CLI缺宿主仍失败关闭;旧workspace.getPrd/confirmPlan不可替代 |
73
73
 
74
74
  以后新增绑定时,以实际 `domain.json`、workflow、输入/输出契约为准,再更新本表;不能凭名称相近推定等价。
75
75
 
76
76
  `user-journey-mock` 及全部 workspace mock 已移除。`abelien-hardware` 仍有独立示例能力,仅在明确演示时使用,不能替代产品能力;“继续修改”不等于 `cancel`。
77
77
 
78
- ## 产品旅程入口的驱动与恢复
78
+ <a id="host-driven-journey"></a>
79
79
 
80
- 新入口为本包的 [user-journey.json](../workflows/user-journey.json)。先读 [完整规则](user-journey.md) 及原 JSON 的 `journey`;后者不会自动进入 HTTL context。启动需要 prompt,已有真实作品可附 productContextRef,二创附来源版本与资产/授权引用;这些附加字段由 Agent 核对,不是内核校验器。
80
+ ## 作品推进:宿主持有状态,按 journey-next 取阶段
81
+
82
+ 作品推进没有 workflow。任何宿主(Web、Claude Code、Codex)都按同一套做法推进,旅程规则只有正文与阶段计划两处来源:
83
+
84
+ 1. **回读作品事实。** 从宿主/产品服务取需求(含 clarify 保存的 `intent`)、已保存产物、采用版本、待答卡与任务。上一轮的推断不当作当前事实;缺产品写入接口时只交接待登记差异,不另存第二份作品状态。
85
+ 2. **A1 之前。** 按[完整旅程](user-journey.md) §4 与 clarify 整理需求,A1 的判定按正文。
86
+ 3. **A1 通过后到交付首版。** 把已保存产物映射为 `artifacts` 调用 `workspace.journey-next`;旁路阶段(重选、取舍、保底、修复、首版后合并提问)由宿主守卫按真实结果决定后以 `stage` 传入。按返回的 `goal` 与 `directives` 执行,只用 `actions` 对应的工具或能力;领域多步流程运行各自的 workflow(硬件见下文「硬件阶段实际驱动」)。
87
+ 4. **首版之后。** B1/B2/B3/C 按正文;修改、必要问题与确认都由宿主保存,不推进任何 workflow。
81
88
 
82
89
  ```bash
83
- node "$AB" --workdir "$WD" workflow run user-journey --input '{"prompt":"用户本轮真实创作意图"}'
84
- node "$AB" --workdir "$WD" workflow status <runId> --httl
90
+ node "$AB" --workdir "$WD" capability run workspace.journey-next --input '{"intent":"create","artifacts":{"reference_final":true}}'
85
91
  ```
86
92
 
87
- 在 `drive-product-journey` 内按 A1–C 跨轮推进,按需运行真实子流程,不递归启动主入口。首版展示、必要问题、普通修改、待任务或缺能力都不 advance;从宿主恢复最新事件与采用版本,启动 input 不会随对话自动更新。必要条件未齐时保留 awaiting_agent 并停止本轮等待新事实。
93
+ 首版展示、必要问题、普通修改、待任务或缺能力时,停止本轮并说明下一步,等新事实到来再按上面的顺序重新判断;不为“保持位置”启动或挂起任何 run。
88
94
 
89
95
  Studio 修改与首次创建均不设固定修复次数。沿用同一个 operation、根预算和上游身份,由剩余预算、有效租约、超时、用户取消及确定的冲突报告控制继续执行;同一父候选不得重复发起同一步修复。`repairCount` 是非负整数,非空 `repairOrdinal` 为正整数且不得大于计数。解析回执不代表授权执行,也不代表目标数据库已升级;预算预占与原子计数仍由 Web 负责。
90
96
 
91
- 点击“随时收尾”由宿主切换成果体验并收起编辑面板,不提交 workflow 完成,也不取消作品。真实任务与确认条件独立保留;后续结果不自动恢复面板或追问。返回编辑、播放/重置演示、查看或导出草稿都不重启主入口、不升产品版本。
97
+ 点击“随时收尾”由宿主切换成果体验并收起编辑面板,不取消作品。真实任务与确认条件独立保留;后续结果不自动恢复面板或追问。返回编辑、播放/重置演示、查看或导出草稿都不重新开始推进、不升产品版本。
92
98
 
93
- 必要答卡等待只属于宿主工具生命周期:保存原 assistant 调用及私有检查点后结束 HTTP/Worker,收到用户决定后以匹配的 tool 结果发起下一次模型请求。原任务/toolCallId 不随后继物理 run 改变;答卡与发卡都保持 Harness awaiting_agent,不提交 advance。完整 wire、幂等、部分回答、旧卡与安装边界见 Agent Card 协议 §5–§8。
99
+ 必要答卡等待只属于宿主工具生命周期:保存原 assistant 调用及私有检查点后结束 HTTP/Worker,收到用户决定后以匹配的 tool 结果发起下一次模型请求。原任务/toolCallId 不随后继物理 run 改变;发卡与答卡都不改变任何 workflow 的状态。完整 wire、幂等、部分回答、旧卡与安装边界见 Agent Card 协议 §5–§8。
94
100
 
95
- 同版确认事件经 B3 复核、快照真实保存且真实交付状态已交代后,按 replySchema 提交一次交接。PRD/BOM/分享/视频分别注明已就绪、进行中、失败或未接入及依据;预订不是必经步骤。若产品已接 `publish_agent_card(handoff)`,可在完成回执后引导用户主动点击“去保存与预约”;未接时只给普通入口,不伪造卡片。页面打开不等于预约、订单或支付成功。内核不解析业务语义或完整 replySchema,但 `completionGuard:workspace.confirmed-snapshot` 必须由可信宿主 `verifyCompletion` 回读确认、当前版本、组合检查、快照与交付状态。未接或未通过时返回 `WORKFLOW_COMPLETION_REQUIRED`,保持 awaiting_agent;普通 resource.put 和模型自填引用不能代替回执。done 仍不代表制造或实物交付通过。C 后修改按真实产品新工作版继续,已 done 的 run 不再 advance。
101
+ 同版确认事件经 B3 复核、快照真实保存且真实交付状态已交代后,才算交接完成。PRD/BOM/分享/视频分别注明已就绪、进行中、失败或未接入及依据;预订不是必经步骤。若产品已接 `publish_agent_card(handoff)`,可在完成回执后引导用户主动点击“去保存与预约”;未接时只给普通入口,不伪造卡片。页面打开不等于预约、订单或支付成功。确认、当前版本、组合检查、快照与交付状态都以宿主回执为准,普通 resource.put 和模型自填引用不能代替回执;内核不再提供 workflow 完成门。完成仍不代表制造或实物交付通过。C 后修改按真实产品新工作版继续。
96
102
 
97
- **迁移防误用:** 旧名 `user-journey` 的实例若仍含 25 步、review-views 或 review-plan,保留只读原始 status,不取新版 HTTL 或 advance。v1 重载同名最新定义,不冻结旧定义;不得手改 run 名或 cursor。此禁令**内核已强制**:advance 与 `status --httl` 比对实例记录的步序列与当前同名定义,不同构即报 `WORKFLOW_DRIFTED` 并拒绝(不落盘、不静默跳过未执行步骤);只读 `status`/`list` 不比对,原始状态仍取得到。`user-journey-mock` 定义已删除,其实例也只读原始 status;不重放旧 mock,不改历史文件。真实作品从产品服务回读后按新入口接续。
103
+ **旧运行:** 已退役的 `user-journey`(单步 `drive-product-journey` 与更早的 25 步)和 `user-journey-hardware-algorithm` 实例保留只读 `status`/`list`;`advance` 与 `status --httl` 被拒(定义已不随包分发时为 `WORKFLOW_NOT_FOUND`,冻结定义带已移除的完成门时为 `WORKFLOW_DRIFTED`),不落盘、不跳步。工作目录里冻结了原定义的 `user-journey-hardware-algorithm` 运行仍按原定义恢复。不得手改 run 名或 cursor;`user-journey-mock` 实例同样只读。新的工作按上面的做法从产品服务回读后接续:硬件用 `user-journey-hardware`,算法在硬件过门后另起 `user-journey-algorithm`。
98
104
 
99
105
  <a id="adopted-design-revision-loop"></a>
100
106