@entrorove/skill 0.1.70 → 0.1.73

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 (42) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/IDE-EXECUTION.md +4 -0
  3. package/SKILL.md +3 -2
  4. package/local-budget-reads.mjs +7 -2
  5. package/local-budget.mjs +3 -2
  6. package/local-probe.mjs +16 -7
  7. package/local-scope-build-data.mjs +73 -0
  8. package/local-scope-build-facts.mjs +33 -0
  9. package/local-scope-build-io.mjs +57 -0
  10. package/local-scope-build-read.mjs +22 -0
  11. package/local-scope-build-readset.mjs +49 -0
  12. package/local-scope-files.mjs +20 -7
  13. package/local-scope-repository.mjs +47 -37
  14. package/local-scope-signature.mjs +6 -4
  15. package/local-scope-state.mjs +7 -5
  16. package/local-scope-uni-build.mjs +69 -119
  17. package/local-scope-uni-conditional.mjs +8 -6
  18. package/local-scope-uni-directives.mjs +5 -5
  19. package/local-scope-uni-expressions.mjs +10 -5
  20. package/local-scope-workspaces.mjs +3 -3
  21. package/manifest.json +1 -1
  22. package/package.json +12 -2
  23. package/release-source-groups.mjs +46 -0
  24. package/release-state.json +40 -30
  25. package/release.mjs +6 -40
  26. package/releases.json +18 -0
  27. package/remote-client.mjs +1 -1
  28. package/remote-transport.mjs +14 -0
  29. package/scope-amendment-client.mjs +31 -19
  30. package/scope-amendment-reference.mjs +22 -4
  31. package/scope-amendment-state.mjs +1 -1
  32. package/scope-context-client.mjs +17 -8
  33. package/scope-context-journal.mjs +5 -4
  34. package/scope-context-observer.mjs +3 -2
  35. package/scope-context-remote.mjs +5 -5
  36. package/scope-context-state.mjs +4 -1
  37. package/scope-context-wire.mjs +9 -1
  38. package/scope-evidence-local.mjs +52 -0
  39. package/scope-evidence-remote.mjs +78 -0
  40. package/scope-evidence-wire.mjs +120 -0
  41. package/scope-runner-client.mjs +65 -35
  42. package/scope-runner-journal.mjs +32 -0
@@ -12,14 +12,19 @@ export function uniConditionalError(filename, offset, reason, source = '') {
12
12
  return Object.assign(new Error(`${filename}:${line}:${column}: ${reason}`), {
13
13
  code: 'ENTROROVE_IMPORT_UNSUPPORTED', path: filename, line, column, loc: { start: { offset } } });
14
14
  }
