@entrorove/skill 0.1.55 → 0.1.56

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/CAPABILITY-MAP.md CHANGED
@@ -6,7 +6,7 @@
6
6
  | --- | --- | --- |
7
7
  | 成员受限编辑 | `mutation-connect <repositoryRoot>`、`local mutation-delegate-prepare` | 原任务、冻结修改声明及租约绑定;观察、编辑、独立验证三把密钥分离。运行时依赖保持未知且只读,只允许现存文件的受限修改;不授予通用执行、合并或交付权限 |
8
8
  | 连续编辑结算 | `mutation-settle <repositoryRoot>`、`local mutation-consumption-attest`、服务端 `mutation-pass-settle` | 实际写后 M 回执与 fresh S 后验共同确认原 pass;下一轮使用 `previousPassId`,不伪造架构验证事件 |
9
- | 沙箱关闭后的本地测试 | `workflow-observe-step <repositoryRoot>`、`member-observation-admit/report/status` | 仅运行原冻结命令,对照已结算源文件和保护依赖;结果独立保存为 local / pending-independent,不升格为 V 验证或交付 |
9
+ | 沙箱关闭后的本地测试 | `workflow-observe-step <repositoryRoot>`、`member-observation-admit/report/status` | 仅运行原冻结命令;写入步骤对照已结算源文件,只读步骤对照原 W 计划的签名源文件且无需 M 结算。结果独立保存为 local / pending-independent,不升格为 V 验证或交付 |
10
10
  | 续跑入口 | 原 `execution-claim`、原 flow 恢复 | 成员受限范围返回原字段的 `localAction`,客户端串联真实快照、签名、云端通行证及本地安装;网络结果不明时恢复原请求 |
11
11
  | 同一阻断停止 | IDE `Stop` 回调 | 真实重复阻断可等待;状态查询及检查点不能重置阻断,宿主重复 Stop 不再生成无限续跑。原 flow、需求队列与恢复数据保留 |
12
12
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.56 — 2026-10-17
4
+
5
+ - 补齐原工作流显式只读步骤及无写入结算的本地观察,复核真实受检仓库字节与权限;修复流程列表空输入兼容,返回精确规划前置条件与范围错误,并配置 GitHub OIDC 发布。
6
+ - Added explicit read-only workflow steps and local observations without fabricated mutation settlements, checking actual repository bytes and modes; fixed empty-input flow listing, precise planning prerequisite and scope errors, and GitHub OIDC publishing metadata.
7
+
3
8
  ## 0.1.55 — 2026-10-17
4
9
 
5
10
  - 修复任务间消息被技能误拦截,将已授权通信交回主机审批;规范只读和 SDK 命令执行参数;新增原流程计划阶段的显式范围重评与恢复提示,保留原门禁、合同、请求身份及失败历史。
package/IDE-EXECUTION.md CHANGED
@@ -6,6 +6,8 @@ If the IDE guard itself is the blocked step, run `entrorove unlock` directly fro
6
6
 
7
7
  Capability discovery and offline unlock remain administrative calls during `entry`, before a flow exists. The guard recognizes its own rendered commands with quoted identity assignments, their order changed, or a literal `env` prefix containing only supported host/session identities; it rewrites approved calls to the installed runtime and current session. This does not authorize native scripts or unbound project writes.
8
8
 
9
+ `flows` is a read-only account query with no stdin. For host compatibility, the guard accepts a literal empty `{}` here-doc on `flows` and removes it from the bound command; any nonempty input remains rejected.
10
+
9
11
  Approved Codex `exec_command` reads and SDK calls run with `/bin/sh` and `login:false`; static code mode uses the same execution input check. Its noninteractive workdir and output controls pass through. Other hosts keep their adapter's command schema, including timeout where supported, with custom executors and permission expansion rejected.
10
12
 
11
13
  For a Codex task-to-task report explicitly authorized by the human user, the guard passes the exact `mcp__codex_app__send_message_to_thread` tool through unchanged, including a static `tools.mcp__codex_app__send_message_to_thread({...})` call inside `functions.exec`. Codex still decides whether the user authorized the message. Its text and response grant no Entrorove flow, file, shell or task-creation authority.
@@ -90,7 +92,7 @@ After the pass's actual writes finish, rerun the original `scope-connect` with i
90
92
 
91
93
  If settlement reports `ENTROROVE_SCOPE_AFTER_WRITE_REQUIRED`, refresh only the original scope and retry the same consumption request and receipt. A lost network response is different: rerunning the identical connector restores its original GET result without a replacement POST or signature.
92
94
 
