amalgm 0.1.262 → 0.1.264
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/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/observer/README.md +4 -1
- package/runtime/scripts/amalgm-mcp/observer/coverage.js +52 -0
- package/runtime/scripts/amalgm-mcp/observer/index.js +171 -37
- package/runtime/scripts/amalgm-mcp/observer/watch.js +6 -2
- package/runtime/scripts/amalgm-mcp/registration/entity-cloud.js +54 -19
- package/runtime/scripts/amalgm-mcp/registration/index.js +168 -18
- package/runtime/scripts/amalgm-mcp/registration/repo-followers.js +43 -4
- package/runtime/scripts/amalgm-mcp/registration/repo-states.js +15 -5
- package/runtime/scripts/amalgm-mcp/registration/service.js +99 -12
- package/runtime/scripts/amalgm-mcp/registration/tree.js +104 -20
- package/runtime/scripts/amalgm-mcp/registry/evidence.js +16 -14
- package/runtime/scripts/amalgm-mcp/repocard/capture.js +45 -3
- package/runtime/scripts/amalgm-mcp/repocard/follow.js +65 -10
- package/runtime/scripts/amalgm-mcp/tests/entity-cloud.test.js +90 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-materialization.test.js +243 -0
- package/runtime/scripts/amalgm-mcp/tests/observer.coverage.test.js +197 -0
- package/runtime/scripts/amalgm-mcp/tests/registration.service.test.js +48 -3
- package/runtime/scripts/amalgm-mcp/tests/registration.test.js +10 -6
- package/runtime/scripts/amalgm-mcp/tests/repocard.scope.test.js +182 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The warm checkpoint under exam: compute the state once, then let the
|
|
5
|
+
* doorbell keep it fresh — silence proves the written answer is still
|
|
6
|
+
* true, and a ding re-reads exactly what it named.
|
|
7
|
+
*
|
|
8
|
+
* The identity law above everything: a SCOPED capture's ids are
|
|
9
|
+
* byte-identical to a from-scratch capture's over the same ground.
|
|
10
|
+
* Scope changes what is READ, never what is TRUE.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const assert = require('node:assert/strict');
|
|
14
|
+
const { execFileSync } = require('child_process');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const os = require('os');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const test = require('node:test');
|
|
19
|
+
|
|
20
|
+
const { capture } = require('../repocard/capture');
|
|
21
|
+
const { createFollower } = require('../repocard/follow');
|
|
22
|
+
|
|
23
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm-repocard-scope-'));
|
|
24
|
+
test.after(() => fs.rmSync(tempRoot, { recursive: true, force: true }));
|
|
25
|
+
|
|
26
|
+
function repo(name) {
|
|
27
|
+
const dir = fs.realpathSync(fs.mkdtempSync(path.join(tempRoot, `${name}-`)));
|
|
28
|
+
execFileSync('git', ['init', '-q'], { cwd: dir });
|
|
29
|
+
execFileSync('git', ['config', 'user.email', 'scope@amalgm.test'], { cwd: dir });
|
|
30
|
+
execFileSync('git', ['config', 'user.name', 'Scope'], { cwd: dir });
|
|
31
|
+
execFileSync('git', ['config', 'commit.gpgsign', 'false'], { cwd: dir });
|
|
32
|
+
return dir;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const previousOf = (snap) => snap.checkpoint.untracked.map(
|
|
36
|
+
({ path: rel, mode, sha256 }) => ({ path: rel, mode, sha256 }),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
test('a scoped capture is byte-identical to a from-scratch one — and reads less', () => {
|
|
40
|
+
const dir = repo('identity');
|
|
41
|
+
fs.writeFileSync(path.join(dir, 'kept.txt'), 'unchanged bytes');
|
|
42
|
+
fs.mkdirSync(path.join(dir, 'src'));
|
|
43
|
+
fs.writeFileSync(path.join(dir, 'src', 'edited.js'), 'v1');
|
|
44
|
+
|
|
45
|
+
const full = capture(dir, { withBundle: false });
|
|
46
|
+
fs.writeFileSync(path.join(dir, 'src', 'edited.js'), 'v2');
|
|
47
|
+
|
|
48
|
+
const scoped = capture(dir, {
|
|
49
|
+
withBundle: false,
|
|
50
|
+
suspects: ['src/edited.js'],
|
|
51
|
+
previousUntracked: previousOf(full),
|
|
52
|
+
});
|
|
53
|
+
const fresh = capture(dir, { withBundle: false });
|
|
54
|
+
|
|
55
|
+
assert.equal(scoped.stateId, fresh.stateId, 'scope changes what is read, never what is true');
|
|
56
|
+
assert.equal(scoped.checkpointId, fresh.checkpointId);
|
|
57
|
+
const kept = scoped.checkpoint.untracked.find((file) => file.path === 'kept.txt');
|
|
58
|
+
assert.equal(kept.reused, true, 'the unsuspected file was never opened');
|
|
59
|
+
assert.equal(kept.content, null);
|
|
60
|
+
assert.equal(kept.sha256, fresh.checkpoint.untracked.find((file) => file.path === 'kept.txt').sha256);
|
|
61
|
+
const edited = scoped.checkpoint.untracked.find((file) => file.path === 'src/edited.js');
|
|
62
|
+
assert.ok(!edited.reused, 'the suspect was read fresh');
|
|
63
|
+
assert.equal(edited.content.toString(), 'v2');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('a directory-named suspect covers every file beneath it', () => {
|
|
67
|
+
const dir = repo('dir-suspect');
|
|
68
|
+
fs.mkdirSync(path.join(dir, 'notes'));
|
|
69
|
+
fs.writeFileSync(path.join(dir, 'notes', 'a.md'), 'a1');
|
|
70
|
+
fs.writeFileSync(path.join(dir, 'other.md'), 'other');
|
|
71
|
+
const full = capture(dir, { withBundle: false });
|
|
72
|
+
|
|
73
|
+
fs.writeFileSync(path.join(dir, 'notes', 'a.md'), 'a2');
|
|
74
|
+
const scoped = capture(dir, {
|
|
75
|
+
withBundle: false, suspects: ['notes'], previousUntracked: previousOf(full),
|
|
76
|
+
});
|
|
77
|
+
assert.equal(scoped.stateId, capture(dir, { withBundle: false }).stateId,
|
|
78
|
+
'the OS may name only the folder; everything under it is suspect');
|
|
79
|
+
assert.ok(!scoped.checkpoint.untracked.find((file) => file.path === 'notes/a.md').reused);
|
|
80
|
+
assert.equal(scoped.checkpoint.untracked.find((file) => file.path === 'other.md').reused, true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('new and deleted untracked files are truth regardless of scope', () => {
|
|
84
|
+
const dir = repo('churn');
|
|
85
|
+
fs.writeFileSync(path.join(dir, 'stays.txt'), 'stays');
|
|
86
|
+
fs.writeFileSync(path.join(dir, 'leaves.txt'), 'leaves');
|
|
87
|
+
const full = capture(dir, { withBundle: false });
|
|
88
|
+
|
|
89
|
+
fs.rmSync(path.join(dir, 'leaves.txt'));
|
|
90
|
+
fs.writeFileSync(path.join(dir, 'arrives.txt'), 'new');
|
|
91
|
+
const scoped = capture(dir, {
|
|
92
|
+
withBundle: false, suspects: [], previousUntracked: previousOf(full),
|
|
93
|
+
});
|
|
94
|
+
assert.equal(scoped.stateId, capture(dir, { withBundle: false }).stateId,
|
|
95
|
+
'git names the living; the previous list never resurrects the dead');
|
|
96
|
+
const paths = scoped.checkpoint.untracked.map((file) => file.path);
|
|
97
|
+
assert.ok(paths.includes('arrives.txt') && !paths.includes('leaves.txt'));
|
|
98
|
+
assert.ok(!scoped.checkpoint.untracked.find((file) => file.path === 'arrives.txt').reused,
|
|
99
|
+
'a path the previous state never held is read, scoped or not');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('bundle captures never reuse: cloud cargo reads every byte', () => {
|
|
103
|
+
const dir = repo('bundle');
|
|
104
|
+
fs.writeFileSync(path.join(dir, 'file.txt'), 'bytes');
|
|
105
|
+
fs.writeFileSync(path.join(dir, 'seed.txt'), 'seed');
|
|
106
|
+
execFileSync('git', ['add', 'seed.txt'], { cwd: dir });
|
|
107
|
+
execFileSync('git', ['commit', '-qm', 'seed'], { cwd: dir });
|
|
108
|
+
const full = capture(dir, { withBundle: false });
|
|
109
|
+
|
|
110
|
+
const bundled = capture(dir, {
|
|
111
|
+
withBundle: true, suspects: [], previousUntracked: previousOf(full),
|
|
112
|
+
});
|
|
113
|
+
const entry = bundled.checkpoint.untracked.find((file) => file.path === 'file.txt');
|
|
114
|
+
assert.ok(!entry.reused && entry.content, 'withBundle ignores scope by contract');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('the follower scopes doorbell-driven pairs and re-suspects everything on flush', async () => {
|
|
118
|
+
const dir = repo('follower');
|
|
119
|
+
fs.writeFileSync(path.join(dir, 'a.txt'), 'a');
|
|
120
|
+
fs.writeFileSync(path.join(dir, 'b.txt'), 'b');
|
|
121
|
+
|
|
122
|
+
const seen = [];
|
|
123
|
+
const follower = createFollower({
|
|
124
|
+
repoPath: dir,
|
|
125
|
+
settleMs: 10,
|
|
126
|
+
emit: () => {},
|
|
127
|
+
captureFn: (repo, { suspects } = {}) => {
|
|
128
|
+
seen.push(suspects);
|
|
129
|
+
return capture(repo, { withBundle: false, suspects });
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
await follower.start();
|
|
134
|
+
assert.equal(seen[0], null, 'birth suspects everything — rings before birth were nobody\'s');
|
|
135
|
+
assert.equal(seen[1], null, 'both halves of the pair read the same scope');
|
|
136
|
+
|
|
137
|
+
seen.length = 0;
|
|
138
|
+
fs.writeFileSync(path.join(dir, 'a.txt'), 'a2');
|
|
139
|
+
follower.ring('a.txt');
|
|
140
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
141
|
+
assert.deepEqual(seen, [['a.txt'], ['a.txt']],
|
|
142
|
+
'a settled state narrows the next pair to exactly what rang');
|
|
143
|
+
|
|
144
|
+
seen.length = 0;
|
|
145
|
+
follower.ring('a.txt');
|
|
146
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
147
|
+
assert.deepEqual(seen, [['a.txt'], ['a.txt']],
|
|
148
|
+
'consumed hints stay consumed: only the new ring is suspect');
|
|
149
|
+
|
|
150
|
+
seen.length = 0;
|
|
151
|
+
await follower.flush();
|
|
152
|
+
assert.deepEqual(seen, [null, null], 'flush exists to catch lost doorbells: it never trusts the scope');
|
|
153
|
+
|
|
154
|
+
follower.close();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('a nameless ring makes everything suspect until a pair settles', async () => {
|
|
158
|
+
const dir = repo('nameless');
|
|
159
|
+
fs.writeFileSync(path.join(dir, 'x.txt'), 'x');
|
|
160
|
+
const seen = [];
|
|
161
|
+
const follower = createFollower({
|
|
162
|
+
repoPath: dir,
|
|
163
|
+
settleMs: 10,
|
|
164
|
+
emit: () => {},
|
|
165
|
+
captureFn: (repo, { suspects } = {}) => {
|
|
166
|
+
seen.push(suspects);
|
|
167
|
+
return capture(repo, { withBundle: false, suspects });
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
await follower.start();
|
|
171
|
+
seen.length = 0;
|
|
172
|
+
|
|
173
|
+
follower.ring(null);
|
|
174
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
175
|
+
assert.deepEqual(seen, [null, null], 'null means anywhere: the whole ground is suspect');
|
|
176
|
+
|
|
177
|
+
seen.length = 0;
|
|
178
|
+
follower.ring('x.txt');
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
180
|
+
assert.deepEqual(seen, [['x.txt'], ['x.txt']], 'and the settle consumed the nameless suspicion');
|
|
181
|
+
follower.close();
|
|
182
|
+
});
|