amalgm 0.1.245 → 0.1.246
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/lib/cli.js +146 -8
- package/lib/layout.js +89 -4
- package/lib/shared-realtime-tunnel.js +384 -43
- package/lib/supervisor.js +0 -3
- package/package.json +2 -2
- package/runtime/scripts/amalgm-mcp/adapters/contract.js +197 -0
- package/runtime/scripts/amalgm-mcp/adapters/filesystem.js +135 -0
- package/runtime/scripts/amalgm-mcp/adapters/git.js +84 -0
- package/runtime/scripts/amalgm-mcp/adapters/index.js +43 -0
- package/runtime/scripts/amalgm-mcp/adapters/reference.js +31 -0
- package/runtime/scripts/amalgm-mcp/adapters/truth.js +19 -0
- package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +20 -68
- package/runtime/scripts/amalgm-mcp/index.js +4 -0
- package/runtime/scripts/amalgm-mcp/lib/layout.js +89 -4
- package/runtime/scripts/amalgm-mcp/observer/README.md +259 -0
- package/runtime/scripts/amalgm-mcp/observer/apply.js +150 -0
- package/runtime/scripts/amalgm-mcp/observer/continuity.js +111 -0
- package/runtime/scripts/amalgm-mcp/observer/edges.js +111 -0
- package/runtime/scripts/amalgm-mcp/observer/index.js +1146 -0
- package/runtime/scripts/amalgm-mcp/observer/scan.js +387 -0
- package/runtime/scripts/amalgm-mcp/observer/store.js +262 -0
- package/runtime/scripts/amalgm-mcp/observer/verify.js +200 -0
- package/runtime/scripts/amalgm-mcp/observer/watch.js +62 -0
- package/runtime/scripts/amalgm-mcp/project-context/store.js +19 -87
- package/runtime/scripts/amalgm-mcp/registration/classify.js +33 -0
- package/runtime/scripts/amalgm-mcp/registration/entity-cloud.js +456 -0
- package/runtime/scripts/amalgm-mcp/registration/entity-content.js +345 -0
- package/runtime/scripts/amalgm-mcp/registration/index.js +752 -0
- package/runtime/scripts/amalgm-mcp/registration/refusal.js +42 -0
- package/runtime/scripts/amalgm-mcp/registration/repo-followers.js +127 -0
- package/runtime/scripts/amalgm-mcp/registration/repo-states.js +109 -0
- package/runtime/scripts/amalgm-mcp/registration/service.js +310 -0
- package/runtime/scripts/amalgm-mcp/registration/tree.js +178 -0
- package/runtime/scripts/amalgm-mcp/registry/evidence.js +826 -0
- package/runtime/scripts/amalgm-mcp/registry/index.js +666 -0
- package/runtime/scripts/amalgm-mcp/registry/store.js +290 -0
- package/runtime/scripts/amalgm-mcp/repocard/README.md +103 -0
- package/runtime/scripts/amalgm-mcp/repocard/apply.js +111 -0
- package/runtime/scripts/amalgm-mcp/repocard/capture-worker.js +94 -0
- package/runtime/scripts/amalgm-mcp/repocard/capture.js +149 -0
- package/runtime/scripts/amalgm-mcp/repocard/follow.js +310 -0
- package/runtime/scripts/amalgm-mcp/repocard/git.js +48 -0
- package/runtime/scripts/amalgm-mcp/repocard/index.js +48 -0
- package/runtime/scripts/amalgm-mcp/server/local-service-router.js +2 -0
- package/runtime/scripts/amalgm-mcp/server/routes/entities.js +287 -0
- package/runtime/scripts/amalgm-mcp/server/routes/state.js +0 -13
- package/runtime/scripts/amalgm-mcp/state/attachments.js +11 -45
- package/runtime/scripts/amalgm-mcp/state/db.js +46 -215
- package/runtime/scripts/amalgm-mcp/state/docs.js +301 -1101
- package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +0 -99
- package/runtime/scripts/amalgm-mcp/state/mutations.js +130 -507
- package/runtime/scripts/amalgm-mcp/state/promotions.js +1 -21
- package/runtime/scripts/amalgm-mcp/state/replicas.js +13 -134
- package/runtime/scripts/amalgm-mcp/state/shared-rest.js +3 -46
- package/runtime/scripts/amalgm-mcp/tests/adapters.test.js +379 -0
- package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +0 -193
- package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.life.test.js +449 -0
- package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.rig.js +107 -0
- package/runtime/scripts/amalgm-mcp/tests/doorbell.matrix.watch.test.js +213 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/adapter.js +72 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/assert.js +497 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/bind.js +91 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/graph.js +1031 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/ledger.js +234 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-oracle/referee.js +118 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.oracle.test.js +1365 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.registry.test.js +967 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.rig.js +239 -0
- package/runtime/scripts/amalgm-mcp/tests/entity.storm.test.js +486 -0
- package/runtime/scripts/amalgm-mcp/tests/fake-watch.js +35 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.links.test.js +289 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.rig.js +103 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.roots.test.js +505 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.storm.test.js +389 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.territory.test.js +845 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.test.js +657 -0
- package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +17 -32
- package/runtime/scripts/amalgm-mcp/tests/registration.service.test.js +256 -0
- package/runtime/scripts/amalgm-mcp/tests/registration.test.js +931 -0
- package/runtime/scripts/amalgm-mcp/tests/repocard.storm.test.js +284 -0
- package/runtime/scripts/amalgm-mcp/tests/repocard.test.js +371 -0
- package/runtime/scripts/amalgm-mcp/tests/repofollow.storm.test.js +435 -0
- package/runtime/scripts/amalgm-mcp/tests/repofollow.test.js +675 -0
- package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +0 -84
- package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +13 -376
- package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +0 -309
- package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +30 -758
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +3 -72
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +1 -89
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +24 -55
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +141 -424
- package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +3 -171
- package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +7 -220
- package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +236 -179
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +326 -2228
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +3 -5319
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +1 -191
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +17 -3140
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +740 -137
- package/runtime/scripts/amalgm-mcp/workspace/access-store.js +1 -5
- package/runtime/scripts/amalgm-mcp/workspace/card.js +11 -35
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +14 -39
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +147 -587
- package/runtime/scripts/amalgm-mcp/workspace/git.js +34 -406
- package/runtime/scripts/amalgm-mcp/workspace/merge.js +181 -102
- package/runtime/scripts/amalgm-mcp/workspace/objects.js +56 -221
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +538 -1964
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +28 -49
- package/runtime/scripts/amalgm-mcp/workspace/store.js +4 -77
- package/runtime/scripts/amalgm-mcp/workspace/transition.js +192 -1734
- package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +1 -7
- package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +61 -73
- package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +6 -51
- package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +8 -79
- package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +146 -3461
- package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +71 -899
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +538 -370
- package/runtime/scripts/local-gateway.js +1 -0
- package/runtime/scripts/amalgm-mcp/state/doc-disk.js +0 -550
- package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +0 -399
- package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +0 -1
- package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +0 -24
- package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +0 -1999
- package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +0 -364
- package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +0 -185
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +0 -26
- package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +0 -119
- package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +0 -1446
- package/runtime/scripts/amalgm-mcp/workspace/content.js +0 -32
- package/runtime/scripts/amalgm-mcp/workspace/durable.js +0 -45
- package/runtime/scripts/amalgm-mcp/workspace/intent.js +0 -535
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +0 -31
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +0 -72
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +0 -165
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +0 -5
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +0 -2
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +0 -2201
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +0 -19
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +0 -18
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +0 -18
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +0 -33
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +0 -47
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +0 -43
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +0 -114
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +0 -84
- package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +0 -771
- package/runtime/scripts/amalgm-mcp/workspace/provenance.js +0 -240
- package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +0 -240
- package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +0 -100
- package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +0 -127
- package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +0 -12
- package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +0 -125
- package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +0 -76
- package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +0 -3766
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const crypto = require('crypto');
|
|
4
|
-
|
|
5
|
-
const CHUNK_BYTES = 512 * 1024;
|
|
6
|
-
|
|
7
|
-
function sha256(bytes) {
|
|
8
|
-
return crypto.createHash('sha256').update(bytes).digest('hex');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function describeBytes(bytesInput) {
|
|
12
|
-
const bytes = Buffer.isBuffer(bytesInput) ? bytesInput : Buffer.from(bytesInput);
|
|
13
|
-
const chunks = [];
|
|
14
|
-
for (let offset = 0; offset < bytes.length; offset += CHUNK_BYTES) {
|
|
15
|
-
const part = bytes.subarray(offset, Math.min(offset + CHUNK_BYTES, bytes.length));
|
|
16
|
-
chunks.push({ sha256: sha256(part), bytes: part.length });
|
|
17
|
-
}
|
|
18
|
-
return chunks;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function manifestChecksum(chunks) {
|
|
22
|
-
return sha256(Buffer.from(JSON.stringify(
|
|
23
|
-
chunks.map((chunk) => ({ sha256: chunk.sha256, bytes: chunk.bytes })),
|
|
24
|
-
)));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = {
|
|
28
|
-
CHUNK_BYTES,
|
|
29
|
-
describeBytes,
|
|
30
|
-
manifestChecksum,
|
|
31
|
-
sha256,
|
|
32
|
-
};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const crypto = require('crypto');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
function syncDirectory(directory) {
|
|
8
|
-
const descriptor = fs.openSync(directory, 'r');
|
|
9
|
-
try {
|
|
10
|
-
fs.fsyncSync(descriptor);
|
|
11
|
-
} finally {
|
|
12
|
-
fs.closeSync(descriptor);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Durable sync state is replaced, never edited in place. After a process
|
|
18
|
-
* death, readers therefore see either the complete old value or the complete
|
|
19
|
-
* new value.
|
|
20
|
-
*/
|
|
21
|
-
function writeFile(file, content, mode = 0o600) {
|
|
22
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
23
|
-
const temporary = path.join(
|
|
24
|
-
path.dirname(file),
|
|
25
|
-
`.${path.basename(file)}.${process.pid}.${crypto.randomBytes(8).toString('hex')}.tmp`,
|
|
26
|
-
);
|
|
27
|
-
let descriptor = fs.openSync(temporary, 'wx', mode);
|
|
28
|
-
try {
|
|
29
|
-
fs.writeFileSync(descriptor, content);
|
|
30
|
-
fs.fsyncSync(descriptor);
|
|
31
|
-
fs.closeSync(descriptor);
|
|
32
|
-
descriptor = null;
|
|
33
|
-
fs.renameSync(temporary, file);
|
|
34
|
-
syncDirectory(path.dirname(file));
|
|
35
|
-
} finally {
|
|
36
|
-
if (descriptor !== null) fs.closeSync(descriptor);
|
|
37
|
-
if (fs.existsSync(temporary)) fs.rmSync(temporary, { force: true });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function writeJson(file, value, { pretty = false } = {}) {
|
|
42
|
-
writeFile(file, `${JSON.stringify(value, null, pretty ? 2 : 0)}\n`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
module.exports = { writeFile, writeJson };
|
|
@@ -1,535 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Durable local authorship for one registered code folder.
|
|
5
|
-
*
|
|
6
|
-
* The official stream still has only two values: card and checkpoint. This
|
|
7
|
-
* file is not shared state. It records the exact local state a real file/Git
|
|
8
|
-
* observation saw, relative to the last verified official state on this
|
|
9
|
-
* machine. The reducer may preserve only this record; a physical mismatch by
|
|
10
|
-
* itself never creates authorship.
|
|
11
|
-
*
|
|
12
|
-
* Axiom 4: a reserved predecessor is immutable and later work occupies one
|
|
13
|
-
* successor pinned to its exact content. Proven by workspace-local-intent.test.js.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const crypto = require('crypto');
|
|
17
|
-
const fs = require('fs');
|
|
18
|
-
const path = require('path');
|
|
19
|
-
const { AMALGM_DIR } = require('../config');
|
|
20
|
-
const durable = require('./durable');
|
|
21
|
-
const provenance = require('./provenance');
|
|
22
|
-
|
|
23
|
-
const INTENT_CONTRACT = 'repo-local-intent@4';
|
|
24
|
-
const LEGACY_INTENT_CONTRACTS = new Set([
|
|
25
|
-
'repo-local-intent@2',
|
|
26
|
-
'repo-local-intent@3',
|
|
27
|
-
]);
|
|
28
|
-
const SUCCESSOR_CONTRACT = 'repo-local-intent-successor@2';
|
|
29
|
-
const LEGACY_SUCCESSOR_CONTRACT = 'repo-local-intent-successor@1';
|
|
30
|
-
const INTENT_DIR = path.join(AMALGM_DIR, 'state', 'repo-local-intent');
|
|
31
|
-
const SHA = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
32
|
-
const MUTATION_ID = /^[A-Za-z0-9_-]{16,160}$/;
|
|
33
|
-
|
|
34
|
-
function newMutationId() {
|
|
35
|
-
return `mut_${crypto.randomBytes(18).toString('base64url')}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function intentFile(resourceId) {
|
|
39
|
-
const digest = crypto.createHash('sha256').update(resourceId).digest('hex').slice(0, 24);
|
|
40
|
-
return path.join(INTENT_DIR, `${digest}.json`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function canonicalPath(localPath) {
|
|
44
|
-
const resolved = path.resolve(localPath);
|
|
45
|
-
const tail = [];
|
|
46
|
-
let existing = resolved;
|
|
47
|
-
while (!fs.existsSync(existing)) {
|
|
48
|
-
const parent = path.dirname(existing);
|
|
49
|
-
if (parent === existing) return resolved;
|
|
50
|
-
tail.unshift(path.basename(existing));
|
|
51
|
-
existing = parent;
|
|
52
|
-
}
|
|
53
|
-
return path.join(fs.realpathSync.native(existing), ...tail);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function branchState(branches) {
|
|
57
|
-
return (branches || [])
|
|
58
|
-
.map((branch) => ({ name: branch.name, oid: branch.oid ?? null }))
|
|
59
|
-
.sort((left, right) => left.name.localeCompare(right.name));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function cardState(card) {
|
|
63
|
-
return {
|
|
64
|
-
head: {
|
|
65
|
-
branch: card?.head?.branch ?? null,
|
|
66
|
-
oid: card?.head?.oid ?? null,
|
|
67
|
-
detached: Boolean(card?.head?.detached),
|
|
68
|
-
},
|
|
69
|
-
branches: branchState(card?.branches || card?.refs),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function semanticState(value) {
|
|
74
|
-
return {
|
|
75
|
-
card: cardState(value?.local?.card),
|
|
76
|
-
checkpoint: {
|
|
77
|
-
baseOid: value?.local?.checkpoint?.baseOid ?? null,
|
|
78
|
-
branch: value?.local?.checkpoint?.branch ?? null,
|
|
79
|
-
revision: value?.local?.checkpoint?.value?.revision ?? null,
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function semanticDigest(value) {
|
|
85
|
-
return crypto.createHash('sha256')
|
|
86
|
-
.update(JSON.stringify(semanticState(value)))
|
|
87
|
-
.digest('hex');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function validTree(value) {
|
|
91
|
-
return SHA.test(String(value || ''));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function validInitialBase(base) {
|
|
95
|
-
return Boolean(
|
|
96
|
-
base?.through === 0
|
|
97
|
-
&& base.card === null
|
|
98
|
-
&& validTree(base.indexTree)
|
|
99
|
-
&& base.indexTree === base.worktreeTree
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function validLocal(local) {
|
|
104
|
-
return Boolean(
|
|
105
|
-
validTree(local?.card?.head?.oid)
|
|
106
|
-
&& Array.isArray(local?.card?.branches)
|
|
107
|
-
&& validTree(local?.indexTree)
|
|
108
|
-
&& validTree(local?.worktreeTree)
|
|
109
|
-
&& local?.checkpoint?.baseOid === local.card.head.oid
|
|
110
|
-
&& typeof local?.physicalDigest === 'string'
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function sameCard(left, right) {
|
|
115
|
-
return JSON.stringify(cardState(left)) === JSON.stringify(cardState(right));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function validSuccessor(value, predecessor) {
|
|
119
|
-
const currentContract = value?.contract === SUCCESSOR_CONTRACT;
|
|
120
|
-
const legacyContract = value?.contract === LEGACY_SUCCESSOR_CONTRACT;
|
|
121
|
-
return Boolean(
|
|
122
|
-
value
|
|
123
|
-
&& (currentContract || legacyContract)
|
|
124
|
-
&& (!currentContract || MUTATION_ID.test(value.birthMutationId))
|
|
125
|
-
&& value.predecessorSemanticDigest === predecessor.semanticDigest
|
|
126
|
-
&& value.base?.semanticDigest === predecessor.semanticDigest
|
|
127
|
-
&& sameCard(value.base?.card, predecessor.local.card)
|
|
128
|
-
&& value.base?.indexTree === predecessor.local.indexTree
|
|
129
|
-
&& value.base?.worktreeTree === predecessor.local.worktreeTree
|
|
130
|
-
&& value.base?.physicalDigest === predecessor.local.physicalDigest
|
|
131
|
-
&& validLocal(value.local)
|
|
132
|
-
&& value.semanticDigest === semanticDigest(value)
|
|
133
|
-
&& typeof value.capturedAt === 'string'
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function validIntent(value, project) {
|
|
138
|
-
const initial = validInitialBase(value?.base);
|
|
139
|
-
const currentContract = value?.contract === INTENT_CONTRACT;
|
|
140
|
-
const legacyContract = LEGACY_INTENT_CONTRACTS.has(value?.contract)
|
|
141
|
-
&& (
|
|
142
|
-
value.contract !== 'repo-local-intent@2'
|
|
143
|
-
|| value.successor === undefined
|
|
144
|
-
|| value.successor === null
|
|
145
|
-
);
|
|
146
|
-
return Boolean(
|
|
147
|
-
value
|
|
148
|
-
&& (currentContract || legacyContract)
|
|
149
|
-
&& (!currentContract || MUTATION_ID.test(value.birthMutationId))
|
|
150
|
-
&& value.resourceId === project.resourceId
|
|
151
|
-
&& value.ownerUserId === project.ownerUserId
|
|
152
|
-
&& canonicalPath(value.path) === canonicalPath(project.path)
|
|
153
|
-
&& Number.isInteger(value.base?.through)
|
|
154
|
-
&& value.base.through >= 0
|
|
155
|
-
&& (
|
|
156
|
-
initial
|
|
157
|
-
|| (
|
|
158
|
-
validTree(value.base?.card?.head?.oid)
|
|
159
|
-
&& Array.isArray(value.base?.card?.branches)
|
|
160
|
-
&& validTree(value.base?.indexTree)
|
|
161
|
-
&& validTree(value.base?.worktreeTree)
|
|
162
|
-
)
|
|
163
|
-
)
|
|
164
|
-
&& validLocal(value.local)
|
|
165
|
-
&& value.semanticDigest === semanticDigest(value)
|
|
166
|
-
&& (
|
|
167
|
-
value.successor === undefined
|
|
168
|
-
|| value.successor === null
|
|
169
|
-
|| validSuccessor(value.successor, value)
|
|
170
|
-
)
|
|
171
|
-
&& (
|
|
172
|
-
value.publication === null
|
|
173
|
-
|| (
|
|
174
|
-
typeof value.publication?.mutationId === 'string'
|
|
175
|
-
&& (
|
|
176
|
-
!currentContract
|
|
177
|
-
|| value.publication.mutationId === value.birthMutationId
|
|
178
|
-
)
|
|
179
|
-
&& value.publication.semanticDigest === value.semanticDigest
|
|
180
|
-
&& value.publication.baseThrough === value.base.through
|
|
181
|
-
&& (
|
|
182
|
-
(
|
|
183
|
-
value.publication.numberedVersion === null
|
|
184
|
-
&& value.publication.numberedAt === null
|
|
185
|
-
&& value.publication.foldedAt === null
|
|
186
|
-
)
|
|
187
|
-
|| (
|
|
188
|
-
Number.isInteger(value.publication.numberedVersion)
|
|
189
|
-
&& value.publication.numberedVersion > 0
|
|
190
|
-
&& typeof value.publication.numberedAt === 'string'
|
|
191
|
-
&& (
|
|
192
|
-
value.publication.foldedAt === null
|
|
193
|
-
|| typeof value.publication.foldedAt === 'string'
|
|
194
|
-
)
|
|
195
|
-
)
|
|
196
|
-
)
|
|
197
|
-
)
|
|
198
|
-
)
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function persistIntent(project, value) {
|
|
203
|
-
if (!validIntent(value, project)) throw new Error('local code intent is invalid');
|
|
204
|
-
durable.writeJson(intentFile(project.resourceId), value, { pretty: true });
|
|
205
|
-
return value;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function readIntent(project) {
|
|
209
|
-
if (!project?.resourceId || !project?.path || !project?.ownerUserId) return null;
|
|
210
|
-
const file = intentFile(project.resourceId);
|
|
211
|
-
let raw;
|
|
212
|
-
try {
|
|
213
|
-
raw = fs.readFileSync(file, 'utf8');
|
|
214
|
-
} catch (error) {
|
|
215
|
-
if (error?.code === 'ENOENT') return null;
|
|
216
|
-
throw new Error(`cannot read local code intent ${file}`, { cause: error });
|
|
217
|
-
}
|
|
218
|
-
let value;
|
|
219
|
-
try {
|
|
220
|
-
value = JSON.parse(raw);
|
|
221
|
-
} catch (error) {
|
|
222
|
-
throw new Error(`local code intent ${file} is not valid JSON`, { cause: error });
|
|
223
|
-
}
|
|
224
|
-
if (!validIntent(value, project)) {
|
|
225
|
-
throw new Error(`local code intent ${file} has an invalid or unsupported contract`);
|
|
226
|
-
}
|
|
227
|
-
return value;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function writeIntent(project, input) {
|
|
231
|
-
const held = readIntent(project);
|
|
232
|
-
const nextSemanticDigest = semanticDigest({ local: input.local });
|
|
233
|
-
const samePredecessor = held
|
|
234
|
-
&& held.semanticDigest === nextSemanticDigest
|
|
235
|
-
&& JSON.stringify(held.base) === JSON.stringify(input.base);
|
|
236
|
-
let birthMutationId = MUTATION_ID.test(String(input.birthMutationId || ''))
|
|
237
|
-
? input.birthMutationId
|
|
238
|
-
: null;
|
|
239
|
-
if (
|
|
240
|
-
!birthMutationId
|
|
241
|
-
&& (
|
|
242
|
-
input.preserveBirthMutationId === true
|
|
243
|
-
|| samePredecessor
|
|
244
|
-
)
|
|
245
|
-
&& MUTATION_ID.test(String(held?.birthMutationId || ''))
|
|
246
|
-
) birthMutationId = held.birthMutationId;
|
|
247
|
-
if (
|
|
248
|
-
!birthMutationId
|
|
249
|
-
&& MUTATION_ID.test(String(input.publication?.mutationId || ''))
|
|
250
|
-
) birthMutationId = input.publication.mutationId;
|
|
251
|
-
if (!birthMutationId) birthMutationId = newMutationId();
|
|
252
|
-
const next = {
|
|
253
|
-
contract: INTENT_CONTRACT,
|
|
254
|
-
birthMutationId,
|
|
255
|
-
resourceId: project.resourceId,
|
|
256
|
-
ownerUserId: project.ownerUserId,
|
|
257
|
-
path: canonicalPath(project.path),
|
|
258
|
-
base: input.base,
|
|
259
|
-
local: input.local,
|
|
260
|
-
publication: input.publication || null,
|
|
261
|
-
terminal: input.terminal || null,
|
|
262
|
-
capturedAt: new Date().toISOString(),
|
|
263
|
-
};
|
|
264
|
-
next.semanticDigest = nextSemanticDigest;
|
|
265
|
-
next.successor = input.successor === undefined && samePredecessor
|
|
266
|
-
? held.successor || null
|
|
267
|
-
: input.successor || null;
|
|
268
|
-
return persistIntent(project, next);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Freeze the published intent and atomically replace only its open successor.
|
|
273
|
-
*
|
|
274
|
-
* A successor is a final desk state, not an edit log. Its base is the exact
|
|
275
|
-
* content identity of the frozen predecessor, so repeated saves coalesce
|
|
276
|
-
* without ever changing the bytes or publication identity already in flight.
|
|
277
|
-
*/
|
|
278
|
-
function writeSuccessor(project, predecessor, input) {
|
|
279
|
-
let held = readIntent(project);
|
|
280
|
-
if (!held || held.semanticDigest !== predecessor?.semanticDigest) return null;
|
|
281
|
-
if (
|
|
282
|
-
held.contract !== INTENT_CONTRACT
|
|
283
|
-
|| !MUTATION_ID.test(String(held.birthMutationId || ''))
|
|
284
|
-
) {
|
|
285
|
-
held = writeIntent(project, {
|
|
286
|
-
base: held.base,
|
|
287
|
-
local: held.local,
|
|
288
|
-
terminal: held.terminal,
|
|
289
|
-
successor: held.successor,
|
|
290
|
-
publication: held.publication,
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
const prior = held.successor || null;
|
|
294
|
-
const successor = {
|
|
295
|
-
contract: SUCCESSOR_CONTRACT,
|
|
296
|
-
birthMutationId: null,
|
|
297
|
-
predecessorSemanticDigest: held.semanticDigest,
|
|
298
|
-
base: {
|
|
299
|
-
semanticDigest: held.semanticDigest,
|
|
300
|
-
card: held.local.card,
|
|
301
|
-
indexTree: held.local.indexTree,
|
|
302
|
-
worktreeTree: held.local.worktreeTree,
|
|
303
|
-
physicalDigest: held.local.physicalDigest,
|
|
304
|
-
},
|
|
305
|
-
local: input.local,
|
|
306
|
-
terminal: input.terminal || null,
|
|
307
|
-
capturedAt: new Date().toISOString(),
|
|
308
|
-
};
|
|
309
|
-
successor.semanticDigest = semanticDigest(successor);
|
|
310
|
-
successor.birthMutationId = (
|
|
311
|
-
prior?.semanticDigest === successor.semanticDigest
|
|
312
|
-
&& MUTATION_ID.test(String(prior.birthMutationId || ''))
|
|
313
|
-
) ? prior.birthMutationId : newMutationId();
|
|
314
|
-
persistIntent(project, { ...held, contract: INTENT_CONTRACT, successor });
|
|
315
|
-
return successor;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function readSuccessor(project) {
|
|
319
|
-
return readIntent(project)?.successor || null;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function clearSuccessor(project, predecessor = null) {
|
|
323
|
-
let held = readIntent(project);
|
|
324
|
-
if (
|
|
325
|
-
!held?.successor
|
|
326
|
-
|| (
|
|
327
|
-
predecessor
|
|
328
|
-
&& held.semanticDigest !== predecessor.semanticDigest
|
|
329
|
-
)
|
|
330
|
-
) return false;
|
|
331
|
-
if (
|
|
332
|
-
held.contract !== INTENT_CONTRACT
|
|
333
|
-
|| !MUTATION_ID.test(String(held.birthMutationId || ''))
|
|
334
|
-
) {
|
|
335
|
-
held = writeIntent(project, {
|
|
336
|
-
base: held.base,
|
|
337
|
-
local: held.local,
|
|
338
|
-
terminal: held.terminal,
|
|
339
|
-
successor: held.successor,
|
|
340
|
-
publication: held.publication,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
persistIntent(project, { ...held, contract: INTENT_CONTRACT, successor: null });
|
|
344
|
-
return true;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function clearIntent(project) {
|
|
348
|
-
if (!project?.resourceId) return false;
|
|
349
|
-
const file = intentFile(project.resourceId);
|
|
350
|
-
if (!fs.existsSync(file)) return false;
|
|
351
|
-
fs.rmSync(file, { force: true });
|
|
352
|
-
return true;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function stampPublication(project, intent, mutationId) {
|
|
356
|
-
const held = readIntent(project);
|
|
357
|
-
if (
|
|
358
|
-
!held
|
|
359
|
-
|| held.semanticDigest !== intent?.semanticDigest
|
|
360
|
-
|| mutationId !== held.birthMutationId
|
|
361
|
-
) return null;
|
|
362
|
-
return writeIntent(project, {
|
|
363
|
-
birthMutationId: held.birthMutationId,
|
|
364
|
-
base: held.base,
|
|
365
|
-
local: held.local,
|
|
366
|
-
terminal: held.terminal,
|
|
367
|
-
publication: {
|
|
368
|
-
mutationId,
|
|
369
|
-
semanticDigest: held.semanticDigest,
|
|
370
|
-
baseThrough: held.base.through,
|
|
371
|
-
numberedVersion: null,
|
|
372
|
-
numberedAt: null,
|
|
373
|
-
foldedAt: null,
|
|
374
|
-
},
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function reservePublication(project, intent) {
|
|
379
|
-
let held = readIntent(project);
|
|
380
|
-
if (!held || held.semanticDigest !== intent?.semanticDigest) return null;
|
|
381
|
-
if (!MUTATION_ID.test(String(held.birthMutationId || ''))) {
|
|
382
|
-
held = writeIntent(project, {
|
|
383
|
-
base: held.base,
|
|
384
|
-
local: held.local,
|
|
385
|
-
terminal: held.terminal,
|
|
386
|
-
successor: held.successor,
|
|
387
|
-
publication: held.publication,
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
if (
|
|
391
|
-
held.publication
|
|
392
|
-
&& held.publication.semanticDigest === held.semanticDigest
|
|
393
|
-
&& held.publication.baseThrough === held.base.through
|
|
394
|
-
&& held.publication.numberedVersion === null
|
|
395
|
-
) return held;
|
|
396
|
-
provenance.requireTransition(
|
|
397
|
-
provenance.publicationState(held.publication),
|
|
398
|
-
provenance.ACTION.RESERVE,
|
|
399
|
-
provenance.STATE.RESERVED,
|
|
400
|
-
['intent-record', 'publication-id'],
|
|
401
|
-
);
|
|
402
|
-
return stampPublication(
|
|
403
|
-
project,
|
|
404
|
-
held,
|
|
405
|
-
held.birthMutationId,
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function markPublicationNumbered(project, mutationId, version, numberedAt = null) {
|
|
410
|
-
const held = readIntent(project);
|
|
411
|
-
if (!held || held.publication?.mutationId !== mutationId) return null;
|
|
412
|
-
if (!Number.isInteger(version) || version < 1) {
|
|
413
|
-
throw new Error('numbered publication version is invalid');
|
|
414
|
-
}
|
|
415
|
-
if (
|
|
416
|
-
held.publication.numberedVersion !== null
|
|
417
|
-
&& held.publication.numberedVersion !== version
|
|
418
|
-
) throw new Error('publication received a different official number');
|
|
419
|
-
if (held.publication.numberedVersion === version) return held;
|
|
420
|
-
provenance.requireTransition(
|
|
421
|
-
provenance.publicationState(held.publication),
|
|
422
|
-
provenance.ACTION.NUMBER,
|
|
423
|
-
provenance.STATE.NUMBERED,
|
|
424
|
-
['publication-id', 'official-version', 'server-receipt'],
|
|
425
|
-
);
|
|
426
|
-
return writeIntent(project, {
|
|
427
|
-
base: held.base,
|
|
428
|
-
local: held.local,
|
|
429
|
-
terminal: held.terminal,
|
|
430
|
-
publication: {
|
|
431
|
-
...held.publication,
|
|
432
|
-
numberedVersion: version,
|
|
433
|
-
numberedAt: numberedAt || new Date().toISOString(),
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function markPublicationFolded(project, mutationId, version, numberedAt = null) {
|
|
439
|
-
const numbered = markPublicationNumbered(project, mutationId, version, numberedAt);
|
|
440
|
-
if (!numbered || numbered.publication.foldedAt !== null) return numbered;
|
|
441
|
-
provenance.requireTransition(
|
|
442
|
-
provenance.publicationState(numbered.publication),
|
|
443
|
-
provenance.ACTION.FOLD,
|
|
444
|
-
provenance.STATE.FOLDED,
|
|
445
|
-
['predecessor-folded', 'official-event', 'official-snapshot'],
|
|
446
|
-
);
|
|
447
|
-
return writeIntent(project, {
|
|
448
|
-
base: numbered.base,
|
|
449
|
-
local: numbered.local,
|
|
450
|
-
terminal: numbered.terminal,
|
|
451
|
-
publication: {
|
|
452
|
-
...numbered.publication,
|
|
453
|
-
foldedAt: new Date().toISOString(),
|
|
454
|
-
},
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function officialAcknowledgement(project, intent) {
|
|
459
|
-
if (!intent?.publication?.mutationId) return null;
|
|
460
|
-
if (
|
|
461
|
-
Number.isInteger(intent.publication.numberedVersion)
|
|
462
|
-
&& intent.publication.numberedVersion > 0
|
|
463
|
-
&& typeof intent.publication.numberedAt === 'string'
|
|
464
|
-
) {
|
|
465
|
-
return {
|
|
466
|
-
mutationId: intent.publication.mutationId,
|
|
467
|
-
version: intent.publication.numberedVersion,
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
const row = require('../state/mutations').getMutation(
|
|
471
|
-
`resource:${project.resourceId}`,
|
|
472
|
-
intent.publication.mutationId,
|
|
473
|
-
{ revealOperation: true },
|
|
474
|
-
);
|
|
475
|
-
return (
|
|
476
|
-
row
|
|
477
|
-
&& row.state === 'synced'
|
|
478
|
-
&& Number.isInteger(row.officialCloudVersion)
|
|
479
|
-
&& row.officialCloudVersion > 0
|
|
480
|
-
&& intent.publication.semanticDigest === intent.semanticDigest
|
|
481
|
-
) ? {
|
|
482
|
-
mutationId: row.mutationId,
|
|
483
|
-
version: row.officialCloudVersion,
|
|
484
|
-
} : null;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function publicationMayRebase(project, intent, through) {
|
|
488
|
-
if (
|
|
489
|
-
!intent?.publication?.mutationId
|
|
490
|
-
|| intent.publication.numberedVersion !== null
|
|
491
|
-
|| !Number.isInteger(through)
|
|
492
|
-
|| through <= intent.base.through
|
|
493
|
-
) return false;
|
|
494
|
-
const row = require('../state/mutations').getMutation(
|
|
495
|
-
`resource:${project.resourceId}`,
|
|
496
|
-
intent.publication.mutationId,
|
|
497
|
-
);
|
|
498
|
-
return !row || (
|
|
499
|
-
row.state === 'rejected'
|
|
500
|
-
&& row.failureCode === 'code_project_stale_base'
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
function readOwnedIntent(project) {
|
|
505
|
-
const intent = readIntent(project);
|
|
506
|
-
if (!intent) return null;
|
|
507
|
-
return officialAcknowledgement(project, intent) ? null : intent;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
module.exports = {
|
|
511
|
-
INTENT_CONTRACT,
|
|
512
|
-
SUCCESSOR_CONTRACT,
|
|
513
|
-
_test: {
|
|
514
|
-
canonicalPath,
|
|
515
|
-
cardState,
|
|
516
|
-
semanticDigest,
|
|
517
|
-
validInitialBase,
|
|
518
|
-
validIntent,
|
|
519
|
-
validSuccessor,
|
|
520
|
-
},
|
|
521
|
-
clearIntent,
|
|
522
|
-
clearSuccessor,
|
|
523
|
-
intentFile,
|
|
524
|
-
markPublicationFolded,
|
|
525
|
-
markPublicationNumbered,
|
|
526
|
-
officialAcknowledgement,
|
|
527
|
-
publicationMayRebase,
|
|
528
|
-
readIntent,
|
|
529
|
-
readOwnedIntent,
|
|
530
|
-
readSuccessor,
|
|
531
|
-
reservePublication,
|
|
532
|
-
stampPublication,
|
|
533
|
-
writeIntent,
|
|
534
|
-
writeSuccessor,
|
|
535
|
-
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# Native workspace file transaction
|
|
2
|
-
|
|
3
|
-
This helper is the only code allowed to observe or cross an ordered file or
|
|
4
|
-
Git-metadata mutation. JavaScript cannot express
|
|
5
|
-
`openat`/`renameat`/`linkat`, so path confinement at that layer has an
|
|
6
|
-
unavoidable check/use race. The helper opens each parent directory with
|
|
7
|
-
`O_NOFOLLOW` and performs every observation and mutation relative to those
|
|
8
|
-
held descriptors.
|
|
9
|
-
|
|
10
|
-
`main.go` owns the transaction and crash ordering. `move_*.go` consumes a
|
|
11
|
-
prepared target without replacing a concurrent destination; its disappearance
|
|
12
|
-
is the crossing receipt. `writers_*.go` proves that an extracted source inode
|
|
13
|
-
has no pre-existing writer before it can be retired. The checked-in binaries
|
|
14
|
-
are required because managed runtimes do not include a Go compiler.
|
|
15
|
-
`manifest.json` binds them to the exact source bytes.
|
|
16
|
-
|
|
17
|
-
The `git-lock` backend prepares index or HEAD bytes inside the linked
|
|
18
|
-
worktree's private Git directory, hard-links them to Git's canonical lock, and
|
|
19
|
-
renames that lock over the destination. The retained hard link identifies the
|
|
20
|
-
crossing even after later Git activity changes the destination or creates a
|
|
21
|
-
new lock.
|
|
22
|
-
|
|
23
|
-
Rebuild and verify both release platforms:
|
|
24
|
-
|
|
25
|
-
```sh
|
|
26
|
-
node build.mjs
|
|
27
|
-
node verify.mjs
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
The ordinary JavaScript transition tests execute the host binary against real
|
|
31
|
-
Git repositories, crashes, open descriptors, and directory-swap races.
|
|
Binary file
|