@entrorove/skill 0.1.64 → 0.1.67

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 (52) hide show
  1. package/CAPABILITY-MAP.md +23 -0
  2. package/CHANGELOG.md +15 -0
  3. package/DELIVERY-BOUNDARIES.md +2 -0
  4. package/DESIGN-GATES.md +160 -0
  5. package/REUSE-DISCIPLINE.md +15 -1
  6. package/SKILL.md +4 -2
  7. package/blueprint-criterion-runner.mjs +1 -1
  8. package/cli.mjs +11 -4
  9. package/design-browser-checks.mjs +86 -0
  10. package/design-browser-identity.mjs +13 -0
  11. package/design-browser-state.mjs +41 -0
  12. package/design-browser.mjs +181 -0
  13. package/design-context.mjs +34 -0
  14. package/design-contracts.d.mts +4 -0
  15. package/design-contracts.mjs +77 -0
  16. package/design-declaration.mjs +17 -0
  17. package/design-frameworks.mjs +4 -0
  18. package/design-gate.mjs +19 -0
  19. package/design-lint.mjs +115 -0
  20. package/design-local-contracts.mjs +15 -0
  21. package/design-operations.mjs +81 -0
  22. package/design-policy.d.mts +5 -0
  23. package/design-policy.mjs +137 -0
  24. package/design-release.mjs +25 -0
  25. package/design-research.mjs +99 -0
  26. package/design-runner.mjs +31 -0
  27. package/design-store.mjs +116 -0
  28. package/design-ui.mjs +24 -0
  29. package/install-identity.mjs +16 -0
  30. package/local-cli.mjs +3 -1
  31. package/local-contracts.mjs +2 -0
  32. package/local-discipline-actions.mjs +5 -0
  33. package/local-discipline-batch.mjs +12 -3
  34. package/local-discipline-contracts.mjs +15 -3
  35. package/local-discipline-gate.mjs +9 -2
  36. package/local-error.mjs +18 -1
  37. package/local-execution.mjs +2 -1
  38. package/local-mutation-authority.mjs +2 -0
  39. package/local-runner-discipline.mjs +17 -3
  40. package/local-scope-zip.mjs +21 -0
  41. package/manifest.json +1 -1
  42. package/package-archive.mjs +54 -0
  43. package/package-replacement.mjs +48 -0
  44. package/package.json +34 -6
  45. package/release-state.json +50 -24
  46. package/release.mjs +3 -1
  47. package/releases.json +18 -0
  48. package/reuse-discipline-release.mjs +3 -1
  49. package/rollback-history.mjs +51 -0
  50. package/rollback-runner.mjs +77 -0
  51. package/version.mjs +20 -3
  52. package/workflow-local-runner.mjs +9 -0
@@ -6,6 +6,9 @@ import { disciplineRepositoryObservation, assertDisciplineRepository } from './l
6
6
  import { readDisciplineSnapshot } from './local-discipline-snapshot.mjs';
7
7
  import { readSnapshotCopy14 } from './local-mutation-snapshot.mjs';
8
8
  import { assertDelegatedSourceModification } from './local-delegated-modify-analysis.mjs';
9
+ import { assertDesignWrite } from './design-gate.mjs';
10
+ import { assertPackageReplacement } from './package-replacement.mjs';
11
+ import { assertRollbackCandidate } from './rollback-history.mjs';
9
12
 
10
13
  const needs = message => mutationFail14('ENTROROVE_DISCIPLINE_NEEDS_HUMAN', message);
11
14
  function declaredWrite(input, pass, baseline) {
@@ -71,7 +74,7 @@ function checkReuse(review, candidate, target, intent, parallelPaths) {
71
74
  empty: duplicates.length === 0, complete: review.scanComplete };
72
75
  }