93
- With cloud sandbox disabled, the returned `observationAction` runs `npx @entrorove/skill workflow-observe-step <original repositoryRoot>` with `{taskId,flowId,stepId,leaseId,generation,settlementId}`. The service admits only the original frozen command, executable hash, environment and limits, with source hashes updated solely by accepted settlements. The local runner checks the actual source and protected dependencies before and after execution. No validator private key is needed. A real successful process remains `status: incomplete`, `runner: local`, `receipt: null`, `independentValidation: pending`; its result is stored separately and read with `member-observation-status`. It cannot advance formal verified dependencies or task delivery. Reentry preserves a completed process result; an unknown process outcome never authorizes an automatic second launch.
95
+ With cloud sandbox disabled, the returned `observationAction` runs `npx @entrorove/skill workflow-observe-step <original repositoryRoot>`. A mutable step uses `{taskId,flowId,stepId,leaseId,generation,settlementId}` and the service updates source hashes only through accepted M settlements. An explicitly frozen read-only step uses `{taskId,flowId,stepId,leaseId,generation}`; its signed admission declares `sourceKind: original-read-only-plan` and contains no settlement ID or digest. Both paths require the original frozen command, executable hash, environment, limits and current signed source manifest. The local runner checks the actual source and protected dependencies before and after execution, including repository bytes and modes. No validator private key is needed. A real successful process remains `status: incomplete`, `runner: local`, `receipt: null`, `independentValidation: pending`; its result is stored separately and read with `member-observation-status`. It cannot advance formal verified dependencies or task delivery. Reentry preserves a completed process result; an unknown process outcome never authorizes an automatic second launch.
94
96
 
95
97
  This command runs with the host OS user's permissions, not inside an OS sandbox. Its repository checks do not prove that external files, networks or databases were unchanged: `executionIsolation` remains `host-permissions`, and `externalEffects` remains `unverified`. The independent supervisor enforces the admitted start deadline and command timeout and reclaims its own process group if the CLI disappears. Only the original approved command can be launched; these observations grant no general shell or project-write permission.
96
98
 
package/SKILL.md CHANGED
@@ -5,7 +5,7 @@ description: Execute an engineering task through Entrorove, preserving its origi
5
5
 
6
6
  # entrorove · 熵巡
7
7
 
8
- Version: 0.1.55.
8
+ Version: 0.1.56.
9
9
 
10
10
  Entrorove applies to the whole task once selected. Reading this document or calling an entry once does not execute the task. On every later turn, including after compaction, continue the original task and its required next operation before dependent work. Keep the original goal, scope, account, flow, cloud task and failure records. Never describe planning, a reported step or model output as verified delivery.
11
11
 