15
+ export function uniConditionalLimitError(filename, offset, reason, source = '') {
16
+ return Object.assign(uniConditionalError(filename, offset, reason, source), { code: 'ENTROROVE_SCOPE_LIMIT' });
17
+ }
18
+ const limit = (fail, reason) => Object.assign(fail(reason), { code: 'ENTROROVE_SCOPE_LIMIT' });
15
19
  export function uniCondition(expression, fail) {
16
- if (expression.length > 512) throw fail('Conditional expression exceeds its 512-character bound');
20
+ if (expression.length > 512) throw limit(fail, 'Conditional expression exceeds its 512-character bound');
17
21
  const tokens = expression.match(/uniVersion|[A-Za-z_][A-Za-z0-9_-]*|\d+(?:\.\d+)?|\|\||&&|>=|<=|===|!==|==|!=|[!()<>]|\S/g) ?? [];
18
- if (!tokens.length || tokens.length > 128) throw fail('Conditional expression requires a bounded supported expression');
22
+ if (!tokens.length) throw fail('Conditional expression requires a supported expression');
23
+ if (tokens.length > 128) throw limit(fail, 'Conditional expression exceeds its 128-token bound');
19
24
  let cursor = 0, depth = 0;
20
25
  const versions = [], macros = new Set();
21
26
  const atom = () => {
22
- if (++depth > 24) throw fail('Conditional expression exceeds its nesting bound');
27
+ if (++depth > 24) throw limit(fail, 'Conditional expression exceeds its nesting bound');
23
28
  let result; const token = tokens[cursor++];
24
29
  if (token === '!') result = { kind: 'not', value: atom() };
25
30
  else if (token === '(') { result = disjunction(); if (tokens[cursor++] !== ')') throw fail('Conditional parentheses are not balanced'); }
@@ -62,7 +67,7 @@ export function evaluateUniCondition(node, context) {
62
67
  }
63
68
  function versionSamples(conditions, fail) {
64
69
  const values = [...new Set(conditions.flatMap(condition => condition.versions))].sort((a, b) => a - b);
65
- if (values.length > 8) throw fail('Conditional versions exceed their eight-boundary inspection limit');
70
+ if (values.length > 8) throw limit(fail, 'Conditional versions exceed their eight-boundary inspection limit');
66
71
  const samples = new Set([0, ...values]);
67
72
  for (let index = 0; index < values.length; index++) {
68
73
  const previous = values[index - 1] ?? 0;
@@ -94,7 +99,7 @@ export function uniConditionContexts(conditions, fail) {
94
99
  const key = conditions.map(condition => evaluateUniCondition(condition.tree, context) ? '1' : '0').join('');
95
100
  if (!seen.has(key)) {
96
101
  seen.add(key); output.push({ ...context, key });
97
- if (output.length > 64) throw fail('Conditional compilation exceeds its 64-variant inspection bound');
102
+ if (output.length > 64) throw limit(fail, 'Conditional compilation exceeds its 64-variant inspection bound');
98
103
  }
99
104
  }
100
105
  return output;
@@ -1,11 +1,11 @@
1
1
  import path from 'node:path';
2
2
  import { promises as fs } from 'node:fs';
3
- import { scopeFail, scopePath, scopeRead, scopeTarget } from './local-scope-files.mjs';
3
+ import { scopeFail, scopePath, scopeBuildSourceRead, scopeTarget } from './local-scope-files.mjs';
4
4
  import { observeScopeDirectory } from './local-scope-read-set.mjs';
5
5
 
6
6
  async function manifest(root, relative, input) {
7
7
  try {
8
- const read = await scopeRead(root, relative, input);
8
+ const read = await scopeBuildSourceRead(root, relative, input);
9
9
  return { value: JSON.parse(read.content.toString('utf8')), path: relative, sha256: read.sha256 };
10
10
  } catch (error) {
11
11
  if (error.code === 'ENOENT') return null;
@@ -20,7 +20,7 @@ async function patterns(root, input) {
20
20
  if (!Array.isArray(result)) result = result.packages;
21
21
  if (!Array.isArray(result) || result.some(pattern => typeof pattern !== 'string')) scopeFail('ENTROROVE_PACKAGE_INVALID', 'workspace patterns must be a literal string array');
22
22
  try {
23
- const read = await scopeRead(root, 'pnpm-workspace.yaml', input); const yaml = read.content.toString('utf8'); let active = false;
23
+ const read = await scopeBuildSourceRead(root, 'pnpm-workspace.yaml', input); const yaml = read.content.toString('utf8'); let active = false;
24
24
  metadata.push({ path: 'pnpm-workspace.yaml', sha256: read.sha256 });
25
25
  for (const line of yaml.split('\n')) {
26
26
  if (/^packages:\s*$/.test(line)) { active = true; continue; }
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.70",
4
+ "version": "0.1.73",
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.70",
3
+ "version": "0.1.73",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "entrorove": "cli.mjs"
@@ -319,7 +319,17 @@
319
319
  "local-scope-uni-directives.mjs",
320
320
  "local-scope-uni-conditional.mjs",
321
321
  "local-scope-uni-build.mjs",
322
- "local-scope-read-set.mjs"
322
+ "local-scope-read-set.mjs",
323
+ "release-source-groups.mjs",
324
+ "local-scope-build-read.mjs",
325
+ "local-scope-build-io.mjs",
326
+ "local-scope-build-facts.mjs",
327
+ "local-scope-build-data.mjs",
328
+ "local-scope-build-readset.mjs",
329
+ "scope-evidence-wire.mjs",
330
+ "scope-evidence-local.mjs",
331
+ "scope-evidence-remote.mjs",
332
+ "scope-runner-journal.mjs"
323
333
  ],
324
334
  "scripts": {
325
335
  "test": "node test/run.mjs",
@@ -0,0 +1,46 @@
1
+ export const swarmSourceFiles = Object.freeze([
2
+ 'local-swarm-authority.mjs',
3
+ 'local-swarm-baseline.mjs',
4
+ 'local-swarm-completion.mjs',
5
+ 'local-swarm-contracts.mjs',
6
+ 'local-swarm-mutation.mjs',
7
+ 'local-swarm-priority.mjs',
8
+ 'local-swarm-state.mjs',
9
+ 'local-swarm-storage.mjs',
10
+ 'local-swarm.mjs',
11
+ ])
12
+ export const coordination17SourceFiles = Object.freeze([
13
+ 'local-autocoord17-authority.mjs',
14
+ 'local-autocoord17-controller.mjs',
15
+ 'local-autocoord17-peers.mjs',
16
+ 'local-autocoord17-store.mjs',
17
+ 'local-autocoord17-tasks.mjs',
18
+ 'local-autocoord17-waits.mjs',
19
+ 'local-validator-isolation17.mjs',
20
+ 'local-calctool-composition17.mjs',
21
+ ])
22
+ export const preservedMutationSourceFiles = Object.freeze([
23
+ 'local-preserved-mutation-protection.mjs', 'local-preserved-mutation-signer.mjs',
24
+ 'local-preserved-mutation-state.mjs', 'local-preserved-mutation-wire.mjs',
25
+ 'local-delegated-modify-analysis.mjs', 'mutation-client.mjs', 'cloud-mutation-authority.mjs',
26
+ ])
27
+ export const memberContinuationSourceFiles = Object.freeze([
28
+ 'connected-cli.mjs',
29
+ 'local-approved-process.mjs',
30
+ 'local-approved-supervisor.mjs',
31
+ 'local-mutation-consumption.mjs',
32
+ 'workflow-local-acceptance.mjs',
33
+ 'workflow-observation-client.mjs',
34
+ 'workflow-observation-connection.mjs',
35
+ 'workflow-observation-remote.mjs',
36
+ 'workflow-observation-runner.mjs',
37
+ 'workflow-observation-state.mjs',
38
+ 'workflow-observation-wire.mjs',
39
+ ])
40
+ export const scopeEvidenceSourceFiles = Object.freeze([
41
+ 'scope-evidence-wire.mjs', 'scope-evidence-local.mjs', 'scope-evidence-remote.mjs', 'scope-runner-journal.mjs',
42
+ ])
43
+ export const scopeEvidenceReleaseNote = {
44
+ zh: '将完整范围证据与签名控制分离:四类原 S 证明经有界独立通道上传,紧凑权威绑定不可变摘要与字节数;未知上传仅恢复原 GET,保留原路径、上下文、审批、独立 M 写入和全部旧失败记录。',
45
+ en: 'Separate full scope evidence from signed controls: upload four kinds of original S proof through a bounded data channel and bind immutable hashes and byte counts in compact authorities. Recover uncertain uploads with the original GET only, preserving complete paths, contexts, approvals, separate M writes and prior failures.',
46
+ }
@@ -1,15 +1,25 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.release-state/1.0",
3
- "version": "0.1.70",
4
- "digest": "888f8c2a60595e87e39b9423f811a0e231ec5309094f3de0d88cb3f729371ba8",
3
+ "version": "0.1.73",
4
+ "digest": "06693e8422b76b4037a70c0be657104b918234d74fa4c5dd1e29a394d5c83e0e",
5
5
  "files": {
6
- "scope-context-client.mjs": "dbfec7db0e118f3025a0e45475f3328a171e4cd1dd86e6c8d326d779ce61b459",
7
- "scope-context-observer.mjs": "9d041db8de6e45113ee04b189c9d52097ec902a104721829dd382cf6daf5d1ce",
6
+ "release-source-groups.mjs": "876b8c8bcf459d3b469bf4f2021d4206de5b199add545597a5c8f6dea7385b38",
7
+ "local-scope-build-read.mjs": "361d4b461cfa2c77b4ec22bc59a0a1c78fed2cfb0ea2e05a8da5c2551ecd88ac",
8
+ "local-scope-build-io.mjs": "8af69722738fa094780f9a2dfcddaf12993fc3b936a75c56c4b0d4f90cb20fd1",
9
+ "local-scope-build-facts.mjs": "8b0bd19347d06d1cb66d8cd2d6784103af9d00ea691d7f4acea523d994b874fb",
10
+ "local-scope-build-data.mjs": "b6e272ca4385629326b4517a53408c561272f5f1a01e5b701f17eaa4c5e64b2e",
11
+ "local-scope-build-readset.mjs": "fd1cb1470c54b3902a4612056281746e947cf96d1f5c03a09ea0becbcd79cc45",
12
+ "scope-evidence-wire.mjs": "d283bbfd002b034e1deb539633b4970b430a97065388288ab3398e2bc0285633",
13
+ "scope-evidence-local.mjs": "25b22ba2cc5a17978d2422e290ee015299f05d639b4b4b9551001afefab55c0d",
14
+ "scope-evidence-remote.mjs": "27077bb8238f3d11e6e18453e2ea8a6e096a104bbee5ee43a82b8c099d253c1e",
15
+ "scope-runner-journal.mjs": "cddb2f20a2812e8bcc3268d8ebdd486c22d0c7f5740e958d0b35cd2cc8819f92",
16
+ "scope-context-client.mjs": "115d82022f71396bab137e9af06421a6d0aa9671838f0df64e6aa83d9d7fb039",
17
+ "scope-context-observer.mjs": "25a84339512c1e80711aca53baed20da556fa2ae5d84e83fa95d58dcdca7cf7d",
8
18
  "scope-context-observation.mjs": "d92f2fca3c923caa6f0c5c9cc60b4f12bdae212071da64da6692e3e99b251969",
9
- "scope-context-remote.mjs": "b52cdee03e845edc15eae6bb3ccfaea9c4482c03f2fae7001439e4abcb2acd33",
10
- "scope-context-state.mjs": "7bc434620bfc505ee290f1977e64ef22e50f72665d75e7dfd6805deab4e81826",
11
- "scope-context-wire.mjs": "a15d0b966615b8293e53e4229edc8fb444fb144cc336dbd6354ac995f33e8cc8",
12
- "scope-context-journal.mjs": "753821f546e36185b2e891e8f38758a2d15a591f700f0650d478892c8d958a45",
19
+ "scope-context-remote.mjs": "cbcef3fb0b9e1cf11b68f877c227ac9b0c617432221dc6e661bce9bc4667b87a",
20
+ "scope-context-state.mjs": "dc47da57e411ef11677560a767f2e6d50f8a2fae17706b44715d8cd115ffcf8e",
21
+ "scope-context-wire.mjs": "1aa8c86223e54927a209de59d3514393b4c905ee2fc520d49819aec0f1e97162",
22
+ "scope-context-journal.mjs": "0d9aa1315204d3600f62075fe9a4b0b958b21a04c3a50a743eb5dce1eb16e18a",
13
23
  "scope-context-release.mjs": "3b6f5d3bf7abe25e1a1edd0521053f4c1b1e3487b7f37d5f7c6080d32bc0c044",
14
24
  "cloud-locale.mjs": "0627ac75b98b439563b61ab217aacb55a099c1d0c8ed3b118776d2007c52dd70",
15
25
  "cloud-locale.d.mts": "aff4bced9bbac140f73fd0de24681287349389be5750d89d96abe0f8a2167664",
@@ -76,7 +86,7 @@
76
86
  "runtime/validator-runtime-v7.0.44.d.mts": "7797edbfe2f7de24da370204dce4dfd22822a1bd80df93514fff900b98afe36f",
77
87
  "snapshot-retention.mjs": "ca46aeb36b1a5a24fe7e967031a71b9a3d33f40cb715d344402cbe0dc9c425cb",
78
88
  "snapshot-cleanup-journal.mjs": "76a9684cc95b4eb076e1298d78a9dff37948b00d9607b8f546c599072d3ed331",
79
- "IDE-EXECUTION.md": "26d4f32de1694c7f29ceefc5a5e5f4bba717efc5f91c37a91c399faa1574a191",
89
+ "IDE-EXECUTION.md": "3da7cbfafc1a4e7d4059c6fe8e69d14dacd2c72e300fe8e056581faea7f23aeb",
80
90
  "REUSE-DISCIPLINE.md": "a2452911c2b50c4762b4a1374f1958059eb03efd59f63e1b50e1650c52b0dc98",
81
91
  "install-identity.mjs": "41b8c8d8b3e265d2057a07bf7dc98066c34d6b05c65bf7159ca9609c01ee4646",
82
92
  "package-archive.mjs": "e06db8d709db8710bead254ee950b78928088be01c58dcea383beaa5eb574e59",
@@ -152,13 +162,13 @@
152
162
  "install-cleanup.mjs": "8a8e1a7dfe37427f86cd4d6e317890d0829894a9949d4216674a716b36edbc54",
153
163
  "review-client.mjs": "3491eef68ed533174b31db3c53febb6519b8f088f71a9964644714a9a4c03c77",
154
164
  "version.mjs": "85284d4cd5e3314b449e6ae124243f96397bcc274817b5983a79cfdd903d46fd",
155
- "SKILL.md": "db0ff568f83086226044726c37a0f8de517042aece8fb1676f1830c9e32f8d79",
165
+ "SKILL.md": "1c1509924526711c92df7fd0600e4165353cc602663d83b225e083676501bba3",
156
166
  "CAPABILITY-MAP.md": "517809bb20750e5de89b2dab5705c902a27fa68cf4f3f392a5d8cf2f19818888",
157
167
  "AGREEMENT.md": "f93a68e92c457200dd024610e65f74e478b8f60860f951da818b70561f1b9bf8",
158
168
  "DELIVERY-BOUNDARIES.md": "cb23ce71b2197085c8441ba7c5b6c05fb21f2a954bf66b4d60cfef8b15895b8b",
159
- "package.json": "71c16c55000a8d0d7cca2b935ceba15799b3e8d15be2081a7e077ce803d7b319",
169
+ "package.json": "9a3bb032185a679b7b0146cc6bd1b46ed44e59d16cbab236c5e90fd4f167ad7c",
160
170
  "manifest.json": "ab06e307795bc9da5add926752b6c4dbdaf18625090261a035aa356a679c504f",
161
- "release.mjs": "2250561b382b61aa1f760705c872d7248266a69a96cc30e8957f42e9e48649e5",
171
+ "release.mjs": "c3a5af873db43872e5a6bdc16eb3d859e8d69f6b21bd1c3b754f4a2b6c305360",
162
172
  "local-cli.mjs": "058ac9a4653bc12e0d031b72034ebfe6a72b91b9b23353b9fee04881c7d2e098",
163
173
  "local-state-lock.mjs": "1dbf64264642a0851faf043e07ee9f66d2ad0508e9f799b0626246e42fae893e",
164
174
  "local-execution.mjs": "721ed7d29bbe4ce6d088909cd84ffc440978c3c9eb0946c49a4281ee685fe3aa",
@@ -181,28 +191,28 @@
181
191
  "local-chain-results.mjs": "0455f72cad574cfef87c2c6c0928d6432bba7e5dbaff521a2b62a9575299ca10",
182
192
  "local-approved-plan.mjs": "c2c88ab4085ff35a1811fe43e9a28699cfc9387eda54bfeafae3e7dd9e8cdb19",
183
193
  "local-budget-renewal.mjs": "6b436358066fe33b2f6d871fc90d8e7c551edd6255efd73c0d255d0fd35d5ef8",
184
- "local-probe.mjs": "9b948db1de1e34c7605c4c454cd4aced66d2b57d2cf4471adbb80bcc76dee635",
194
+ "local-probe.mjs": "398f5dd4973503ccad280e0224e4f567c9763d21555a0816afdd83c75987d33a",
185
195
  "local-budget-grants.mjs": "c5f6542f5d2c3bfe07f3ca5b5bc849bf05d886b06014f720322d530510775d3d",
186
196
  "local-budget-handoff.mjs": "85a8f006dad9c0153843c7b7748ee7fcd756fdba159c97c3905d7fe0c4f604af",
187
197
  "local-budget-handoff-contracts.mjs": "bb53ebf135ec7ecfbecee858cfb354f03fee9cfb3940fa5d967d45008bf9aac5",
188
198
  "local-budget-lock.mjs": "ab0722630e1792843a94a6d01e79422cd578fe9e4d423b59d63e636770381236",
189
199
  "local-budget-handoff-completion.mjs": "f8bc45df7c18a064d0b5aac7338131deb709da5193bfe9a1d20300585ea9acb6",
190
- "local-budget-reads.mjs": "9baa12cbebe84a8abf1e55cbf02eb5414540973bde804d96609e914169bef5b1",
200
+ "local-budget-reads.mjs": "91eab0bf12a057709a6d5685c5602debe5299a58fbd6624f6e0d0f3876dc918b",
191
201
  "local-budget-source-bindings.mjs": "cfe97b257fd2da0dd45f95f5e45be20baa6445d11ac6d03538a0105b4985b8ff",
192
202
  "local-budget-store.mjs": "2ae36c977ecd32d81f88e2eaa697ce8c228cb4405ecc5c4169cdda104c8ce138",
193
203
  "local-budget-transition.mjs": "3894f05d4b5d4d6e16e3f201ff8902e6311cf6434010262934e0d1251aa8b9f0",
194
- "local-budget.mjs": "857bf879addbfbc13bd95d0957b223e05e73a69b8092f1d1933f09ff7968659a",
204
+ "local-budget.mjs": "378348e583d6889a731679e1427e645f56f6cb7c547d48d300e00b9e10050516",
195
205
  "local-flow-lifecycle.mjs": "47534e16599002e5e7cf7d3e95195f4d7a364d2b651ec339d8c6c017d71c1b05",
196
206
  "local-stage-boundary.mjs": "84c3bb5beab29306274c5ca8a78f19a83ed664ae1b93a8372bb493158b304fe3",
197
207
  "local-scope-candidate.mjs": "8f0bd9606e70d4a0ebacdd3cd600ec3afed85e2bb3e1760aa1144cf55d4e028a",
198
208
  "local-scope-context.mjs": "79753abfc6c76089d17f003a1be537dfaea10d6731c542f7fc53f2a9996ed164",
199
- "local-scope-files.mjs": "4793c2a1b458346bd9099e74bbfa0623024c423e754f65ae9e01ca388b49fd36",
200
- "local-scope-repository.mjs": "1e0c1aef43929237226145d3ba776facaa7e3fc89eb8f88af0a2e5ab38bcdde9",
209
+ "local-scope-files.mjs": "b8c98b8ca8bb4953f3d2d9ae1fa71b2a54286032c9dd660d3061b584e77778cb",
210
+ "local-scope-repository.mjs": "fe5187824744233b9fce59d7b964a3f24349a3fe31415181654a1b3a70e8de9c",
201
211
  "local-scope-read-set.mjs": "a5137d2e7ecffe669143fb51f0f3fba7526a881b512269858ea1e3504d9a50b2",
202
- "local-scope-uni-expressions.mjs": "200f3f5284125c2674d9e797a7550e7bb7700932db237e1df2d21c3a209ac924",
203
- "local-scope-uni-directives.mjs": "9951af9f98de5299bad636a09cd7f4e129c96d20939650375f7f677f7919e0c6",
204
- "local-scope-uni-conditional.mjs": "8a4d03939e1daf8d257b10b44b3808b84404433113c39eff32c8643d7f9f7b18",
205
- "local-scope-uni-build.mjs": "9cc45336ad6807a73c6e65d2b23f0c6606e22a423f8105710e532b50f76896c5",
212
+ "local-scope-uni-expressions.mjs": "626247c87a4fd5f6162e548aa907d3f983d5a77d341e640b6f42a539f0c84405",
213
+ "local-scope-uni-directives.mjs": "e546be317962e30a3547aea376db9a7b8d7641055daf9e3dce4a9ef55cd872af",
214
+ "local-scope-uni-conditional.mjs": "f143a142af069279fcac0a01206744ce323c8dfc4496c2f6cc1f0b229c4e5f5e",
215
+ "local-scope-uni-build.mjs": "1dafd4802859b4c53504395f6186b003ee2657fc16f373ca2637e438e4d1b9e5",
206
216
  "local-scope-create-require.mjs": "6b6a209bfcad8fe1398e99c5c8cc3af189c120b9c38e244a73dd1a99c3d91295",
207
217
  "local-scope-require-resolution.mjs": "b24596462c8776cbefa6f0a74749d665aa928d1643c68d44798eb93e2dd5ad42",
208
218
  "local-scope-git.mjs": "53bbbb6c4795db711b5bc164591a6ad79a561427da046d6bbe997dae6eb82829",
@@ -228,23 +238,23 @@
228
238
  "local-budget-git-system-attributes.mjs": "367a4e5a88490c4d4c3717bc893a271970fe70d7705f0c1c72d6f76a442d5d21",
229
239
  "runtime/git-native-linux-v1.mjs": "cf5cb91426a105e62c0f07a8a230dec592dba46f85a3f708b4c4e8cfcd1126c6",
230
240
  "runtime/git-native-darwin-v1.mjs": "f88aab72dc86f94eb1115784872957cb8ed0f74205ae70e1ecd1939e7790e310",
231
- "local-scope-signature.mjs": "5aba6422031873946874307175b5a6f1fa609faa8160e1a1bb8db44c02c58d18",
241
+ "local-scope-signature.mjs": "3804b84d5ddca5c1ac991b31151d84cddc508517eff7500351ba1bcffdf3c672",
232
242
  "local-scope-config-imports.mjs": "a399407227bbdd14468633bb6d25586df7c6a30a1ef0d1a80ab7c7b499d3fadf",
233
243
  "local-scope-config-extends.mjs": "3ab4150b846b247e9e0851221123f220c5574699af651830e751f026ab33659e",
234
244
  "local-scope-snapshot.mjs": "c0f851470b98863a1d187825ed61ee6072d1c057b178fdc85ae29af02a3b250f",
235
- "local-scope-state.mjs": "8b600a91fc0068d7a8c0bb752ddba7ed30ada93daafb118d6cba2368fd94cb27",
236
- "local-scope-workspaces.mjs": "7484905ab0434e1a98f95038c65003ae48e395e9cfae07a4322f481537a08392",
245
+ "local-scope-state.mjs": "7a6d275d6267a4d0bb55582e1bcc7654c502ecba9221b84b2149a98f0ee7518f",
246
+ "local-scope-workspaces.mjs": "8a75ce7b6639b5682370b5738c5c2a90408e3130729608e008f03053cd50351f",
237
247
  "local-task-routing.mjs": "6e2e506b2fb0e2e8d8c5147d7df32849b57a4e94a23634f01a355d45735858e4",
238
- "remote-client.mjs": "d30fd61fab3171a9e41546706a57ae372786d20ea9e9c01cad39d9863c156b40",
248
+ "remote-client.mjs": "6cf62706f8ac5f69368d951cd697f912040b2e58ac480dfb151bddb18b09b20d",
239
249
  "cloud-task-client.mjs": "f9085c5e6af6f179282670d2e872b85bfabac2657d3d232b1246dc23f5a6ccdd",
240
- "scope-runner-client.mjs": "b85999bca05efd804325f97b6e5704623ac3fcf751c6f9760717a23f40661ad1",
241
- "scope-amendment-client.mjs": "64a38deddc2dbfae543fead2cff8c9c78771b576391cc903aa58db925a138ad7",
242
- "scope-amendment-state.mjs": "922c4fd5a71101651d40fdb11f94f09c904cc244ff2574ae6b6ee004448aad06",
250
+ "scope-runner-client.mjs": "cc4fe202d5f777a0ec4c46f9b41a18aaf024c0d2e769a0b26bc6505f2f69f460",
251
+ "scope-amendment-client.mjs": "022ec88ecf12a98dd6e9e7915158fc9111cdadfda12cf714804948c8027a57d3",
252
+ "scope-amendment-state.mjs": "9f88cc7029f62eed3b1a0cc1c03b6a98c5713b467a2112278692de90c973b2be",
243
253
  "scope-amendment-wire.mjs": "3c4823e23bbdf330616bb675f07f9fcad7423266bd50b92e1134d226060e9b83",
244
254
  "scope-amendment-remote.mjs": "15a0b3126248f3101a52e3d7742f9a816dd3d8e58f21bac33e99e810e86cc4fd",
245
255
  "scope-amendment-observer.mjs": "de796f2aca2f3bc7f33ae3323b07d98096c0f16b1ffde70e3055610d70d18020",
246
256
  "scope-amendment-mutation.mjs": "fa24191df290efad29d41eca3d1a081981fefde02b7dc4a11c75c6118a69106e",
247
- "scope-amendment-reference.mjs": "0537e53a1bea4b695f4804c5051261778c88f05b1ae4aff86c709dcfe83aa148",
257
+ "scope-amendment-reference.mjs": "dc1768131beb7db224dd2ff8522dbf6361f73e01f466c9334cdfa24118720078",
248
258
  "native-client.mjs": "6f5575909bae9daba643287f876ffa0f9ff1c53a367b32a40145d740456d6605",
249
259
  "native-notification.mjs": "8859a26c6f13d222711f763acf49ed8546b3306cf71f990643ac31a5ccaf1ba1",
250
260
  "local-peer-status.mjs": "a971bcc69d60bb32b875ca5bb4297d948631d5d2821b812c279613ea059a8b4f",
@@ -255,7 +265,7 @@
255
265
  "task-entry-client.mjs": "940fb068afe28a2a34fe96e285e149afdd9ba79649c0c65cde37ec4ed71ae39d",
256
266
  "task-identity.mjs": "e0e6cf1f48bfa5ee3c5cb86a3c21adfce6d66b4220f4a164ca099263c7a09fb4",
257
267
  "task-identity.d.mts": "55472e1ffc81b9d5e5c5bd56c0bf1d18ecde03ee5170a58a15ce9caf222d8761",
258
- "remote-transport.mjs": "cf0e31a0319518b6c7e1ce2ac99766a98fe9c936d26bbbec05952aaf0c33efb0",
268
+ "remote-transport.mjs": "e9ecb3b9cd4d3bcc7abe1f7097069a07a15319ca0d5641fee752d094eb039194",
259
269
  "local-credential.mjs": "2a302a7f67b5ea8dc73453fc96ab33db349b59ca11fa9456e48ca1d9669d877c",
260
270
  "local-invocations.mjs": "f6ac4c41c1a5ec34de358c4cca210ca8e403b3a913150a43bb55ec9be55d9998",
261
271
  "local-task-handoff-fence.mjs": "5532a359ee29b885f7ce0cf7178f76dba0fc9245bdd92ef460982275baf0d557",
package/release.mjs CHANGED
@@ -9,47 +9,10 @@ import { fileURLToPath } from 'node:url'
9
9
  import { chainReleaseLabels } from './local-chain-release-labels.mjs'
10
10
 
11
11
  const directory = path.dirname(fileURLToPath(import.meta.url))
12
- const swarmSourceFiles = Object.freeze([
13
- 'local-swarm-authority.mjs',
14
- 'local-swarm-baseline.mjs',
15
- 'local-swarm-completion.mjs',
16
- 'local-swarm-contracts.mjs',
17
- 'local-swarm-mutation.mjs',
18
- 'local-swarm-priority.mjs',
19
- 'local-swarm-state.mjs',
20
- 'local-swarm-storage.mjs',
21
- 'local-swarm.mjs',
22
- ])
23
- const coordination17SourceFiles = Object.freeze([
24
- 'local-autocoord17-authority.mjs',
25
- 'local-autocoord17-controller.mjs',
26
- 'local-autocoord17-peers.mjs',
27
- 'local-autocoord17-store.mjs',
28
- 'local-autocoord17-tasks.mjs',
29
- 'local-autocoord17-waits.mjs',
30
- 'local-validator-isolation17.mjs',
31
- 'local-calctool-composition17.mjs',
32
- ])
33
- const preservedMutationSourceFiles = Object.freeze([
34
- 'local-preserved-mutation-protection.mjs', 'local-preserved-mutation-signer.mjs',
35
- 'local-preserved-mutation-state.mjs', 'local-preserved-mutation-wire.mjs',
36
- 'local-delegated-modify-analysis.mjs', 'mutation-client.mjs', 'cloud-mutation-authority.mjs',
37
- ])
38
- const memberContinuationSourceFiles = Object.freeze([
39
- 'connected-cli.mjs',
40
- 'local-approved-process.mjs',
41
- 'local-approved-supervisor.mjs',
42
- 'local-mutation-consumption.mjs',
43
- 'workflow-local-acceptance.mjs',
44
- 'workflow-observation-client.mjs',
45
- 'workflow-observation-connection.mjs',
46
- 'workflow-observation-remote.mjs',
47
- 'workflow-observation-runner.mjs',
48
- 'workflow-observation-state.mjs',
49
- 'workflow-observation-wire.mjs',
50
- ])
12
+ import { swarmSourceFiles, coordination17SourceFiles, preservedMutationSourceFiles, memberContinuationSourceFiles, scopeEvidenceSourceFiles, scopeEvidenceReleaseNote } from './release-source-groups.mjs'
51
13
  const sourceFiles = [
52
- ...SCOPE_CONTEXT_SOURCE_FILES, 'cloud-locale.mjs', 'cloud-locale.d.mts', 'cloud-task-images.mjs', 'model-images.mjs', 'model-images.d.mts', 'cli-input.mjs',
14
+ 'release-source-groups.mjs', 'local-scope-build-read.mjs', 'local-scope-build-io.mjs', 'local-scope-build-facts.mjs', 'local-scope-build-data.mjs', 'local-scope-build-readset.mjs',
15
+ ...scopeEvidenceSourceFiles, ...SCOPE_CONTEXT_SOURCE_FILES, 'cloud-locale.mjs', 'cloud-locale.d.mts', 'cloud-task-images.mjs', 'model-images.mjs', 'model-images.d.mts', 'cli-input.mjs',
53
16
  'scope-amendment-retirement.mjs', 'scope-amendment-consumption.mjs', 'mutation-connection-state.mjs', 'mutation-challenge-refresh.mjs',
54
17
  'local-scope-font.mjs', 'local-scope-zip.mjs', 'member-producer-repair-authority.mjs',
55
18
  'local-mutation-completion-evidence.mjs', 'local-mutation-environment-observation.mjs', 'local-mutation-finder-observation.mjs', 'local-finder-store.mjs', 'local-finder-plist.mjs',
@@ -310,6 +273,7 @@ function releaseNote(changed) {
310
273
  if (suppliedZh && suppliedEn) return {
311
274
  zh: suppliedZh.replaceAll(/\s+/g, ' '), en: suppliedEn.replaceAll(/\s+/g, ' '),
312
275
  }
276
+ if (changed.some(file => scopeEvidenceSourceFiles.includes(file))) return scopeEvidenceReleaseNote
313
277
  if (changed.some(file => SCOPE_CONTEXT_SOURCE_FILES.includes(file))) return scopeContextReleaseNote
314
278
  if (changed.some(file => file.startsWith('scope-amendment-'))) return {
315
279
  zh: '新增同一任务的精确范围补正:原账号审核文件哈希与写入/只读角色,原S签名真实事实并重评原合同;计划、写入和结算绑定不可变范围修订,改后刷新保留原M消费证据与失败历史。',
@@ -342,6 +306,8 @@ const archguardReleaseLabels = {
342
306
  }
343
307
  function releaseLabels() {
344
308
  return withFinalGate16ReleaseLabels({ ...designGateReleaseLabels, ...reuseDisciplineReleaseLabels, ...calctoolReleaseLabels, ...mutationReleaseLabels, ...swarmReleaseLabels, ...coordination17ReleaseLabels, ...nativeReadReleaseLabels, ...feedbackReleaseLabels, ...invocationReleaseLabels, ...chainReleaseLabels, ...taskEntryReleaseLabels, ...archguardReleaseLabels,
309
+ ...Object.fromEntries(['local-scope-build-read.mjs', 'local-scope-build-io.mjs', 'local-scope-build-facts.mjs', 'local-scope-build-data.mjs', 'local-scope-build-readset.mjs', 'local-scope-uni-build.mjs', 'local-scope-uni-expressions.mjs', 'local-scope-uni-directives.mjs', 'local-scope-uni-conditional.mjs'].map(name => [name, { zh: '源码事实与未知构建行为分离', en: 'raw source facts and opaque build observation' }])),
310
+ 'release-source-groups.mjs': { zh: '自动版本源文件清单', en: 'automatic version source inventory' },
345
311
  ...Object.fromEntries(['cloud-task-images.mjs', 'model-images.mjs', 'model-images.d.mts', 'cli-input.mjs'].map(name => [name, { zh: '受限图片输入通道', en: 'bounded image input channel' }])),
346
312
  ...Object.fromEntries(['scope-amendment-retirement.mjs', 'scope-amendment-consumption.mjs', 'mutation-connection-state.mjs', 'mutation-challenge-refresh.mjs', 'member-producer-repair-authority.mjs'].map(name => [name, { zh: '原授权恢复与请求生命周期', en: 'original-authority recovery and request lifecycle' }])),
347
313
  ...Object.fromEntries(['local-scope-font.mjs', 'local-scope-zip.mjs'].map(name => [name, { zh: '字体与 ZIP 完整结构核验', en: 'font and ZIP structural verification' }])),
package/releases.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "version": "0.1.73",
4
+ "date": "2026-10-20",
5
+ "zh": "修复通用范围连接在长时间扫描后的签名期限绑定:原 S 证明到期时间不晚于原云端挑战,保留原随机数、完整范围、独立验签和失败历史;已过期挑战仍明确拒绝。",
6
+ "en": "Bind ordinary scope signatures to their original cloud deadline after long scans. The original S proof expires no later than its challenge, preserving the nonce, complete scope, independent signatures and failure history; expired challenges still reject explicitly."
7
+ },
8
+ {
9
+ "version": "0.1.72",
10
+ "date": "2026-10-20",
11
+ "zh": "将完整范围证据与签名控制分离:四类原 S 证明经有界独立通道上传,紧凑权威绑定不可变摘要与字节数;未知上传仅恢复原 GET,保留原路径、上下文、审批、独立 M 写入和全部旧失败记录。",
12
+ "en": "Separate full scope evidence from signed controls: upload four kinds of original S proof through a bounded data channel and bind immutable hashes and byte counts in compact authorities. Recover uncertain uploads with the original GET only, preserving complete paths, contexts, approvals, separate M writes and prior failures."
13
+ },
14
+ {
15
+ "version": "0.1.71",
16
+ "date": "2026-10-20",
17
+ "zh": "通用分离源码依赖事实与未知构建行为:动态配置、宏及加载器保留真实哈希与只读来源,不执行项目代码,不把构建未知标为完整。原审批与独立 S/M 签名仅允许已知源码的非结构修改;隐私文件、路径、资源预算、未知目标及配置漂移继续明确拒绝,实际构建与测试仍需原流程验收。",
18
+ "en": "Separate raw source dependency facts from opaque build behavior across projects. Preserve actual hashes and read-only origins for dynamic configuration, macros and loaders without executing project code or claiming compiled completeness. Original approvals and separate S/M signatures permit only known-source nonstructural modifications; private files, unsafe paths, resource budgets, unknown targets and configuration drift remain explicit refusals. Actual builds and tests still require original-flow acceptance."
19
+ },
2
20
  {
3
21
  "version": "0.1.70",
4
22
  "date": "2026-10-20",
package/remote-client.mjs CHANGED
@@ -39,7 +39,7 @@ export function buildInvocation(operation, input) {
39
39
  'message-status', 'message-delivery-start', 'message-delivery-report', 'message-resolve', 'message-complete',
40
40
  'handoff-prepare', 'handoff-release', 'handoff-claim', 'handoff-status', 'handoff-resume', 'handoff-execution-authority', 'mutation-snapshot-challenge', 'mutation-pass-issue', 'mutation-pass-settle',
41
41
  'member-observation-admit', 'member-observation-report', 'member-observation-status',
42
- 'scope-runner-prepare', 'scope-runner-register', 'scope-runner-challenge', 'scope-runner-attest',
42
+ 'scope-runner-prepare', 'scope-runner-register', 'scope-runner-challenge', 'scope-runner-attest', 'scope-evidence-prepare',
43
43
  'scope-runner-context-review', 'scope-runner-context-status', 'scope-runner-context-challenge', 'scope-runner-context-apply',
44
44
  'start', 'continue'].includes(operation)) throw new Error('Invalid entrorove operation');
45
45
  if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('Input must be a JSON object');
@@ -108,6 +108,20 @@ export async function requestTransport(origin, pathname, options = {}) {
108
108
  }
109
109
  }
110
110
 
111
+ // Evidence is a separate bounded data channel. A persisted upload reservation
112
+ // is attempted once; every uncertain outcome must use its original GET.
113
+ export async function putScopeEvidence(origin, uploadId, body, headers = {}) {
114
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(uploadId ?? '') || !Buffer.isBuffer(body)
115
+ || body.length < 1 || body.length > 16_000_000) throw new Error('Invalid Entrorove scope evidence upload');
116
+ const url = new URL(`/api/skill/evidence/${uploadId}`, trustedOrigin(origin));
117
+ const response = await attempt(url, { method: 'PUT', body, timeoutMs: 30_000,
118
+ headers: { ...headers, 'content-type': 'application/json; charset=utf-8', 'content-length': body.length, 'accept-encoding': 'gzip' } });
119
+ if (response.status >= 300 && response.status < 400) {
120
+ throw Object.assign(new Error('Entrorove API redirects are refused'), { code: 'ENTROROVE_REDIRECT', established: true });
121
+ }
122
+ return { ...response, attempts: 1 };
123
+ }
124
+
111
125
  export function parseTransportResponse(response, label = 'Entrorove') {
112
126
  try { return JSON.parse(response.body); }
113
127
  catch { throw Object.assign(new Error(`Invalid ${label} response`), { code: 'ENTROROVE_RESPONSE_INVALID' }); }
@@ -9,6 +9,7 @@ import { scopeAmendmentLocation, readScopeAmendmentState, saveScopeAmendmentStat
9
9
  import { amendmentRemote, scopeAmendmentStatus } from './scope-amendment-remote.mjs';
10
10
  import { observeScopeAmendment } from './scope-amendment-observer.mjs';
11
11
  import { retainScopeAmendmentReference } from './scope-amendment-reference.mjs';
12
+ import { saveLocalScopeEvidence, ensureScopeEvidence, validateAppliedEvidence } from './scope-evidence-remote.mjs';
12
13
 
13
14
  const schemaVersion = 'entrorove.scope-amendment-connection/1.0';
14
15
  function validateProposal(status, input) {
@@ -17,8 +18,9 @@ function validateProposal(status, input) {
17
18
  || !['pending', 'approved', 'declined'].includes(proposal.approval)) amendmentFail('Current account has no matching original scope proposal');
18
19
  return proposal;
19
20
  }
20
- function validateApplied(output, state, location) {
21
- const effective = output.effectiveAuthority, record = scopeRevisionAuthority(effective?.record, 'entrorove.scope-revision-authority/1.0');
21
+ async function validateApplied(output, state, location) {
22
+ const effective = output.effectiveAuthority, version = effective?.record?.schemaVersion === 'entrorove.scope-revision-authority/2.0' ? '2.0' : '1.0';
23
+ const record = scopeRevisionAuthority(effective?.record, `entrorove.scope-revision-authority/${version}`);
22
24
  const challenge = scopeAmendmentChallenge(state.challenged.challenge, location, { allowExpired: true }), refresh = challenge.purpose === 'scope-revision-refresh';
23
25
  if (output.status !== 'succeeded' || output.grantsExecutionAuthority !== false || output.claimsCompletion !== false
24
26
  || output.scopeRevisionDigest !== sha256(canonical(record)) || effective.revisionDigest !== output.scopeRevisionDigest
@@ -30,21 +32,26 @@ function validateApplied(output, state, location) {
30
32
  || canonical(record.files.map(file => ({ ...file, sha256: null }))) !== canonical(challenge.files.map(file => ({ ...file, sha256: null })))) {
31
33
  amendmentFail('Applied scope amendment lost its signed original authority or exact roles');
32
34
  }
33
- const fact = refresh ? scopeRevisionAuthority(effective.fact, 'entrorove.scope-revision-facts/1.0') : record;
35
+ const factVersion = effective?.fact?.schemaVersion === 'entrorove.scope-revision-facts/2.0' ? '2.0' : '1.0';
36
+ const fact = refresh ? scopeRevisionAuthority(effective.fact, `entrorove.scope-revision-facts/${factVersion}`) : record;
37
+ const compact = fact.schemaVersion.endsWith('/2.0');
38
+ const attestation = compact ? await validateAppliedEvidence(fact, state, location, challenge,
39
+ refresh ? 'scope-refresh' : 'scope-revision') : state.attestation;
34
40
  if (refresh && (fact.scopeRevisionDigest !== output.scopeRevisionDigest || fact.factRevision !== challenge.expectedFactRevision
35
41
  || ['flowId', 'ownerId', 'taskDigest', 'chainDigest'].some(field => fact[field] !== challenge[field]))
36
- || canonical(fact.candidateAttestation) !== canonical(state.attestation)
37
- || fact.scope?.attestationDigest !== sha256(canonical(state.attestation.scopeAttestation))
42
+ || !compact && canonical(fact.candidateAttestation) !== canonical(attestation)
43
+ || fact.scope?.attestationDigest !== sha256(canonical(attestation.scopeAttestation))
44
+ || compact && effective.candidateAttestationDigest !== sha256(canonical(attestation))
38
45
  || effective.factRevision !== (refresh ? fact.factRevision : 0) || output.factRevision !== effective.factRevision) {
39
46
  amendmentFail('Applied scope proof differs from this exact actual signed candidate');
40
47
  }
41
48
  return { schemaVersion, status: 'succeeded', flowId: challenge.flowId, proposalId: challenge.proposalId,
42
49
  proposalDigest: challenge.proposalDigest, scopeRevisionDigest: output.scopeRevisionDigest, revision: output.revision,
43
50
  factRevision: effective.factRevision, grantsExecutionAuthority: false, claimsCompletion: false,
44
- ...(refresh && state.attestation.memberMutation ? { localAction: {
51
+ ...(refresh && attestation.memberMutation ? { localAction: {
45
52
  command: 'npx @entrorove/skill mutation-settle <absolute repositoryRoot>', input: {
46
- flowId: challenge.flowId, taskId: state.attestation.memberMutation.consumptionReceipt.taskId,
47
- stepId: state.attestation.memberMutation.consumptionReceipt.stepId, gatePassPath: location.identity.gatePassPath } } } : {}),
53
+ flowId: challenge.flowId, taskId: attestation.memberMutation.consumptionReceipt.taskId,
54
+ stepId: attestation.memberMutation.consumptionReceipt.stepId, gatePassPath: location.identity.gatePassPath } } } : {}),
48
55
  nextStep: output.nextStep, ...(state.feedback ? { feedback: state.feedback } : {}) };
49
56
  }
50
57
  async function archiveAttempt(location, state) {
@@ -56,23 +63,28 @@ async function archiveAttempt(location, state) {
56
63
  await saveScopeAmendmentState(location, state);
57
64
  }
58
65
  async function finish(location, state, dependencies) {
59
- if (!state.attestation) {
60
- state.attestation = await observeScopeAmendment(location, state);
61
- state.attestationDigest = sha256(canonical(state.attestation)); await saveScopeAmendmentState(location, state);
66
+ if (!state.evidenceDescriptor && !state.applied) {
67
+ const attestation = state.attestation ?? await observeScopeAmendment(location, state);
68
+ const challenge = scopeAmendmentChallenge(state.challenged.challenge, location, { allowExpired: true });
69
+ await saveLocalScopeEvidence(attestation, challenge.purpose === 'scope-revision-refresh' ? 'scope-refresh' : 'scope-revision',
70
+ challenge, state, location, saveScopeAmendmentState);
71
+ }
72
+ if (!state.applied) {
73
+ const attestationRef = await ensureScopeEvidence(state, location, dependencies, state.challenged.challenge, saveScopeAmendmentState);
74
+ await amendmentRemote('applied', 'workflow-scope-apply', { proposalId: location.identity.proposalId,
75
+ proposalDigest: location.identity.proposalDigest, attestationRef }, state, location, dependencies);
62
76
  }
63
- const applied = state.applied ?? await amendmentRemote('applied', 'workflow-scope-apply', {
64
- proposalId: location.identity.proposalId, proposalDigest: location.identity.proposalDigest,
65
- attestation: state.attestation }, state, location, dependencies);
66
- const result = validateApplied(applied, state, location);
77
+ const applied = state.applied, result = await validateApplied(applied, state, location);
67
78
  await retainScopeAmendmentReference(location, applied); return result;
68
79
  }
69
80
  async function amend(location, dependencies) {
70
81
  const state = await readScopeAmendmentState(location);
82
+ if (state.evidencePending || state.pending?.operation === 'scope-evidence-prepare') return finish(location, state, dependencies);
71
83
  if (state.pending) {
72
84
  const pending = state.pending;
73
85
  await amendmentRemote(pending.stage, pending.input.operation, pending.input.payload, state, location, dependencies);
74
86
  if (pending.stage === 'applied') {
75
- const result = validateApplied(state.applied, state, location);
87
+ const result = await validateApplied(state.applied, state, location);
76
88
  await retainScopeAmendmentReference(location, state.applied); return result;
77
89
  }
78
90
  return finish(location, state, dependencies);
@@ -95,7 +107,7 @@ async function amend(location, dependencies) {
95
107
  const challenge = scopeAmendmentChallenge(challenged.challenge, location);
96
108
  if (status.effective ? challenge.purpose !== 'scope-revision-refresh'
97
109
  || challenge.authorityRevisionDigest !== status.effective.revisionDigest || challenge.expectedFactRevision !== status.effective.factRevision + 1
98
- || challenge.currentScopeArtifactDigest !== sha256(canonical(status.effective.scope))
110
+ || challenge.currentScopeArtifactDigest !== (status.effective.scopeArtifactDigest ?? sha256(canonical(status.effective.scope)))
99
111
  : challenge.purpose !== 'scope-revision' || challenge.expectedRevision !== proposal.expectedRevision
100
112
  || challenge.previousDigest !== proposal.previousDigest) amendmentFail('Scope amendment challenge has a different original authority head');
101
113
  return finish(location, state, dependencies);
@@ -115,9 +127,9 @@ export async function runScopeAmend(repositoryRoot, input, dependencies) {
115
127
  message: ['ENTROROVE_SCOPE_AMENDMENT_BINDING', 'ENTROROVE_SCOPE_AMENDMENT_STATE', 'ENTROROVE_SCOPE_AMENDMENT_UNKNOWN',
116
128
  'ENTROROVE_SCOPE_AMENDMENT_NOT_SENT', 'ENTROROVE_SCOPE_AMENDMENT_MUTATION_PENDING'].includes(error?.code) ? error.message
117
129
  : 'Scope amendment stopped; preserve the original proposal, signer, journal and actual evidence.',
118
- ...(requestId ? { requestId } : {}), ...(error?.output ? { output: error.output } : {}),
130
+ ...(requestId ? { requestId } : {}), ...(error?.uploadId ? { uploadId: error.uploadId } : {}), ...(error?.output ? { output: error.output } : {}),
119
131
  ...(error?.feedback ? { feedback: error.feedback } : {}),
120
- ...(error?.outcomeUnknown ? { outcomeUnknown: true, nextAction: { operation: 'scope-amend', requestId,
132
+ ...(error?.outcomeUnknown ? { outcomeUnknown: true, nextAction: { operation: 'scope-amend', ...(requestId ? { requestId } : { uploadId: error.uploadId }),
121
133
  method: 'GET', reuseOriginalInput: true, repeatPost: false } } : {}) };
122
134
  }
123
135
  }
@@ -1,7 +1,10 @@
1
1
  import path from 'node:path';
2
2
  import { canonical, sha256 } from './local-integrity.mjs';
3
- import { mutationHome14, privateMutationDirectory14, readMutationJson14, atomicMutationJson14 } from './local-mutation-files.mjs';
3
+ import { mutationHome14, mutationFile14, privateMutationDirectory14, readMutationJson14, atomicMutationJson14 } from './local-mutation-files.mjs';
4
4
  import { scopeRevisionAuthority, amendmentFail } from './scope-amendment-wire.mjs';
5
+ import { readScopeEvidence } from './scope-evidence-local.mjs';
6
+ import { evidenceReference } from './scope-evidence-wire.mjs';
7
+ import { trustedOrigin } from './remote-transport.mjs';
5
8
 
6
9
  async function referenceLocation(root, home, flowId, revisionDigest) {
7
10
  const stateRoot = path.join(await mutationHome14(home), '.entrorove');
@@ -11,21 +14,36 @@ async function referenceLocation(root, home, flowId, revisionDigest) {
11
14
  export async function retainScopeAmendmentReference(location, output) {
12
15
  const effective = output.effectiveAuthority, target = await referenceLocation(location.root, location.home,
13
16
  location.identity.flowId, output.scopeRevisionDigest);
14
- const value = { schemaVersion: 'entrorove.scope-amendment-reference/1.0', input: {
17
+ const compact = effective.record.schemaVersion === 'entrorove.scope-revision-authority/2.0';
18
+ if (compact) await readScopeEvidence(location, evidenceReference(effective.record.candidateAttestationRef, undefined, location));
19
+ const value = { schemaVersion: compact ? 'entrorove.scope-amendment-reference/2.0' : 'entrorove.scope-amendment-reference/1.0',
20
+ ...(compact ? { apiOrigin: location.apiOrigin } : {}), input: {
15
21
  flowId: location.identity.flowId, proposalId: location.identity.proposalId, proposalDigest: location.identity.proposalDigest }, record: effective.record };
16
22
  await privateMutationDirectory14(target.directory, { privateFrom: target.stateRoot });
17
23
  try { if (canonical(await readMutationJson14(target.file)) !== canonical(value)) amendmentFail('Scope amendment original authority reference changed'); }
18
24
  catch (error) { if (error.code !== 'ENOENT') throw error; await atomicMutationJson14(target.file, value, { privateFrom: target.stateRoot }); }
19
25
  }
26
+ async function readReferenceEvidence(reference, input) {
27
+ const origin = trustedOrigin(reference.apiOrigin), stateRoot = path.join(await mutationHome14(input.home), '.entrorove');
28
+ const base = path.join(stateRoot, 'scope-runners', sha256(`${origin}:${reference.record.flowId}:${input.repositoryRoot}`));
29
+ const file = path.join(base, 'identity', 'binding.json'), item = await mutationFile14(file);
30
+ if (item.stat.size > 1_000_000 || item.stat.nlink !== 1) amendmentFail('Original amendment signer binding is invalid');
31
+ const binding = await readMutationJson14(file);
32
+ if (binding.repositoryRoot !== input.repositoryRoot || binding.apiOrigin !== origin || binding.flowId !== reference.record.flowId) amendmentFail('Original amendment signer binding changed');
33
+ const location = { base, binding };
34
+ await readScopeEvidence(location, evidenceReference(reference.record.candidateAttestationRef, undefined, location));
35
+ }
20
36
  export async function scopeAmendmentRefreshAction(input, pass) {
21
37
  if (!pass.scopeRevisionDigest) return undefined;
22
38
  try {
23
39
  const target = await referenceLocation(input.repositoryRoot, input.home, pass.flowId, pass.scopeRevisionDigest);
24
40
  const reference = await readMutationJson14(target.file);
25
- const record = scopeRevisionAuthority(reference.record, 'entrorove.scope-revision-authority/1.0');
26
- if (reference.schemaVersion !== 'entrorove.scope-amendment-reference/1.0' || sha256(canonical(record)) !== pass.scopeRevisionDigest
41
+ const compact = reference.schemaVersion === 'entrorove.scope-amendment-reference/2.0';
42
+ const record = scopeRevisionAuthority(reference.record, `entrorove.scope-revision-authority/${compact ? '2.0' : '1.0'}`);
43
+ if (!['entrorove.scope-amendment-reference/1.0', 'entrorove.scope-amendment-reference/2.0'].includes(reference.schemaVersion) || sha256(canonical(record)) !== pass.scopeRevisionDigest
27
44
  || record.flowId !== pass.flowId || record.taskDigest !== pass.taskDigest || record.scope.keyId !== pass.memberScopeKeyId
28
45
  || ['flowId', 'proposalId', 'proposalDigest'].some(field => reference.input[field] !== record[field])) amendmentFail('Stored refresh reference has another original authority');
46
+ if (compact) await readReferenceEvidence(reference, input);
29
47
  return { command: 'npx @entrorove/skill scope-amend <absolute repositoryRoot>',
30
48
  input: { ...reference.input, gatePassPath: input.gatePassPath }, resumeOriginalFlow: true, requiredBefore: 'mutation-settle' };
31
49
  } catch {
@@ -80,7 +80,7 @@ export async function scopeAmendmentLocation(repositoryRoot, input, dependencies
80
80
  const directory = path.join(base, 'amendments', sha256(canonical(input)));
81
81
  await privateMutationDirectory14(directory, { privateFrom: stateRoot });
82
82
  const contextAuthority = await boundScopeContexts(base, binding, dependencies);
83
- return { root, home, stateRoot, directory, file: path.join(directory, 'journal.json'), identity,
83
+ return { root, home, apiOrigin, base, stateRoot, directory, file: path.join(directory, 'journal.json'), identity,
84
84
  binding, key, signerConfigPath, original, contextAuthority };
85
85
  }
86
86
  export async function readScopeAmendmentState(location) {