73
76
  export async function disciplineWritePreflight(input, value, content) {
74
- const candidateDigest = sha256(content);
77
+ const candidateBytes = Buffer.from(content), candidateDigest = sha256(candidateBytes);
75
78
  // Analyze decoded source bytes through the same original source checks.
76
79
  // Binary assets remain in the exact-byte repository and M/S integrity chain.
77
80
  if (Buffer.isBuffer(content)) {
@@ -83,7 +86,11 @@ export async function disciplineWritePreflight(input, value, content) {
83
86
  const review = await readDisciplineSnapshot(snapshot), baseline = snapshot.manifest.files.find(file => file.path === target);
84
87
  await assertDisciplineRepository(input, binding.root, review, ledger);
85
88
  const intent = declaredWrite(input, pass, baseline);
86
- const original = baseline.exists ? (await readSnapshotCopy14(input, snapshot.snapshotRoot, snapshot.manifest, target)).content.toString('utf8') : '';
89
+ const originalBytes = baseline.exists ? (await readSnapshotCopy14(input, snapshot.snapshotRoot, snapshot.manifest, target)).content : Buffer.alloc(0);
90
+ if (baseline.exists) assertPackageReplacement(target, originalBytes, candidateBytes);
91
+ await assertRollbackCandidate(input, binding.root, target, baseline.sha256, candidateDigest);
92
+ const original = originalBytes.toString('utf8');
93
+ await assertDesignWrite(input, pass, target, original, content);
87
94
  if (pass.writeDelegation?.purpose === 'planned-file-create') {
88
95
  if (baseline.exists || intent?.kind !== 'create' || !pass.newFilePaths.includes(target)) needs('Planned creation cannot overwrite an existing source or replace its create role');
89
96
  } else if (pass.writeDelegation?.purpose === 'preserved-existing-modify') assertDelegatedSourceModification({ path: target, before: original, after: content });
package/local-error.mjs CHANGED
@@ -17,9 +17,24 @@ export const LOCAL_ERROR_CODE_CATALOG = Object.freeze({
17
17
  ENTROROVE_MUTATION_DELEGATION_INVALID: Object.freeze({ disposition: 'terminal', stability: 'stable' }),
18
18
  ENTROROVE_IDE_GUARD_REQUIRED: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
19
19
  ENTROROVE_IDE_GUARD_UNSUPPORTED: Object.freeze({ disposition: 'terminal', stability: 'stable' }),
20
+ ENTROROVE_DESIGN_REQUIRED: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
21
+ ENTROROVE_DESIGN_APPROVAL_REQUIRED: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
22
+ ENTROROVE_DESIGN_LINT: Object.freeze({ disposition: 'terminal', stability: 'stable' }),
23
+ ENTROROVE_DESIGN_ACCEPTANCE: Object.freeze({ disposition: 'terminal', stability: 'stable' }),
24
+ ENTROROVE_PACKAGE_DOWNGRADE: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
25
+ ENTROROVE_PACKAGE_VERSION_CONFLICT: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
26
+ ENTROROVE_PACKAGE_VERSION_UNKNOWN: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
27
+ ENTROROVE_PACKAGE_IDENTITY_CHANGED: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
28
+ ENTROROVE_PACKAGE_PROVENANCE_REQUIRED: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
29
+ ENTROROVE_ROLLBACK_PROOF_REQUIRED: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
30
+ ENTROROVE_ROLLBACK_HISTORY_INVALID: Object.freeze({ disposition: 'needs-human', stability: 'stable' }),
20
31
  });
21
32
 
22
33
  const BLOCKED_CODES = new Set([
34
+ 'ENTROROVE_PACKAGE_DOWNGRADE', 'ENTROROVE_PACKAGE_VERSION_CONFLICT', 'ENTROROVE_PACKAGE_VERSION_UNKNOWN',
35
+ 'ENTROROVE_PACKAGE_IDENTITY_CHANGED', 'ENTROROVE_PACKAGE_PROVENANCE_REQUIRED', 'ENTROROVE_ROLLBACK_PROOF_REQUIRED', 'ENTROROVE_ROLLBACK_HISTORY_INVALID',
36
+ 'ENTROROVE_DESIGN_REQUIRED', 'ENTROROVE_DESIGN_APPROVAL_REQUIRED', 'ENTROROVE_DESIGN_BASELINE_CHANGED',
37
+ 'ENTROROVE_DESIGN_FRAMEWORK_MISSING', 'ENTROROVE_DESIGN_ARTIFACT_CHANGED', 'ENTROROVE_DESIGN_SCOPE',
23
38
  'ENTROROVE_DISCIPLINE_NEEDS_HUMAN', 'ENTROROVE_DISCIPLINE_USAGE_UNKNOWN', 'ENTROROVE_DISCIPLINE_REVIEW_REQUIRED',
24
39
  'ENTROROVE_DISCIPLINE_DATA_SNAPSHOT_REQUIRED', 'ENTROROVE_DISCIPLINE_REPOSITORY_DRIFT', 'ENTROROVE_DISCIPLINE_FORMAT_SCOPE',
25
40
  'ENTROROVE_DISCIPLINE_SPECULATIVE', 'ENTROROVE_DISCIPLINE_REVIEW_INVALID',
@@ -52,5 +67,7 @@ export function localErrorDisposition(code) {
52
67
  export function localErrorEnvelope(error, fallbackCode = 'ENTROROVE_LOCAL_FAILURE') {
53
68
  const code = typeof error?.code === 'string' && error.code ? error.code : fallbackCode;
54
69
  const message = typeof error?.message === 'string' && error.message ? error.message : 'Local operation failed';
55
- return { schemaVersion: LOCAL_ERROR_SCHEMA, status: localErrorStatus({ ...error, code }), code, disposition: localErrorDisposition(code), message };
70
+ const report = /^ENTROROVE_DESIGN_/.test(code) && typeof error?.reportPath === 'string' && /^\//.test(error.reportPath)
71
+ && /^[a-f0-9]{64}$/.test(error.reportDigest ?? '') ? { reportPath: error.reportPath, reportDigest: error.reportDigest } : {};
72
+ return { schemaVersion: LOCAL_ERROR_SCHEMA, status: localErrorStatus({ ...error, code }), code, disposition: localErrorDisposition(code), message, ...report };
56
73
  }
@@ -276,12 +276,13 @@ export async function finalGate(input) {
276
276
  guarantee: 'Original engine, frozen role plans and actual three-party checks are required' };
277
277
  }
278
278
 
279
- const LOCAL_OPERATION_GROUPS = Object.freeze(['core', 'budget', 'scope', 'merge', 'chain', 'handoff', 'archguard', 'calctool', 'swarm', 'validator', 'formula', 'evidence']);
279
+ const LOCAL_OPERATION_GROUPS = Object.freeze(['core', 'budget', 'scope', 'merge', 'chain', 'handoff', 'archguard', 'calctool', 'swarm', 'validator', 'formula', 'evidence', 'design']);
280
280
  const PASS_REQUIRED_OPERATIONS = new Set(['mutation-snapshot-attest', 'mutation-consumption-attest', 'mutate-gate', 'gate-issue', 'gate-verify', 'guarded-write',
281
281
  'discipline-review', 'discipline-check', 'guarded-delete', 'guarded-move',
282
282
  'merge-verified', 'rollback', 'archguard-contract-create', 'archguard-contract-update', 'archguard-checkpoint', 'archguard-scope-request',
283
283
  'budget-handoff', 'budget-transition', 'spawn-bind', 'peer-complete', 'complete']);
284
284
  function operationGroup(operation) {
285
+ if (operation.startsWith('design-')) return 'design';
285
286
  if (operation.startsWith('archguard-')) return 'archguard';
286
287
  if (operation.startsWith('calctool-')) return 'calctool';
287
288
  if (operation.includes('handoff')) return 'handoff';
@@ -17,6 +17,7 @@ import { assertDisciplineRepository } from './local-discipline-repository.mjs';
17
17
  import { withPreservedMutationFence } from './local-preserved-mutation-state.mjs';
18
18
  import { assertPreservedMutationProtection } from './local-preserved-mutation-protection.mjs';
19
19
  import { scopeAmendmentRefreshAction } from './scope-amendment-reference.mjs';
20
+ import { rememberMutationBaselines } from './rollback-history.mjs';
20
21
  export { beginMutationBatch14, finishMutationBatch14, failMutationBatch14 } from './local-mutation-batch.mjs';
21
22
 
22
23
  const SCHEMA = 'entrorove.local-execution/1.0';
@@ -114,6 +115,7 @@ async function performWrite(input, state, value) {
114
115
  const baseline = snapshot.manifest.files.find(file => file.path === target), content = contentFor(input, baseline);
115
116
  const discipline = await disciplineWritePreflight(input, current, content);
116
117
  mutationLifetime14(pass);
118
+ await rememberMutationBaselines(input, current, [baseline]);
117
119
  const entry = { status: 'pending', contentDigest: sha256(content), mode: baseline.mode ?? 0o644, at: new Date().toISOString() };
118
120
  await saveMutationConsumption14(locations, { ...ledger, writes: { ...ledger.writes, [target]: entry } });
119
121
  await auditMutation14(input, 'guarded-write-authorized', { passId: pass.passId, sha256: entry.contentDigest,
@@ -2,11 +2,14 @@ import { canonical, sha256 } from './local-integrity.mjs';
2
2
  import { disciplineRepositoryObservation, disciplineRepositoryDifferences } from './local-discipline-repository.mjs';
3
3
  import { atomicMutationJson14, privateMutationDirectory14, mutationStateRoot14, readMutationJson14 } from './local-mutation-files.mjs';
4
4
  import path from 'node:path';
5
+ import { designRunnerBefore, designRunnerAfter } from './design-runner.mjs';
6
+ import { replacementRunnerBefore, replacementRunnerAfter } from './rollback-runner.mjs';
5
7
 
6
8
  export async function runnerDisciplineBefore(input, authority) {
7
- if (!authority.plan.policy.discipline) return null;
9
+ const packageOriginals = await replacementRunnerBefore(input, authority);
10
+ const designOriginals = await designRunnerBefore(input, authority);
8
11
  const original = await disciplineRepositoryObservation(input, authority.root);
9
- return { input, original: original.comparison, readOnly: authority.plan.policy.discipline.access === 'read-only' };
12
+ return { input, designOriginals, packageOriginals, original: original.comparison, readOnly: authority.plan.policy.discipline?.access === 'read-only' };
10
13
  }
11
14
  export async function runnerDisciplineAfter(before, authority, processResult) {
12
15
  if (!before) return null;
@@ -28,5 +31,16 @@ export async function runnerDisciplineAfter(before, authority, processResult) {
28
31
  { code: 'ENTROROVE_READ_ONLY_EXECUTION_DRIFT', execution: { schemaVersion: 'entrorove.validator-local-runner/1.0',
29
32
  status: 'failed', process: processResult, evidence: [], effects: 'unknown', comparison: report } });
30
33
  }
31
- return { ...report, reportDigest };
34
+ try {
35
+ await replacementRunnerAfter(before.input, authority, before.original, after, changed, before.packageOriginals);
36
+ await designRunnerAfter(before.input, authority, changed, before.designOriginals);
37
+ }
38
+ catch (error) {
39
+ error.execution = { schemaVersion: 'entrorove.validator-local-runner/1.0', status: 'failed', process: processResult,
40
+ evidence: [], effects: 'unknown', comparison: report };
41
+ throw error;
42
+ }
43
+ // Keep legacy non-discipline subjects compatible with their frozen Validator
44
+ // schema; the actual comparison and UI refusal above still ran and persisted.
45
+ return authority.plan.policy.discipline ? { ...report, reportDigest } : null;
32
46
  }
@@ -91,3 +91,24 @@ export function verifiedZip(bytes) {
91
91
  }
92
92
  return covered === found.offset;
93
93
  }
94
+ // Metadata inspection reuses the complete CRC/length validation above; no entry
95
+ // is written or executed. Invalid names, duplicates and links remain unknown.
96
+ export function zipMetadataEntries(bytes) {
97
+ if (!verifiedZip(bytes)) return null;
98
+ const found = directory(bytes), result = new Map();
99
+ for (const entry of found.entries) {
100
+ let name;
101
+ try { name = new TextDecoder('utf-8', { fatal: true }).decode(entry.name); } catch { return null; }
102
+ const mode = bytes.readUInt32LE(entry.central + 38) >>> 16, type = mode & 0o170000;
103
+ if (!name || name.startsWith('/') || /[\\\u0000-\u001f\u007f]/.test(name) || /^[A-Za-z]:/.test(name)
104
+ || name.split('/').some(part => part === '..' || part === '.') || result.has(name)
105
+ || type && ![0o100000, 0o040000].includes(type)) return null;
106
+ const start = entry.local + 30 + bytes.readUInt16LE(entry.local + 26) + bytes.readUInt16LE(entry.local + 28);
107
+ const compressed = bytes.subarray(start, start + entry.compressed);
108
+ const metadata = /(?:^|\/)(?:package|manifest)\.json$/.test(name);
109
+ if (metadata && entry.expanded > 262_144) return null;
110
+ result.set(name, metadata ? entry.method === 0 ? compressed
111
+ : inflateRawSync(compressed, { maxOutputLength: Math.max(1, entry.expanded) }) : null);
112
+ }
113
+ return result;
114
+ }
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.64",
4
+ "version": "0.1.67",
5
5
  "endpoint": "https://entrorove.com",
6
6
  "requestSchemaVersion": "entrorove.skill.request/2.0",
7
7
  "supportedRequestSchemaVersions": [
@@ -0,0 +1,54 @@
1
+ import { gunzipSync } from 'node:zlib';
2
+ import { zipMetadataEntries } from './local-scope-zip.mjs';
3
+
4
+ const MAX = 64 * 1024 * 1024;
5
+ function octal(bytes) {
6
+ const value = bytes.toString('ascii').replace(/\0.*$/s, '').trim();
7
+ return /^[0-7]+$/.test(value) ? parseInt(value, 8) : value === '' ? 0 : NaN;
8
+ }
9
+ function tarEntries(bytes) {
10
+ const entries = new Map(); let offset = 0, count = 0, total = 0;
11
+ while (offset + 512 <= bytes.length) {
12
+ const header = bytes.subarray(offset, offset + 512);
13
+ if (header.every(value => value === 0)) {
14
+ return bytes.length - offset >= 1024 && bytes.subarray(offset).every(value => value === 0) ? entries : null;
15
+ }
16
+ let checksum = 0;
17
+ for (let i = 0; i < 512; i++) checksum += i >= 148 && i < 156 ? 32 : header[i];
18
+ if (checksum !== octal(header.subarray(148, 156)) || ++count > 10_000) return null;
19
+ const text = (start, size) => new TextDecoder('utf-8', { fatal: true }).decode(header.subarray(start, start + size)).replace(/\0.*$/s, '');
20
+ const prefix = text(345, 155), name = `${prefix ? `${prefix}/` : ''}${text(0, 100)}`;
21
+ const size = octal(header.subarray(124, 136)), type = header[156];
22
+ if (!name || name.startsWith('/') || /[\\\u0000-\u001f\u007f]/.test(name) || /^[A-Za-z]:/.test(name)
23
+ || name.split('/').some(part => part === '..' || part === '.') || entries.has(name)
24
+ || ![0, 48, 53].includes(type) || !Number.isSafeInteger(size) || size < 0 || size > 16 * 1024 * 1024
25
+ || (total += size) > MAX || type === 53 && size !== 0) return null;
26
+ const end = offset + 512 + size, next = offset + 512 + Math.ceil(size / 512) * 512;
27
+ if (next > bytes.length || !bytes.subarray(end, next).every(value => value === 0)) return null;
28
+ const metadata = /(?:^|\/)(?:package|manifest)\.json$/.test(name);
29
+ if (metadata && size > 262_144) return null;
30
+ entries.set(name, metadata ? bytes.subarray(offset + 512, end) : null);
31
+ offset = next;
32
+ }
33
+ return null;
34
+ }
35
+ export function archiveFormat(target, bytes) {
36
+ if (bytes.length >= 4 && bytes.readUInt32LE(0) === 0x04034b50 || /\.(?:zip|apk|jar|war)$/i.test(target)) return 'zip';
37
+ if (bytes[0] === 31 && bytes[1] === 139 || /\.(?:tgz|tar\.gz)$/i.test(target)) return 'tgz';
38
+ if (/\.tar$/i.test(target) || bytes.length >= 512 && bytes.toString('ascii', 257, 262) === 'ustar') return 'tar';
39
+ if (/\.(?:7z|rar|exe|msi|dmg|pkg|ipa|deb|rpm|appx|nupkg)$/i.test(target)) return 'unsupported';
40
+ return null;
41
+ }
42
+ export function packageArchiveEntries(target, bytes) {
43
+ try {
44
+ if (bytes.length > MAX) return null;
45
+ const format = archiveFormat(target, bytes);
46
+ if (format === 'zip') return zipMetadataEntries(bytes);
47
+ if (format === 'tar') return tarEntries(bytes);
48
+ if (format === 'tgz') {
49
+ const unpacked = gunzipSync(bytes, { maxOutputLength: MAX, info: true });
50
+ return unpacked.engine.bytesWritten === bytes.length ? tarEntries(unpacked.buffer) : null;
51
+ }
52
+ } catch { /* Corrupt or unsupported package structure never proves an upgrade. */ }
53
+ return null;
54
+ }
@@ -0,0 +1,48 @@
1
+ import path from 'node:path';
2
+ import { compareVersions, versionParts } from './version.mjs';
3
+ import { mutationFail14 } from './local-mutation-files.mjs';
4
+ import { archiveFormat, packageArchiveEntries } from './package-archive.mjs';
5
+
6
+ function metadata(bytes) {
7
+ try {
8
+ if (!bytes || bytes.length > 262_144) return null;
9
+ const value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
10
+ if (typeof value?.name !== 'string' || !/^(?:@[a-z0-9._-]+\/)?[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value.name)) return null;
11
+ versionParts(value.version);
12
+ return { name: value.name, version: value.version };
13
+ } catch { return null; }
14
+ }
15
+ function archiveIdentity(target, bytes) {
16
+ const entries = packageArchiveEntries(target, bytes);
17
+ if (!entries) return null;
18
+ // Prefer the actual package root, never a nested dependency or archive name.
19
+ const candidates = [...entries.keys()].filter(name => /^(?:[^/]+\/)?(?:package|manifest)\.json$/.test(name));
20
+ const direct = candidates.filter(name => !name.includes('/'));
21
+ const selected = direct.length ? direct : candidates;
22
+ const primary = selected.filter(name => /(?:^|\/)package\.json$/.test(name));
23
+ const identities = (primary.length ? primary : selected).map(name => metadata(entries.get(name)));
24
+ if (!identities.length || identities.some(value => !value)) return null;
25
+ if (identities.some(value => value.name !== identities[0].name || value.version !== identities[0].version)) return null;
26
+ return identities[0];
27
+ }
28
+ export function assertPackageReplacement(target, original, candidate) {
29
+ const before = Buffer.from(original), after = Buffer.from(candidate);
30
+ if (before.equals(after)) return null;
31
+ const archive = archiveFormat(target, before) || archiveFormat(target, after);
32
+ const manifest = /^(?:package|manifest)\.json$/i.test(path.posix.basename(target));
33
+ if (!archive && !manifest) return null;
34
+ // A non-package manifest (e.g. web app icons) is outside version comparison.
35
+ const old = archive ? archiveIdentity(target, before) : metadata(before);
36
+ const next = archive ? archiveIdentity(target, after) : metadata(after);
37
+ if (!archive && !old && !next) {
38
+ try { if (!Object.hasOwn(JSON.parse(before), 'version') && !Object.hasOwn(JSON.parse(after), 'version')) return null; }
39
+ catch { /* Invalid metadata cannot establish a safe replacement. */ }
40
+ if (!/package\.json$/i.test(target) && !/"version"\s*:/.test(before + after)) return null;
41
+ }
42
+ if (!old || !next) mutationFail14('ENTROROVE_PACKAGE_VERSION_UNKNOWN', 'Package replacement requires verifiable embedded identity and version; preserve both inputs and obtain evidence');
43
+ if (old.name !== next.name) mutationFail14('ENTROROVE_PACKAGE_IDENTITY_CHANGED', 'A different package identity cannot replace the original package');
44
+ const order = compareVersions(next.version, old.version);
45
+ if (order < 0) mutationFail14('ENTROROVE_PACKAGE_DOWNGRADE', 'Older embedded package version cannot overwrite a newer package; use the independently signed exact rollback entry for an intended rollback');
46
+ if (archive && order === 0) mutationFail14('ENTROROVE_PACKAGE_VERSION_CONFLICT', 'Same-version archive has different bytes; filenames, timestamps and caller labels cannot authorize replacement');
47
+ return { package: old.name, previousVersion: old.version, candidateVersion: next.version };
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entrorove/skill",
3
- "version": "0.1.64",
3
+ "version": "0.1.67",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "entrorove": "cli.mjs"
@@ -278,7 +278,33 @@
278
278
  "workflow-observation-runner.mjs",
279
279
  "workflow-observation-state.mjs",
280
280
  "workflow-observation-wire.mjs",
281
- "member-producer-repair-authority.mjs"
281
+ "member-producer-repair-authority.mjs",
282
+ "design-browser-checks.mjs",
283
+ "design-browser-identity.mjs",
284
+ "design-browser-state.mjs",
285
+ "design-browser.mjs",
286
+ "design-context.mjs",
287
+ "design-contracts.d.mts",
288
+ "design-contracts.mjs",
289
+ "design-declaration.mjs",
290
+ "design-frameworks.mjs",
291
+ "design-gate.mjs",
292
+ "design-lint.mjs",
293
+ "design-local-contracts.mjs",
294
+ "design-operations.mjs",
295
+ "design-policy.d.mts",
296
+ "design-policy.mjs",
297
+ "design-research.mjs",
298
+ "design-runner.mjs",
299
+ "design-store.mjs",
300
+ "design-ui.mjs",
301
+ "DESIGN-GATES.md",
302
+ "design-release.mjs",
303
+ "install-identity.mjs",
304
+ "package-archive.mjs",
305
+ "package-replacement.mjs",
306
+ "rollback-history.mjs",
307
+ "rollback-runner.mjs"
282
308
  ],
283
309
  "scripts": {
284
310
  "test": "node test/run.mjs",
@@ -295,13 +321,15 @@
295
321
  "node": ">=20"
296
322
  },
297
323
  "dependencies": {
324
+ "@vue/compiler-sfc": "3.5.43",
298
325
  "ajv": "8.17.1",
326
+ "playwright-core": "1.64.1",
327
+ "postcss": "8.5.28",
328
+ "svelte": "5.57.1",
329
+ "tree-sitter-wasms": "0.1.13",
299
330
  "typescript": "5.9.3",
300
- "yaml": "2.9.1",
301
331
  "web-tree-sitter": "0.24.7",
302
- "tree-sitter-wasms": "0.1.13",
303
- "@vue/compiler-sfc": "3.5.43",
304
- "svelte": "5.57.1"
332
+ "yaml": "2.9.1"
305
333
  },
306
334
  "repository": {
307
335
  "type": "git",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "entrorove.release-state/1.0",
3
- "version": "0.1.64",
4
- "digest": "56a07e4deb7b361010313c592e4b95e5f9b3f2398726ea626f1172114dcff019",
3
+ "version": "0.1.67",
4
+ "digest": "d9a7da983f154ca0bfdd449911ae3c5e7e3af99e8dfb6ccd8eb9392c179f9e34",
5
5
  "files": {
6
6
  "cloud-task-images.mjs": "a260a94b6b1b47df97a8cc8657dfdce1fd37ec752f1cc2bad2542eaa28622bdb",
7
7
  "model-images.mjs": "ab85ac0c44d857c1f460de3e9c06eb1d37b57688b4a6061405c0a44ae4d40bd7",
@@ -12,7 +12,7 @@
12
12
  "mutation-connection-state.mjs": "05bed65a4636a2128e9a48d2e910c5f5a25f0e990f181f58008dd09cac0fbcb5",
13
13
  "mutation-challenge-refresh.mjs": "eac9b18bdfbd968d815321e66c4f6e482fb1be59662abb9dc8b02f35b9ced809",
14
14
  "local-scope-font.mjs": "a69f9da932ed41aa4718036f98f75c0b17ba7f616a5c8bf5623fa9ed697499fa",
15
- "local-scope-zip.mjs": "b8a5f2a75394c23e570c28218a8c0b3bcd626ccc87eba550e759b6c38a68871e",
15
+ "local-scope-zip.mjs": "46c61d201a570c769fc4896fc19090e790765af5f28596557a288bd9b762c69e",
16
16
  "member-producer-repair-authority.mjs": "1e84e5e77ba5ee9a0dcaa4d843061c7b5447191852d7b2fb603b91cfa7c796e4",
17
17
  "local-mutation-completion-evidence.mjs": "d894f7f7d3b5a906e4c869b02ab0abacbd16dc35987a2fe14ff8fa2cf264a62b",
18
18
  "local-mutation-environment-observation.mjs": "8afffe21cb9a7f6cc889e5af9b5c9953684635a90ca4b98d797547bd7831bafb",
@@ -46,19 +46,19 @@
46
46
  "ide-results.mjs": "baa0618845ced901334644479bf4e0d298bad3f955bc18846752de6c283a5573",
47
47
  "ide-local-acceptance.mjs": "238eec6d7b6093fb8ad022da0ed3383a48ba2ffcf693e3ce8a241cf9624088d8",
48
48
  "ide-queue.mjs": "806e716c775fd0079417027b135977c8516f47dddb0bc5ca19e32a355dab0e9e",
49
- "local-discipline-actions.mjs": "418c220b8c8ef59cb8092b6eb680bb75a93b9fad1a09412b1efa98a78afff11b",
49
+ "local-discipline-actions.mjs": "c8d5fd34ca85c646874c98d2e85977346608a0dd59e7b97744f3c536a39af68c",
50
50
  "local-discipline-analysis.mjs": "e887abdd1c6d6ba59ef7323e9035528f0491e5458ca8159985ec26e1ca67a4af",
51
- "local-discipline-batch.mjs": "2cc90dbae55efdee0c445da399c0a9acafa92e915344c25208ebca554fc4c9e9",
51
+ "local-discipline-batch.mjs": "8c0abfaac41ba7a05aef4aaa4f3cae4473799f49808c6d73dce6eab768afce3b",
52
52
  "local-typescript-function-reference.mjs": "af682b479fe0d69a5ecd62232406bb8078408ec8710733cfed9ae6b059762ba5",
53
53
  "local-scope-import-expressions.mjs": "20f1e065582c68d2334e0dfaa130a559ff19886a57f9038dc569f65993bb06c8",
54
- "local-discipline-contracts.mjs": "ab0c11dcc4a6ffd3cb79b53862c19308eaf0d159deee7f9958266852d977363e",
54
+ "local-discipline-contracts.mjs": "25d2bdbcb2698a1927ce5ef43e33052ed90bfe7dbc7c6a708f2bfd4a258609fd",
55
55
  "local-discipline-contracts.d.mts": "defc684c78dc88afa1c54340ab4a01ae36db7aa11257cbe1f5cd323f608c78c4",
56
- "local-discipline-gate.mjs": "d7e92a541fbb5bce60285ab2e1a6e210d478a6b4c8404961f33e40ade962e829",
56
+ "local-discipline-gate.mjs": "0b85dbdc4c15c5183d821ef4b50c48c59c652801eeb8072e7241e442a516f252",
57
57
  "local-discipline-native.mjs": "8b99b5ce951cab3c1426428ad807650c61c0ffb2c346d747b73b99057b382049",
58
58
  "local-discipline-operations.mjs": "9fcb7b8889b7718d42e1d37936485d8198d5017d405e0cdbbabdbbd12fcc9d48",
59
59
  "local-discipline-repository.mjs": "ca997be4289180600cb97ba2938b6b99210e5d5d5317374b130a886cb712d624",
60
60
  "local-discipline-snapshot.mjs": "8fd35b2cfbece8f64579f144f589a66dec9b6022d50fe931c4c1f886cb83134d",
61
- "local-runner-discipline.mjs": "8d4ada838f81d54963adbb7e433f1ca80ae1934ea126f90c59a49d29742979c4",
61
+ "local-runner-discipline.mjs": "99935b1be0d660d6cd45f5facd911bf1e1203ec74953acef5f12a7ebafacffb2",
62
62
  "local-integrity.d.mts": "21357f306234bd841c0d3d90bf2d09f7663f63746f897b9f38190428ae8198b3",
63
63
  "local-equivalence-runner.mjs": "df2f406c693c4da7bb30542ec7b4d46a9336f56e9fdd263196c0f3a73d2f6e91",
64
64
  "validator-runtime-build.mjs": "da20bd19eb088d79d62e33969b4c075dc2199761b395e40060227f8f86b9519a",
@@ -67,8 +67,34 @@
67
67
  "snapshot-retention.mjs": "ca46aeb36b1a5a24fe7e967031a71b9a3d33f40cb715d344402cbe0dc9c425cb",
68
68
  "snapshot-cleanup-journal.mjs": "76a9684cc95b4eb076e1298d78a9dff37948b00d9607b8f546c599072d3ed331",
69
69
  "IDE-EXECUTION.md": "e7e9c19f19f095f5483784bb8de1e610eb008e8e62504b4229e38a9728b89573",
70
- "REUSE-DISCIPLINE.md": "377df04e93bca02731f15055857bd93b3b9656acdcf121735a46a0114516880c",
71
- "reuse-discipline-release.mjs": "7f55ae10ec3396ed3bc5430a0c64f94c33c2dfbc57b32084caab92fd5fd1586f",
70
+ "REUSE-DISCIPLINE.md": "140e8eef2402d7adcae3eafce01f26965dcbd4c270af2387d4b17d37d63ed93d",
71
+ "install-identity.mjs": "41b8c8d8b3e265d2057a07bf7dc98066c34d6b05c65bf7159ca9609c01ee4646",
72
+ "package-archive.mjs": "e06db8d709db8710bead254ee950b78928088be01c58dcea383beaa5eb574e59",
73
+ "package-replacement.mjs": "02ca3fa4ada2addc479f8cb1b8099cd1b574adfeaa2f3c8ecf612ac2cb9127d3",
74
+ "rollback-history.mjs": "0ebd59e1d25ad5f95fa58ce5050cb67b018a391cad162d7643bde3aa4c176528",
75
+ "rollback-runner.mjs": "02f237b8802a2dc4cd5b545904fa102481277898938e723218fa6e75b18b24a7",
76
+ "reuse-discipline-release.mjs": "f51997624a959d2c133d4ce189eff0724ce65e1b62f9406497251fd2f2257b0c",
77
+ "design-browser-checks.mjs": "2aefcd074f55568259bebbd760866fac437527bcd0358b5b2438da56524a27e2",
78
+ "design-browser-identity.mjs": "cb06f4429d0ee84d6608f87a44fb530af46636dd645cf857778c7d8c4d73e12f",
79
+ "design-browser-state.mjs": "386a76d418aa8445ba94ae9497a91445c7899e148ea19418188d48cb757170af",
80
+ "design-browser.mjs": "4811b1f039dc7a5b41336b20b9feef88fbc3d653f8b03cede2dd06315736f155",
81
+ "design-context.mjs": "2e0d764a2a55d05e00477f984135d77808307afb1fc8668f35910612b5e4cea5",
82
+ "design-contracts.d.mts": "af1a5740557d2c8ce90702179e78797a618544c89dbbb3962cf92bef24f9c2b8",
83
+ "design-contracts.mjs": "e5bd6f6e05f40f229f6244672926615ebfb6f5764530ffcf054d548fc8639f36",
84
+ "design-declaration.mjs": "f6c7f56e4697a05a0a832fcdb5a3b07ffe60aa602ba0681cfd09948bdcafc8ad",
85
+ "design-frameworks.mjs": "5d09a8e56068302f951a6236f36cb50e655fb6d6ffbeb2e4eb4b19f025c8a411",
86
+ "design-gate.mjs": "567eb666937704123ef72f26ad1145d9d32f7dfde59553381286de083e4146cb",
87
+ "design-lint.mjs": "9e27d0a6041fbb7f7f2b88b0c46d69dd790582426b534ad436b151d81512a66e",
88
+ "design-local-contracts.mjs": "f331bd1d21f30994596bd9c46008e551f3ed2b6d5eea19d4546c7da0663b768a",
89
+ "design-operations.mjs": "e21e9fcf9fd16115c72558c7cd7a57f7213a30934bde0f1b649aec647d1205f7",
90
+ "design-policy.d.mts": "48fed97fca0f97fef786f182f666d7e6e33efb814cda1991239e1c38984cf80e",
91
+ "design-policy.mjs": "0ffd3cd9381589f7fd3b817d7ee34e6b2bd284a86fd9f31d64fd2c9cde28ca2f",
92
+ "design-research.mjs": "d1552f47dffec787acf2b702a6bb2a31b50ba5a3f82a6cb1c02fb2ffae2aee7f",
93
+ "design-runner.mjs": "85ffbdc133e58da465ec1229c1c9a0e2c4c694bf7b7be185fa4aafabe58fde45",
94
+ "design-store.mjs": "51420d66dd1c4350e4577d73634b51b62ffbcc99c614b3b02f17e82537170af8",
95
+ "design-ui.mjs": "6dd3c5a97ec67e8f1c23e57bcab5eb64380c520befada6f264c4bfcf6b11c9f7",
96
+ "DESIGN-GATES.md": "17b7db53b84df11d70a1d93a21a068ec1257c14c0f24af64e917d52ba1c91549",
97
+ "design-release.mjs": "5fbfa74f2de9a7adbee4e14136b874bca2c12414c0fe21cc2520f2029e8caa30",
72
98
  "local-final-gate16.mjs": "083aec470670e6cdb7ad2ab27b7a51b53a9ec3a8b4121cb9cbfa17d9940892c2",
73
99
  "local-final-gate16-checks.mjs": "1dd30e9666d458d8b12ca6e4975f6f0ac8450c3ea9cbecc7401ae037f66b589a",
74
100
  "local-final-gate16-worker.mjs": "344d64b6c1c5552b96960e0d8514a876a3dbfa9762ab9eb749038e8351727121",
@@ -110,29 +136,29 @@
110
136
  "local-autocoord17-waits.mjs": "2b0a713987822548a94e1ed3a9a891532db45383797dfc5d5bc8d1e35593cf25",
111
137
  "local-validator-isolation17.mjs": "48e94604e28b8fddd567dfb35ff60b73fd094a5803bd49f076388b7f310f92aa",
112
138
  "local-calctool-composition17.mjs": "ac89fcb41ab0d0d9b47c1b5696945281b0894eb064820775e36a011c76314e6a",
113
- "cli.mjs": "2198b5b0bc6c9a5fcba8b2c488c548c22b63ed56c4f7cdbc93d41784a2650bdf",
139
+ "cli.mjs": "14c0c49f06bf209e5034a69b3e81a8458154e92870ce6aeddd1069d568c00837",
114
140
  "ide-selection.mjs": "e11e2b363c06444f1dcb4cdab1eaa0eb748a379b84354e0e71fd9c2dc79a4815",
115
141
  "ide-exit.mjs": "39d60a15b1517d402c208d22a17033976e43733be8a10dc6da386837c59b37b2",
116
142
  "install-cleanup.mjs": "8a8e1a7dfe37427f86cd4d6e317890d0829894a9949d4216674a716b36edbc54",
117
143
  "review-client.mjs": "3491eef68ed533174b31db3c53febb6519b8f088f71a9964644714a9a4c03c77",
118
- "version.mjs": "1f392147d72c40af4b03b307ea607075fb815af5174fa07e254b95fe12dbf3e7",
119
- "SKILL.md": "3f1f4d586a712a790ebb4247840ce6345511226ead7b26154d76b0322f8b753a",
120
- "CAPABILITY-MAP.md": "c8f211755d106acdc47ed512119089d406712088ed167847b791ca9c47ce103e",
144
+ "version.mjs": "85284d4cd5e3314b449e6ae124243f96397bcc274817b5983a79cfdd903d46fd",
145
+ "SKILL.md": "b39010bc22b517ad24f706d843823c2dfa93eb08fc5de055d30b7cc5fb079cea",
146
+ "CAPABILITY-MAP.md": "e64d1411526413d64b89142b3105e8e5bf25b3bc5b1a6ffa217b9ec93f1b58ae",
121
147
  "AGREEMENT.md": "f93a68e92c457200dd024610e65f74e478b8f60860f951da818b70561f1b9bf8",
122
- "DELIVERY-BOUNDARIES.md": "9b9db50ed1bcce9e77ff6527b6bb7eaf12f3b913162ea9cb7fe90db06b393a4f",
123
- "package.json": "2ebbf6ceaf4c2622073e51659c91a6917cabe260d11b60aa370edc2a42c4464d",
148
+ "DELIVERY-BOUNDARIES.md": "cb23ce71b2197085c8441ba7c5b6c05fb21f2a954bf66b4d60cfef8b15895b8b",
149
+ "package.json": "04be10568ee6bf09d6910ed1e434df2f5247ce2aefe9a5b84f5bbe10def6e96c",
124
150
  "manifest.json": "ab06e307795bc9da5add926752b6c4dbdaf18625090261a035aa356a679c504f",
125
- "release.mjs": "046cff9905ed74a467bcc08227ece55e640950d06bf8a2d3268387691b493400",
126
- "local-cli.mjs": "41e2a737c8587cb5ce469a7661f6d14b5ea4cd073f8286822fc4dc96b67f56a1",
151
+ "release.mjs": "4466d54bee56b80561e46d79cf4f46ee07c27c45e7a8b2727583d1d0946ea8d8",
152
+ "local-cli.mjs": "058ac9a4653bc12e0d031b72034ebfe6a72b91b9b23353b9fee04881c7d2e098",
127
153
  "local-state-lock.mjs": "1dbf64264642a0851faf043e07ee9f66d2ad0508e9f799b0626246e42fae893e",
128
- "local-execution.mjs": "185f34e917440a360b3c4bafd3af245243807d964ae3820b3cffaaa2e6348cc6",
154
+ "local-execution.mjs": "721ed7d29bbe4ce6d088909cd84ffc440978c3c9eb0946c49a4281ee685fe3aa",
129
155
  "local-integrity.mjs": "e6d6eace80c610f3c7299d4f8644b0dc023aa9e3d8d64b26535ea7fbced248f1",
130
156
  "local-evidence-verification.mjs": "9b7dea31777505272f63616fd62af7603338f703bb1285fc565d6451715c7ce0",
131
157
  "local-formula.mjs": "706d5ec0cb2caf95a547531a81ca746fe246d4c53d902e03595a3b360e8c85af",
132
158
  "local-ledger.mjs": "2009fb68aadcd7292b72f1d0d99f378d03644be1c5b4b4150d9f6a0b3f7c15ce",
133
- "local-error.mjs": "f01213b21d84402dc5257aaa3ae4a5fa78931a956e9b8526b1e7dbf1b20b6b5d",
159
+ "local-error.mjs": "c0d23310569dd73a697d375acf1fe8d720ebc53e07cb1e86865b6639fbcc0ad3",
134
160
  "blueprint-criterion-wire.mjs": "fb6b7dcce0b97a699e3e8968372767a3fddd83fedc750998573f789177243072",
135
- "blueprint-criterion-runner.mjs": "761c0e670b4b20c6bed53e5ca5d0178759505c3977ea2ed1b7223383c534bc4c",
161
+ "blueprint-criterion-runner.mjs": "05413fea9d2985806adcbfbd53ca0760ca6a850a52957f0cbde43b753a3b7904",
136
162
  "local-chain-bindings.mjs": "b8eff999ccd17b53c614a3c9f5376da78fcaa8632bb3e07bc1adcceda2e75f6c",
137
163
  "local-chain-broker.mjs": "d9a63f2c9eec561853a84ab5fdb7d6794f256582126017c54e5d21d0d370b015",
138
164
  "local-chain-contracts.mjs": "d4d2be4e5c6980f2dda8ff598c7333a75e0bbf2f656aa7fe327af7c9fe73da07",
@@ -228,13 +254,13 @@
228
254
  "local-task-handoff-read-authority.mjs": "7d2991cfac4562039232566d25441aa98106ce27bc9b7bcf6df4ff28d738e167",
229
255
  "local-task-handoff-write-archive.mjs": "3668f39d8ab5664ed717994cf1b4bcc7ea23e146a0e06291f851d30337faeb77",
230
256
  "local-task-handoff-write-receipts.mjs": "e1321127bd6fcad7840e185639f81ae68a8722a0ee4264a7b504a02b27887647",
231
- "workflow-local-runner.mjs": "dc26f408178f145cbecda7b65add6956c897b952cfa74a77474471d3b97bead1",
257
+ "workflow-local-runner.mjs": "3183c15650e605cc9a8c1531cc3651f5a7d2f61b099554d334713d78a9aae62b",
232
258
  "workflow-local-contracts.mjs": "2ab69043b498dad007e46fa5b4cbda6bffb6720652b970c3dec7f2825175ef87",
233
259
  "workflow-local-client.mjs": "45c954f694d96bf0ea87ee2dc6902a6763dac2cb1df713b6a5e749632054e9af",
234
260
  "workflow-local-request.mjs": "4e24c543ce386292a87dc9cd41f90d23375f12079cacffc515c8cd01fd571780",
235
261
  "task-message-entry.mjs": "31d10056cb905237cd5649e8eb067009ff1612ec5dc1061e0373209fbc0b646a",
236
262
  "task-message-cli.mjs": "3ca65e96c28129b3bd5cac962d106e0859a1f17df1f6a66ee543c6efc22f918d",
237
- "local-contracts.mjs": "e739967e3ffade96589c0a3fd519829452b44e1982aa3af11d6802ddfbb7f53a",
263
+ "local-contracts.mjs": "f65c6f87ec021fa96c4e93d65d9fc2e5aca964915657e517697352d549af21a8",
238
264
  "local-contract-types.mjs": "85e71fa4dd67e351a4b67573c740bcdb1661cede53f097189f11837771e75e91",
239
265
  "local-archguard-ast.mjs": "7ff7df9dab0d98ed62983add751c80edea598bc94ef4f38ede0ebd4d41226c87",
240
266
  "local-archguard-checkpoint.mjs": "37896902520cc995720c96288e4a5ed3585952b55c9c35be281f91fa754c71ec",
@@ -267,7 +293,7 @@
267
293
  "local-mutation-wire.mjs": "6d00400e531c693c3e8bfd0c2a24fcc486127f84aafc56ee450dcec7f534eb05",
268
294
  "local-mutation-binding.mjs": "4440d792453a07a375b635fa5a91db607e58162824baced5c79a9a513f4ec918",
269
295
  "local-mutation-pass-store.mjs": "1edc5fcc537dfa04b8ae8b07af554d61ea5911a5da281423ad57214ccf753ad4",
270
- "local-mutation-authority.mjs": "e60e7150e6d887a51ce5903ab947606b30088231d22e85a0c8bf3f29f1d1fd1f",
296
+ "local-mutation-authority.mjs": "7af25eee1820d6c075bbd662b919e83436e5cbc5733e93f7f9f3b0f4ef9d37ac",
271
297
  "local-mutation-batch.mjs": "de27686b5f99a99b02f0218a3fa159e9469d6b19c595117287d6dd37a6efcb3b",
272
298
  "local-mutation-batch-receipt.mjs": "b0e6a8522867255d899e60b66677885933f6a1c4c771cbb52f84dbbbdcf5e5c2",
273
299
  "calctool-runtime-entry.mjs": "98d2bc8df38d16131fc56db0a3d4d260531ba4df412baa5ddc4b8b5c5ca99fe9",
package/release.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { FINAL_GATE16_PACKAGE_FILES, withFinalGate16ReleaseLabels } from './local-final-gate16-release.mjs'
2
2
  import { REUSE_DISCIPLINE_SOURCE_FILES, reuseDisciplineReleaseLabels } from './reuse-discipline-release.mjs'
3
+ import { DESIGN_GATE_SOURCE_FILES, designGateReleaseLabels } from './design-release.mjs'
3
4
  import { createHash } from 'node:crypto'
4
5
  import { readFile, writeFile, rename, rm } from 'node:fs/promises'
5
6
  import path from 'node:path'
@@ -54,6 +55,7 @@ const sourceFiles = [
54
55
  ...memberContinuationSourceFiles,
55
56
  ...preservedMutationSourceFiles,
56
57
  ...REUSE_DISCIPLINE_SOURCE_FILES,
58
+ ...DESIGN_GATE_SOURCE_FILES,
57
59
  ...FINAL_GATE16_PACKAGE_FILES,
58
60
  'local-merge-archguard-verification.d.mts',
59
61
  'local-merge-archguard-verification.mjs',
@@ -337,7 +339,7 @@ const archguardReleaseLabels = {
337
339
  'runtime/archguard-runtime-v7.0.44.mjs': { zh: 'Archguard 架构合同与执行守卫', en: 'Archguard architecture contract and execution guard' },
338
340
  }
339
341
  function releaseLabels() {
340
- return withFinalGate16ReleaseLabels({ ...reuseDisciplineReleaseLabels, ...calctoolReleaseLabels, ...mutationReleaseLabels, ...swarmReleaseLabels, ...coordination17ReleaseLabels, ...nativeReadReleaseLabels, ...feedbackReleaseLabels, ...invocationReleaseLabels, ...chainReleaseLabels, ...taskEntryReleaseLabels, ...archguardReleaseLabels,
342
+ return withFinalGate16ReleaseLabels({ ...designGateReleaseLabels, ...reuseDisciplineReleaseLabels, ...calctoolReleaseLabels, ...mutationReleaseLabels, ...swarmReleaseLabels, ...coordination17ReleaseLabels, ...nativeReadReleaseLabels, ...feedbackReleaseLabels, ...invocationReleaseLabels, ...chainReleaseLabels, ...taskEntryReleaseLabels, ...archguardReleaseLabels,
341
343
  ...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' }])),
342
344
  ...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' }])),
343
345
  ...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.67",
4
+ "date": "2026-10-19",
5
+ "zh": "更新了包降级、版本冲突与凭证回滚门禁。",
6
+ "en": "Updated package downgrade, version conflict and evidence-bound rollback guards."
7
+ },
8
+ {
9
+ "version": "0.1.66",
10
+ "date": "2026-10-19",
11
+ "zh": "更新了字体与 ZIP 完整结构核验、复用与变更一致性门禁、IDE 执行与复用纪律文档、包降级、版本冲突与凭证回滚门禁、复用纪律版本追踪、技能运行和受管安装、版本安全检查、IDE 工作流程说明、能力覆盖映射、安装包配置、统一本地错误信封、原任务短时写入授权与文件快照隔离。",
12
+ "en": "Updated font and ZIP structural verification, reuse and change consistency checks, IDE execution and reuse discipline documentation, package downgrade, version conflict and evidence-bound rollback guards, reuse discipline release tracking, skill runtime and managed installation, version safety checks, IDE workflow instructions, capability coverage map, package configuration, unified local error envelopes, Original task short-lived mutation authority and file snapshot isolation."
13
+ },
14
+ {
15
+ "version": "0.1.65",
16
+ "date": "2026-10-19",
17
+ "zh": "新增强制界面设计门禁:先全站流程图、实际布局与框架官方规范研究,再以用户选择的效果图或原型图确认设计;原写入权限继续约束实现,功能测试、真实浏览器回读刷新及视觉确认完成后才允许交付。保留原失败和未知记录。",
18
+ "en": "Add enforced UI design gates: whole-site flow, actual layout and official framework research, then original user approval of selected mockups or prototypes before implementation. Original write authority remains mandatory; delivery requires functional tests, actual browser state read-back and reload checks, and user visual acceptance. Preserve failures and unknown outcomes."
19
+ },
2
20
  {
3
21
  "version": "0.1.64",
4
22
  "date": "2026-10-19",
@@ -8,13 +8,15 @@ const equivalence = ['local-equivalence-runner.mjs', 'validator-runtime-build.mj
8
8
  'runtime/validator-runtime-v7.0.44.mjs', 'runtime/validator-runtime-v7.0.44.d.mts'];
9
9
  const cleanup = ['snapshot-retention.mjs', 'snapshot-cleanup-journal.mjs'];
10
10
  const documentation = ['IDE-EXECUTION.md', 'REUSE-DISCIPLINE.md'];
11
- export const REUSE_DISCIPLINE_SOURCE_FILES = [...ide, ...discipline, ...equivalence, ...cleanup, ...documentation, 'reuse-discipline-release.mjs'];
11
+ const replacement = ['install-identity.mjs', 'package-archive.mjs', 'package-replacement.mjs', 'rollback-history.mjs', 'rollback-runner.mjs'];
12
+ export const REUSE_DISCIPLINE_SOURCE_FILES = [...ide, ...discipline, ...equivalence, ...cleanup, ...documentation, ...replacement, 'reuse-discipline-release.mjs'];
12
13
  export const reuseDisciplineReleaseLabels = Object.fromEntries([
13
14
  ...ide.map(file => [file, { zh: 'Codex 后续轮次工具门禁', en: 'Codex tool guards across turns' }]),
14
15
  ...discipline.map(file => [file, { zh: '复用与变更一致性门禁', en: 'reuse and change consistency checks' }]),
15
16
  ...equivalence.map(file => [file, { zh: '真实新旧路径逐字段验证', en: 'actual fieldwise parallel-path verification' }]),
16
17
  ...cleanup.map(file => [file, { zh: '验收后快照自动清理', en: 'verified snapshot cleanup' }]),
17
18
  ...documentation.map(file => [file, { zh: 'IDE 执行与复用纪律文档', en: 'IDE execution and reuse discipline documentation' }]),
19
+ ...replacement.map(file => [file, { zh: '包降级、版本冲突与凭证回滚门禁', en: 'package downgrade, version conflict and evidence-bound rollback guards' }]),
18
20
  ['reuse-discipline-release.mjs', { zh: '复用纪律版本追踪', en: 'reuse discipline release tracking' }],
19
21
  ['local-typescript-function-reference.mjs', { zh: 'TypeScript 类型与 Vue 属性构造器引用分析', en: 'TypeScript type and Vue prop constructor references' }],
20
22
  ['local-scope-import-expressions.mjs', { zh: '不可变有限动态导入与逐站点未核实诊断', en: 'immutable finite imports and precise unresolved import diagnostics' }],
@@ -0,0 +1,51 @@
1
+ import path from 'node:path';
2
+ import { canonical, sha256 } from './local-integrity.mjs';
3
+ import { atomicMutationJson14, mutationFail14, mutationFile14, mutationPath14, mutationStateRoot14,
4
+ privateMutationDirectory14, readMutationJson14 } from './local-mutation-files.mjs';
5
+
6
+ const SHA = /^[a-f0-9]{64}$/;
7
+ async function location(input, root, target, digest) {
8
+ mutationPath14(target);
9
+ if (!SHA.test(digest)) mutationFail14('ENTROROVE_ROLLBACK_HISTORY_INVALID', 'Rollback comparison requires exact file digests');
10
+ const state = await mutationStateRoot14(input), repositoryDigest = sha256(root);
11
+ const directory = path.join(state, 'rollback-baselines', repositoryDigest, sha256(target));
12
+ return { state, directory, file: path.join(directory, `${digest}.json`), repositoryDigest };
13
+ }
14
+ async function read(loc, target, digest) {
15
+ let info;
16
+ try { info = await mutationFile14(loc.file); } catch (error) { if (error.code === 'ENOENT') return null; throw error; }
17
+ if (info.stat.size > 8192) mutationFail14('ENTROROVE_ROLLBACK_HISTORY_INVALID', 'Rollback baseline record exceeds its bounded schema');
18
+ const record = await readMutationJson14(loc.file), { recordDigest, ...body } = record;
19
+ if (body.schemaVersion !== 'entrorove.rollback-baseline/1.0' || body.repositoryDigest !== loc.repositoryDigest
20
+ || body.targetPath !== target || body.baselineDigest !== digest || sha256(canonical(body)) !== recordDigest
21
+ || !['mutation-snapshot', 'approved-process-comparison'].includes(body.proof?.kind)
22
+ || !SHA.test(body.proof.authorityDigest ?? '') || !SHA.test(body.proof.baselineManifestDigest ?? '')
23
+ || body.claimsCompletedWrite !== false || body.grantsRollbackAuthority !== false) {
24
+ mutationFail14('ENTROROVE_ROLLBACK_HISTORY_INVALID', 'Rollback baseline record is invalid; preserve the record and original failure');
25
+ }
26
+ return record;
27
+ }
28
+ export async function assertRollbackCandidate(input, root, target, current, candidate) {
29
+ if (current === candidate) return;
30
+ const loc = await location(input, root, target, candidate);
31
+ if (await read(loc, target, candidate)) mutationFail14('ENTROROVE_ROLLBACK_PROOF_REQUIRED',
32
+ 'Candidate restores an actually observed historical file image; use the original independently signed rollback with exact current/restore hashes and transaction records');
33
+ }
34
+ // These immutable observations prove only earlier bytes. They never claim a
35
+ // pending process finished, revive old write authority, or authorize rollback.
36
+ export async function rememberRollbackBaseline(input, root, target, digest, proof) {
37
+ const loc = await location(input, root, target, digest);
38
+ if (await read(loc, target, digest)) return;
39
+ const body = { schemaVersion: 'entrorove.rollback-baseline/1.0', repositoryDigest: loc.repositoryDigest,
40
+ targetPath: target, baselineDigest: digest, proof, claimsCompletedWrite: false, grantsRollbackAuthority: false };
41
+ await privateMutationDirectory14(loc.directory, { privateFrom: loc.state });
42
+ try { await atomicMutationJson14(loc.file, { ...body, recordDigest: sha256(canonical(body)) }, { privateFrom: loc.state }); }
43
+ catch (error) { if (error.code !== 'ENTROROVE_STATE_EXISTS' || !await read(loc, target, digest)) throw error; }
44
+ }
45
+ export async function rememberMutationBaselines(input, value, before) {
46
+ const proof = { kind: 'mutation-snapshot', authorityDigest: sha256(canonical(value.pass)),
47
+ baselineManifestDigest: value.pass.snapshotManifestDigest };
48
+ for (const file of before) if (file.exists) {
49
+ await rememberRollbackBaseline(input, value.binding.root, file.path, file.sha256, proof);
50
+ }
51
+ }