package/ide-command.mjs CHANGED
@@ -79,7 +79,9 @@ export function sdkCommand(parsed, runtime) {
79
79
  if (['unlock', 'uninstall'].includes(args[0]) && args.length === 1 && !parsed.input) return { args };
80
80
  if (simple.has(args[0]) && args.length === 1 && parsed.input) return { args, input: parsed.input };
81
81
  if (['status', 'feedback'].includes(args[0]) && /^[a-f0-9]{64}$/u.test(args[1] ?? '') && [2,3].includes(args.length)) return { args };
82
- if (['flows', 'install', 'billing'].includes(args[0]) && args.length === 1 && !parsed.input) return { args };
82
+ if (args[0] === 'flows' && args.length === 1 && (!parsed.input || typeof parsed.input === 'object'
83
+ && !Array.isArray(parsed.input) && Object.keys(parsed.input).length === 0)) return { args };
84
+ if (['install', 'billing'].includes(args[0]) && args.length === 1 && !parsed.input) return { args };
83
85
  if (args[0] === 'install' && args.length === 3 && args[1] === '--host' && args[2] === 'codex' && !parsed.input) return { args };
84
86
  if (args[0] === 'recurring' && ['list', 'claim'].includes(args[1] ?? '') && args.length === 2 && !parsed.input) return { args };
85
87
  if (args[0] === 'recurring' && ['finish', 'runs'].includes(args[1] ?? '') && args.length === 3 && (!parsed.input || args[1] === 'finish')) return { args, ...(parsed.input ? { input: parsed.input } : {}) };
@@ -40,6 +40,7 @@ function validatePlan(plan, artifactDigest = planArtifact) {
40
40
  validateRunnerParallelPaths(plan);
41
41
  if (policy.discipline) {
42
42
  const reviewed = disciplineApprovedPolicy(policy.discipline);
43
+ if (reviewed.access === 'read-only' && policy.parallelPaths !== undefined) fail('ENTROROVE_READ_ONLY_POLICY_INVALID', 'Read-only execution cannot grant parallel mutation or equivalence permissions');
43
44
  if (!disciplineDataSnapshotsBound(reviewed, plan.files)) {
44
45
  fail('ENTROROVE_DISCIPLINE_DATA_SNAPSHOT_REQUIRED', 'Existing data operations require their actual frozen snapshot artifact before execution');
45
46
  }
@@ -96,7 +97,7 @@ function evidence(authority, result, sub) { const summary = result.timedOut ? 'A
96
97
  }
97
98
 
98
99
  export async function runApprovedPlan(input) { const owner = await authority(input); const comparison = await runnerDisciplineBefore(input, owner); const result = await runCommand(owner); try { const after = await authority(input); if (after.planFile.sha256 !== owner.planFile.sha256) fail('ENTROROVE_PLAN_CHANGED', 'plan changed during execution'); } catch (cause) { const execution = { schemaVersion: RUNNER_SCHEMA, status: 'failed', error: cause.message, process: result, evidence: [] }; const error = new Error(cause.message); error.code = cause.code; error.execution = execution; throw error; }
99
- owner.disciplineComparison = await runnerDisciplineAfter(comparison, owner);
100
+ owner.disciplineComparison = await runnerDisciplineAfter(comparison, owner, result);
100
101
  const sub = await subject(owner, result); const item = evidence(owner, result, sub); const status = !owner.signing && result.status === 'succeeded' ? 'incomplete' : result.status; return { schemaVersion: RUNNER_SCHEMA, status, planSha256: owner.planFile.sha256, approvalSha256: owner.approval.sha256, approvedBy: owner.approval.approval.approvedBy, subject: sub, process: result, evidence: [item], receipt: item.receipt ?? null, ...(owner.signing ? { receiptPublicKey: createPublicKey(owner.signing.privateKey).export({ format: 'der', type: 'spki' }).toString('base64') } : {}), signingBoundary: owner.signing ? 'configured-signature-only' : 'local-evidence-only' };
101
102
  }
102
103
 
@@ -125,7 +126,7 @@ export async function runApprovedPlanObject(input) {
125
126
  const error = new Error(cause.message); error.code = cause.code;
126
127
  error.execution = { schemaVersion: RUNNER_SCHEMA, status: 'failed', error: cause.message, process: result, evidence: [], effects: 'unknown' }; throw error;
127
128
  }
128
- owner.disciplineComparison = await runnerDisciplineAfter(comparison, owner);
129
+ owner.disciplineComparison = await runnerDisciplineAfter(comparison, owner, result);
129
130
  const sub = await subject(owner, result); const item = evidence(owner, result, sub);
130
131
  return { schemaVersion: RUNNER_SCHEMA, status: !owner.signing && result.status === 'succeeded' ? 'incomplete' : result.status,
131
132
  planSha256: owner.planFile.sha256, approvalSha256: owner.approval.sha256, approvedBy: owner.approval.approval.approvedBy,
@@ -12,11 +12,15 @@ export const DISCIPLINE_WRITE_SCHEMA = object({ targetPath: relativePath,
12
12
  }, ['targetPath', 'kind', 'reason', 'requirementRefs']);
13
13
  const approval = object({ interactionId: identifier, interactionDigest: shaSchema, auditId: identifier,
14
14
  proposalDigest: shaSchema }, ['interactionId', 'interactionDigest', 'auditId', 'proposalDigest']);
15
- export const DISCIPLINE_POLICY_SCHEMA = object({ schemaVersion: { const: 'entrorove.discipline-policy/1.0' },
15
+ const mutablePolicy = object({ schemaVersion: { const: 'entrorove.discipline-policy/1.0' },
16
16
  requirementIds: refs, writes: array(DISCIPLINE_WRITE_SCHEMA, 1), approval,
17
17
  dataOperations: array(object({ operation: { enum: ['delete', 'migrate', 'rebuild'] }, target: text,
18
18
  reason: text, requirementRefs: refs, snapshotArtifactDigest: shaSchema }, ['operation', 'target', 'reason', 'requirementRefs', 'snapshotArtifactDigest'])),
19
19
  }, ['schemaVersion', 'requirementIds', 'writes']);
20
+ const readOnlyPolicy = object({ schemaVersion: { const: 'entrorove.discipline-policy/1.0' },
21
+ access: { const: 'read-only' }, requirementIds: refs, writes: { type: 'array', maxItems: 0 },
22
+ }, ['schemaVersion', 'access', 'requirementIds', 'writes']);
23
+ export const DISCIPLINE_POLICY_SCHEMA = { oneOf: [mutablePolicy, readOnlyPolicy] };
20
24
  export const DISCIPLINE_DECLARATION_SCHEMA = object({ ...DISCIPLINE_WRITE_SCHEMA.properties,
21
25
  usageReviewDigest: shaSchema }, DISCIPLINE_WRITE_SCHEMA.required);
22
26
  const validate = new Ajv({ allErrors: true, strict: true }).compile(DISCIPLINE_POLICY_SCHEMA);
@@ -6,9 +6,9 @@ import path from 'node:path';
6
6
  export async function runnerDisciplineBefore(input, authority) {
7
7
  if (!authority.plan.policy.discipline) return null;
8
8
  const original = await disciplineRepositoryObservation(input, authority.root);
9
- return { input, original: original.comparison };
9
+ return { input, original: original.comparison, readOnly: authority.plan.policy.discipline.access === 'read-only' };
10
10
  }
11
- export async function runnerDisciplineAfter(before, authority) {
11
+ export async function runnerDisciplineAfter(before, authority, processResult) {
12
12
  if (!before) return null;
13
13
  const after = (await disciplineRepositoryObservation(before.input, authority.root)).comparison;
14
14
  const changed = disciplineRepositoryDifferences(before.original, after);
@@ -23,5 +23,10 @@ export async function runnerDisciplineAfter(before, authority) {
23
23
  catch (error) {
24
24
  if (error.code !== 'ENTROROVE_STATE_EXISTS' || canonical(await readMutationJson14(file)) !== canonical(record)) throw error;
25
25
  }
26
+ if (before.readOnly && changed.length) {
27
+ throw Object.assign(new Error('Explicit read-only execution changed repository bytes or permissions; no successful receipt is issued'),
28
+ { code: 'ENTROROVE_READ_ONLY_EXECUTION_DRIFT', execution: { schemaVersion: 'entrorove.validator-local-runner/1.0',
29
+ status: 'failed', process: processResult, evidence: [], effects: 'unknown', comparison: report } });
30
+ }
26
31
  return { ...report, reportDigest };
27
32
  }
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.skill-manifest/1.0",
3
3
  "name": "entrorove",
4
- "version": "0.1.55",
4
+ "version": "0.1.56",
5
5
  "endpoint": "https://entrorove.com",
6
6
  "requestSchemaVersion": "entrorove.skill.request/2.0",
7
7
  "supportedRequestSchemaVersions": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entrorove/skill",
3
- "version": "0.1.55",
3
+ "version": "0.1.56",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "entrorove": "cli.mjs"
@@ -275,5 +275,10 @@
275
275
  "tree-sitter-wasms": "0.1.13",
276
276
  "@vue/compiler-sfc": "3.5.43",
277
277
  "svelte": "5.57.1"
278
+ },
279
+ "repository": {
280
+ "type": "git",
281
+ "url": "git+https://github.com/88208555/entrorove.git",
282
+ "directory": "packages/entrorove-skill"
278
283
  }
279
284
  }
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.release-state/1.0",
3
- "version": "0.1.55",
4
- "digest": "9caa3f7a2e6f43718003b5a0608b77e8b3a635ed5a75f4303da57f4381693f61",
3
+ "version": "0.1.56",
4
+ "digest": "217c46f009d2c8513e5697b663e07e8fabe3b647728ce7c07bf20c49037c991e",
5
5
  "files": {
6
6
  "connected-cli.mjs": "260c0fc99586c24bd4d0351a14e0744849a612ebe6f1bd7951e4fce200daf920",
7
7
  "local-approved-process.mjs": "442856df788e63f38757439ba0657d1c689aaf797631a955fae3dd639e844e6d",
8
8
  "local-approved-supervisor.mjs": "87142a4c13d27c8265cc1fd7fc9088988da9bbb02134164e1a7c5417da926ac7",
9
9
  "local-mutation-consumption.mjs": "cd8650b9be894639ceb35af88d23c8002b4acff27afad2e64174eb21a9424cfb",
10
10
  "workflow-observation-client.mjs": "5e67bc32a8625de9523498f694655c378ae34af59c4aa2f0e07efcd72347dbc0",
11
- "workflow-observation-connection.mjs": "9235e9247b11e1d51628c1784eb1cb1e8e4b79d25ff791883722592dcf8583c0",
11
+ "workflow-observation-connection.mjs": "daf5f707dd93397e3581dc761131ef380026110b07f58fa9a2c9e738e68fa492",
12
12
  "workflow-observation-remote.mjs": "1e5a8b9c2b9b095242ad28a1d5ebce5944efdae74383fb20d6676b93b240c5a7",
13
13
  "workflow-observation-runner.mjs": "3eb8280ec7abffcb30398e3f4e5f7d0ec3df4385fa720f188ed83757b87cd977",
14
14
  "workflow-observation-state.mjs": "a525046df9b3de75b10d2bf35c2b6d7719f12a647e4aee1f1e85dbdbfb167321",
15
- "workflow-observation-wire.mjs": "846d0b48b7799bfca227e30faea025a5a919896538f963ce886cbdb4c75c0253",
15
+ "workflow-observation-wire.mjs": "9bff48377d240c2fd33ee82de6adcd97376b239d32d2552cfa1ce4f7e5e3562e",
16
16
  "local-preserved-mutation-protection.mjs": "95041d4f75253a153c7fd85573ba0457513ffe47a6a6bd00c4f59379b81ce580",
17
17
  "local-preserved-mutation-signer.mjs": "2b0771cbdd18ba00267aa441b7892cd04feab9f8543ae5e2703430b1e580747f",
18
18
  "local-preserved-mutation-state.mjs": "1ff7939d668e39ec641a7cf46a4ce3014c63f3898207cc874f21170376479671",
@@ -21,7 +21,7 @@
21
21
  "mutation-client.mjs": "8d27b267dc660510aa1406c44d4005e44b972a1260f90657de4fa0e75b7b6c3e",
22
22
  "cloud-mutation-authority.mjs": "c7226e882a4f3be4d2f55728f515d5a815c0aa1f21aa5e4689314e6e6a8c66a2",
23
23
  "ide-code-mode.mjs": "f8ba4de33632ab4c6df52dab52f392414b4632c7e94b218b8065677970c075c7",
24
- "ide-command.mjs": "134fa07e152f3966d4763f10eea33e69559a2271fefb453a1384396fd5f01333",
24
+ "ide-command.mjs": "3db45dd1d12db670e594b6ced35f2d7778cb01091695b1e240179b1ea34f25ee",
25
25
  "ide-hook.mjs": "68ca1f5805a49b78182f2508bdd5fac390080e9b2c2ba3645a9aaea69f9d4d0f",
26
26
  "ide-install.mjs": "10247efadcfeef5da001022d12080bd0f789e67fdf7862bcc65db73dda3bbe57",
27
27
  "ide-session.mjs": "6df04bca3231954e8b83f6b1b535befff16b6c62c51ca65ae3aa2559180ad7c2",
@@ -33,14 +33,14 @@
33
33
  "local-discipline-batch.mjs": "2cc90dbae55efdee0c445da399c0a9acafa92e915344c25208ebca554fc4c9e9",
34
34
  "local-typescript-function-reference.mjs": "af682b479fe0d69a5ecd62232406bb8078408ec8710733cfed9ae6b059762ba5",
35
35
  "local-scope-import-expressions.mjs": "20f1e065582c68d2334e0dfaa130a559ff19886a57f9038dc569f65993bb06c8",
36
- "local-discipline-contracts.mjs": "6cf8a3d132fdb9074eb1441600afe7aa199df33e88a71a660d9db534c14f19e4",
36
+ "local-discipline-contracts.mjs": "ab0c11dcc4a6ffd3cb79b53862c19308eaf0d159deee7f9958266852d977363e",
37
37
  "local-discipline-contracts.d.mts": "defc684c78dc88afa1c54340ab4a01ae36db7aa11257cbe1f5cd323f608c78c4",
38
38
  "local-discipline-gate.mjs": "e3851157bf1d8bc86eb44212a88acd20acfc2d1152e7d87af6dc2a37afffcd6f",
39
39
  "local-discipline-native.mjs": "8b99b5ce951cab3c1426428ad807650c61c0ffb2c346d747b73b99057b382049",
40
40
  "local-discipline-operations.mjs": "9fcb7b8889b7718d42e1d37936485d8198d5017d405e0cdbbabdbbd12fcc9d48",
41
41
  "local-discipline-repository.mjs": "b6392e02ecae333dfdc8df41f203fd69bed035d88b15b16d112ab1897b3c297c",
42
42
  "local-discipline-snapshot.mjs": "8fd35b2cfbece8f64579f144f589a66dec9b6022d50fe931c4c1f886cb83134d",
43
- "local-runner-discipline.mjs": "86f451de2f1e12a24f5596a29828891c1eba13cd4714ed0c18a77d66cecef934",
43
+ "local-runner-discipline.mjs": "8d4ada838f81d54963adbb7e433f1ca80ae1934ea126f90c59a49d29742979c4",
44
44
  "local-integrity.d.mts": "21357f306234bd841c0d3d90bf2d09f7663f63746f897b9f38190428ae8198b3",
45
45
  "local-equivalence-runner.mjs": "df2f406c693c4da7bb30542ec7b4d46a9336f56e9fdd263196c0f3a73d2f6e91",
46
46
  "validator-runtime-build.mjs": "da20bd19eb088d79d62e33969b4c075dc2199761b395e40060227f8f86b9519a",
@@ -48,7 +48,7 @@
48
48
  "runtime/validator-runtime-v7.0.44.d.mts": "7797edbfe2f7de24da370204dce4dfd22822a1bd80df93514fff900b98afe36f",
49
49
  "snapshot-retention.mjs": "4f8919fe8ec49fb4c23e080a8ad2fa3a9282bca3fffe7cf55c64e89ef316697e",
50
50
  "snapshot-cleanup-journal.mjs": "76a9684cc95b4eb076e1298d78a9dff37948b00d9607b8f546c599072d3ed331",
51
- "IDE-EXECUTION.md": "abe40303ab31637f5c69b0e5611a49fd579566a0bb95bd9fba1ebd39e7108fbd",
51
+ "IDE-EXECUTION.md": "7e312d8069b0f03deba896b26abfa6beab74ba08835aa58c47633f5c8eeb7d35",
52
52
  "REUSE-DISCIPLINE.md": "5b214cf313e7f715f52d54e28093a2d038f0f196ca64b15d1c49587b9d8eecf8",
53
53
  "reuse-discipline-release.mjs": "2935898d1aa807fb982a78d7ab5cb1abe05e3c8fc945dc0909da8098872aa1ac",
54
54
  "local-final-gate16.mjs": "083aec470670e6cdb7ad2ab27b7a51b53a9ec3a8b4121cb9cbfa17d9940892c2",
@@ -97,10 +97,10 @@
97
97
  "review-client.mjs": "3491eef68ed533174b31db3c53febb6519b8f088f71a9964644714a9a4c03c77",
98
98
  "version.mjs": "1f392147d72c40af4b03b307ea607075fb815af5174fa07e254b95fe12dbf3e7",
99
99
  "SKILL.md": "684438bcdfefef590e51e4ce02be0a244a26d142a24599258bf3ae24c86bbfa1",
100
- "CAPABILITY-MAP.md": "69b2c49b111d651969dea7430f4df7950d8fdf2f0f151d8386fa04968b872ac0",
100
+ "CAPABILITY-MAP.md": "9058ee785768b470f26641032dfdaca638e08473d3508ad36deb63f375b84afe",
101
101
  "AGREEMENT.md": "f93a68e92c457200dd024610e65f74e478b8f60860f951da818b70561f1b9bf8",
102
102
  "DELIVERY-BOUNDARIES.md": "9b9db50ed1bcce9e77ff6527b6bb7eaf12f3b913162ea9cb7fe90db06b393a4f",
103
- "package.json": "e910dc5cf0db61b1fcb9feffa21a8f8d3c2a244b7d858e8af72202f5b91d403b",
103
+ "package.json": "19e30a6adde349d335e9a0b4559799a5af6a1b7df7094344dafd411f6ed7f22f",
104
104
  "manifest.json": "ab06e307795bc9da5add926752b6c4dbdaf18625090261a035aa356a679c504f",
105
105
  "release.mjs": "8b0dc36c47aec72cf9952c9ac1ce5fe27da5ae6829f50cd2339fa101b2eda93c",
106
106
  "local-cli.mjs": "41e2a737c8587cb5ce469a7661f6d14b5ea4cd073f8286822fc4dc96b67f56a1",
@@ -123,7 +123,7 @@
123
123
  "local-chain-plan.mjs": "e45a9170f569c80f7e5532bbc109dbae3c24b696815316e503cb3779135be648",
124
124
  "local-chain-release-labels.mjs": "7ce5350d1062d5236f57f10e7e81713c1101463d5e7f252604ef0c2a30840bfe",
125
125
  "local-chain-results.mjs": "0455f72cad574cfef87c2c6c0928d6432bba7e5dbaff521a2b62a9575299ca10",
126
- "local-approved-plan.mjs": "26731b534f4c276c6235cb73bf6950943061947967a93f477f4bcd64acde72a6",
126
+ "local-approved-plan.mjs": "c2c88ab4085ff35a1811fe43e9a28699cfc9387eda54bfeafae3e7dd9e8cdb19",
127
127
  "local-budget-renewal.mjs": "6b436358066fe33b2f6d871fc90d8e7c551edd6255efd73c0d255d0fd35d5ef8",
128
128
  "local-probe.mjs": "9b948db1de1e34c7605c4c454cd4aced66d2b57d2cf4471adbb80bcc76dee635",
129
129
  "local-budget-grants.mjs": "c5f6542f5d2c3bfe07f3ca5b5bc849bf05d886b06014f720322d530510775d3d",
@@ -201,7 +201,7 @@
201
201
  "local-task-handoff-read-authority.mjs": "7d2991cfac4562039232566d25441aa98106ce27bc9b7bcf6df4ff28d738e167",
202
202
  "local-task-handoff-write-archive.mjs": "3668f39d8ab5664ed717994cf1b4bcc7ea23e146a0e06291f851d30337faeb77",
203
203
  "local-task-handoff-write-receipts.mjs": "e1321127bd6fcad7840e185639f81ae68a8722a0ee4264a7b504a02b27887647",
204
- "workflow-local-runner.mjs": "9fa899a11da2b6b7a436bf9b561641856adcbc5f8b914f6ba89a64dae224900d",
204
+ "workflow-local-runner.mjs": "c74ccd425fd7d4882388338e27f3b25e85c39c1a6cb7935435393844ec13c001",
205
205
  "workflow-local-contracts.mjs": "bc52a461727b34c72daa058e1aa77d58206eafad14a93a347537f9969ae51184",
206
206
  "workflow-local-client.mjs": "45c954f694d96bf0ea87ee2dc6902a6763dac2cb1df713b6a5e749632054e9af",
207
207
  "workflow-local-request.mjs": "4e24c543ce386292a87dc9cd41f90d23375f12079cacffc515c8cd01fd571780",
package/releases.json CHANGED
@@ -1,4 +1,10 @@
1
1
  [
2
+ {
3
+ "version": "0.1.56",
4
+ "date": "2026-10-17",
5
+ "zh": "补齐原工作流显式只读步骤及无写入结算的本地观察,复核真实受检仓库字节与权限;修复流程列表空输入兼容,返回精确规划前置条件与范围错误,并配置 GitHub OIDC 发布。",
6
+ "en": "Added explicit read-only workflow steps and local observations without fabricated mutation settlements, checking actual repository bytes and modes; fixed empty-input flow listing, precise planning prerequisite and scope errors, and GitHub OIDC publishing metadata."
7
+ },
2
8
  {
3
9
  "version": "0.1.55",
4
10
  "date": "2026-10-17",
@@ -286,7 +286,7 @@ async function prepare(input, create = true) {
286
286
  const root = await realDirectory(input.repositoryRoot, 'ENTROROVE_ROOT_INVALID');
287
287
  const home = await realDirectory(input.home ?? os.homedir(), 'ENTROROVE_HOME_INVALID');
288
288
  const directory = await privateHierarchy(home, ['.entrorove', 'workflow-local', sha(root).slice(0, 32), plan.flowId, step.stepId], create);
289
- return { root, plan, lease, step, file: path.join(directory, 'state.json') };
289
+ return { root, home, plan, lease, step, file: path.join(directory, 'state.json') };
290
290
  }
291
291
 
292
292
  export async function observeWorkflowLocalStep(rawInput) {
@@ -323,7 +323,7 @@ export async function runWorkflowLocalStep(rawInput) {
323
323
  });
324
324
  const signer = await loadSigner(authority.root, input.signerConfigPath, authority.plan.runnerKeyId);
325
325
  if (typeof approvedRunner.runApprovedPlanObject !== 'function') fail('ENTROROVE_WORKFLOW_RUNNER_UNAVAILABLE', 'approved object plan runner is unavailable');
326
- execution = await approvedRunner.runApprovedPlanObject({ repositoryRoot: authority.root, executionPlan: authority.step.executionPlan,
326
+ execution = await approvedRunner.runApprovedPlanObject({ repositoryRoot: authority.root, home: authority.home, flowId: authority.plan.flowId, executionPlan: authority.step.executionPlan,
327
327
  approvalPath: input.approvalPath, signerConfigPath: input.signerConfigPath, executionDeadlineAt: authority.lease.expiresAt });
328
328
  return await finalize(authority, signedResult(authority, execution, signer));
329
329
  } catch (error) {
@@ -7,7 +7,8 @@ import { runWorkflowObservation } from './workflow-observation-runner.mjs';
7
7
  import { observationRemote, authorizeObservation } from './workflow-observation-remote.mjs';
8
8
 
9
9
  async function connect(input, state, location, dependencies) {
10
- const original = Object.fromEntries(['taskId', 'flowId', 'stepId', 'leaseId', 'generation', 'settlementId'].map(key => [key, input[key]]));
10
+ const fields = ['taskId', 'flowId', 'stepId', 'leaseId', 'generation', ...(Object.hasOwn(input, 'settlementId') ? ['settlementId'] : [])];
11
+ const original = Object.fromEntries(fields.map(key => [key, input[key]]));
11
12
  const admitted = state.admitted ?? await observationRemote('admitted', 'member-observation-admit', original, state, location, dependencies);
12
13
  const admission = observationAdmission(admitted.admission, input, { allowExpired: Boolean(state.result) });
13
14
  await authorizeObservation(admission, dependencies, !state.result);
@@ -3,14 +3,16 @@ import { canonical, sha256 } from './local-integrity.mjs';
3
3
  import { mutationId14, mutationPaths14 } from './local-mutation-files.mjs';
4
4
 
5
5
  export const observationFail = message => { throw Object.assign(new Error(message), { code: 'ENTROROVE_LOCAL_OBSERVATION' }); };
6
- export const observationIdentityFields = ['taskId', 'flowId', 'stepId', 'leaseId', 'generation', 'settlementId'];
6
+ export const observationReadOnlyIdentityFields = ['taskId', 'flowId', 'stepId', 'leaseId', 'generation'];
7
+ export const observationIdentityFields = [...observationReadOnlyIdentityFields, 'settlementId'];
7
8
  export function observationExact(value, fields) {
8
9
  if (!value || typeof value !== 'object' || Array.isArray(value)
9
10
  || canonical(Object.keys(value).sort()) !== canonical([...fields].sort())) observationFail('Local observation fields are invalid');
10
11
  }
11
12
  export function observationInput(input) {
12
- observationExact(input, observationIdentityFields);
13
- for (const field of observationIdentityFields.filter(field => field !== 'generation')) mutationId14(input[field], field);
13
+ const fields = Object.hasOwn(input ?? {}, 'settlementId') ? observationIdentityFields : observationReadOnlyIdentityFields;
14
+ observationExact(input, fields);
15
+ for (const field of fields.filter(field => field !== 'generation')) mutationId14(input[field], field);
14
16
  if (!Number.isSafeInteger(input.generation) || input.generation < 1) observationFail('Local observation generation is invalid');
15
17
  return input;
16
18
  }
@@ -50,18 +52,23 @@ function signature(value) {
50
52
  } catch { observationFail('Local observation requires its original pinned public cloud admission signature'); }
51
53
  }
52
54
  export function observationAdmission(value, input, { allowExpired = false } = {}) {
53
- observationExact(value, [...observationIdentityFields, 'schemaVersion', 'purpose', 'observationId', 'ownerId', 'taskDigest',
54
- 'chainDigest', 'epoch', 'planId', 'planDigest', 'executionPlanSha256', 'repositoryDigest', 'settlementDigest', 'command',
55
+ const readOnly = !Object.hasOwn(input, 'settlementId');
56
+ const fields = readOnly ? observationReadOnlyIdentityFields : observationIdentityFields;
57
+ observationExact(value, [...fields, 'schemaVersion', 'purpose', 'observationId', 'ownerId', 'taskDigest',
58
+ 'chainDigest', 'epoch', 'planId', 'planDigest', 'executionPlanSha256', 'repositoryDigest',
59
+ ...(readOnly ? ['sourceKind'] : ['settlementDigest']), 'command',
55
60
  'sourceManifest', 'protectedFiles', 'issuedAt', 'expiresAt', 'authorityReceipt']);
56
61
  signature(value);
57
62
  const start = Date.parse(value.issuedAt), end = Date.parse(value.expiresAt);
58
63
  if (value.schemaVersion !== 'entrorove.local-observation-admission/1.0' || value.purpose !== 'observe-approved-command'
59
- || value.epoch !== 0 || observationIdentityFields.some(field => value[field] !== input[field])
64
+ || value.epoch !== 0 || fields.some(field => value[field] !== input[field])
65
+ || readOnly && value.sourceKind !== 'original-read-only-plan'
60
66
  || value.repositoryDigest !== sha256(input.repositoryRoot).slice(0, 32) || !Number.isFinite(start) || !Number.isFinite(end)
61
67
  || start > Date.now() || end <= start || end - start > 600_000 || !allowExpired && end <= Date.now()
62
68
  || !/^[a-f0-9-]{36}$/.test(value.ownerId)
63
- || ['taskDigest', 'chainDigest', 'planDigest', 'executionPlanSha256', 'settlementDigest'].some(field => !/^[a-f0-9]{64}$/.test(value[field]))) {
64
- observationFail('Local observation admission is expired or lost its original task, repository or settlement');
69
+ || ['taskDigest', 'chainDigest', 'planDigest', 'executionPlanSha256', ...(readOnly ? [] : ['settlementDigest'])]
70
+ .some(field => !/^[a-f0-9]{64}$/.test(value[field]))) {
71
+ observationFail('Local observation admission is expired or lost its original task, repository or source binding');
65
72
  }
66
73
  mutationId14(value.observationId, 'observationId'); mutationId14(value.planId, 'planId');
67
74
  command(value.command); files(value.sourceManifest, true); files(value.protectedFiles, false);