@amalgm/shell 0.1.164-preview.34226938713 → 0.1.164-preview.34240142223

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/PURPOSE.md CHANGED
@@ -70,12 +70,17 @@ Fixture cleanup preserves the original failure and every cleanup failure; instal
70
70
  state remains available when its native owner cannot be removed.
71
71
  A fixture's process lease covers its final writes, and its native release fences
72
72
  cleanup after either cooperative or forced exit.
73
+ Fixture termination is proved by releasing its owned native resources; an HTTP
74
+ stop acknowledgement or transport failure cannot substitute for that proof.
73
75
  A fixture publishes file-based readiness only after its complete evidence is
74
76
  atomically visible to the observing process.
75
77
  Native certification runs independent fixture files serially on its shared host,
76
78
  while each fixture retains the concurrent load required by its examination.
77
79
  The certifying producer owns fixture orchestration and loads production adapters
78
80
  from the exact installed archive, without modifying that archive's bytes.
81
+ An outer fixture owner removes a temporary package only after the process loading
82
+ its native libraries has closed and the package's native service owners have stopped.
83
+ The final examination proof includes process completion and temporary-file cleanup.
79
84
 
80
85
  Shell owns both the internal `@amalgm/shell` package and the public `amalgm`
81
86
  distribution. One tested build supplies both archives; only package identity,
@@ -0,0 +1,58 @@
1
+ import assert from 'node:assert/strict';
2
+ import { spawn } from 'node:child_process';
3
+ import fs from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ /** The archive's owner never loads its native libraries. Only actual worker
9
+ * closure and successful native-owner cleanup permit removing its files. */
10
+ export async function certifyRuntimeProcess(directory, signal,
11
+ script = fileURLToPath(new URL('./runtime-upgrade.native-test.mjs', import.meta.url))) {
12
+ const temporary = await fs.mkdtemp(path.join(os.tmpdir(), 'amalgm-native-runtime-update-'));
13
+ const errors = [];
14
+ let proof = { passed: false };
15
+ try {
16
+ await new Promise((resolve, reject) => {
17
+ // Invoke the worker directly: its close must belong to the process that
18
+ // imports the installed archive, rather than another test-runner parent.
19
+ const env = { ...process.env };
20
+ delete env.NODE_TEST_CONTEXT; // The direct worker reports its own tests, not its parent's binary test protocol.
21
+ const child = spawn(process.execPath, [script, '--runtime-examination-worker', temporary],
22
+ { env, stdio: 'inherit', windowsHide: true });
23
+ let launchError;
24
+ const cancel = () => child.kill('SIGKILL');
25
+ signal.addEventListener('abort', cancel, { once: true });
26
+ child.once('error', error => { launchError = error; });
27
+ child.once('close', (code, exitSignal) => {
28
+ signal.removeEventListener('abort', cancel);
29
+ if (launchError) reject(launchError);
30
+ else if (signal.aborted) reject(signal.reason);
31
+ else if (code !== 0) reject(new Error(`Native runtime examination process failed (${exitSignal ?? code}); see its output above`));
32
+ else resolve();
33
+ });
34
+ if (signal.aborted) cancel();
35
+ });
36
+ } catch (error) { errors.push(error); }
37
+ try {
38
+ const observed = JSON.parse(await fs.readFile(path.join(temporary, 'proof.json'), 'utf8'));
39
+ assert.ok(observed && typeof observed === 'object' && !Array.isArray(observed), 'The worker must leave an examination proof');
40
+ proof = observed;
41
+ assert.equal(typeof proof.passed, 'boolean');
42
+ assert.equal(proof.cleanupComplete, true, 'Native fixture owners did not prove cleanup; temporary package retained');
43
+ if (!proof.passed) errors.push(new Error('Native runtime examination did not pass'));
44
+ // Database close cannot unload a native DLL; worker close above supplies
45
+ // that lifetime fence. Retry counts do not substitute for this ordering.
46
+ await fs.rm(temporary, { recursive: true, force: true });
47
+ } catch (error) {
48
+ errors.push(error);
49
+ proof.retainedTemporary = temporary;
50
+ }
51
+ proof.passed = proof.passed === true && errors.length === 0;
52
+ if (errors.length) proof.processErrors = errors.map(error => String(error.message));
53
+ try {
54
+ await fs.writeFile(path.join(directory, `runtime-update-${process.platform}.json`), JSON.stringify(proof, null, 2) + '\n');
55
+ } catch (error) { errors.push(error); }
56
+ if (errors.length) throw new AggregateError(errors, 'Native runtime examination process or package cleanup failed');
57
+ return proof;
58
+ }
@@ -3,7 +3,6 @@ import assert from 'node:assert/strict';
3
3
  import crypto from 'node:crypto';
4
4
  import fs from 'node:fs/promises';
5
5
  import { constants, createWriteStream } from 'node:fs';
6
- import os from 'node:os';
7
6
  import path from 'node:path';
8
7
  import { createRequire } from 'node:module';
9
8
  import { pathToFileURL } from 'node:url';
@@ -17,6 +16,7 @@ import { examineArchive, hashFile, runIsolated } from '../dist/updates/artifacts
17
16
  import { runtimeHost } from '../dist/updates/store.js';
18
17
  import { canonicalHost, startNetworkView } from './network-view.mjs';
19
18
  import { nativeInstalledService } from './native-service.mjs';
19
+ import { certifyRuntimeProcess } from './runtime-examination-process.mjs';
20
20
 
21
21
  const encode = value => Buffer.from(JSON.stringify(value) + '\n');
22
22
  const digest = value => crypto.createHash('sha256').update(encode(value)).digest('hex');
@@ -166,13 +166,15 @@ export function runtimeExaminationView(examination, baselineReceipt) {
166
166
  return { baseline, candidate, immutable, previous: view(examination.previous), candidateView: view(examination.candidate), events: `${scope}/events` };
167
167
  }
168
168
 
169
+ const workerTemporary = process.argv[2] === '--runtime-examination-worker' ? process.argv[3] : undefined;
169
170
  test('the real native Shell service replaces its authorized package and survives OS restart with identity and state intact', {
170
- skip: process.env.AMALGM_TEST_NATIVE_RUNTIME_UPDATES !== '1', timeout: 1_800_000,
171
+ skip: process.env.AMALGM_TEST_NATIVE_RUNTIME_UPDATES !== '1', timeout: workerTemporary ? undefined : 1_800_000,
171
172
  }, async t => {
172
173
  assert.equal(process.env.CI, 'true'); assert.equal(process.env.GITHUB_ACTIONS, 'true');
173
174
  assert.ok(['darwin', 'linux', 'win32'].includes(process.platform));
174
175
  assert.ok(process.env.AMALGM_RUNTIME_EXAMINATION_DIR, 'The producer must supply signed examination inputs');
175
176
  const directory = path.resolve(process.env.AMALGM_RUNTIME_EXAMINATION_DIR);
177
+ if (!workerTemporary) return await certifyRuntimeProcess(directory, t.signal);
176
178
  const examination = await readJson(path.join(directory, 'examination.json'));
177
179
  const baselineReceipt = examination.previous ? null : await readJson(path.join(directory, 'baseline.json'));
178
180
  const view = runtimeExaminationView(examination, baselineReceipt);
@@ -183,8 +185,8 @@ test('the real native Shell service replaces its authorized package and survives
183
185
  previousDigest: view.baseline.digest, candidateDigest: view.candidate.digest,
184
186
  bootstrapProof: examination.previous === null, legacyAdoptionCertified: false, passed: false,
185
187
  previousArtifact: artifact, candidateArtifact: nextArtifact, checkpoints: [] };
186
- const temporary = await fs.mkdtemp(path.join(os.tmpdir(), 'amalgm-native-runtime-update-'));
187
- let network; let service;
188
+ const temporary = path.resolve(workerTemporary);
189
+ let network; let service; let initializingFixtures = false;
188
190
  t.after(async () => {
189
191
  const errors = [];
190
192
  if (!proof.passed) {
@@ -197,9 +199,10 @@ test('the real native Shell service replaces its authorized package and survives
197
199
  }
198
200
  try { await service?.close(); } catch (error) { errors.push(error); }
199
201
  try { await network?.close(); } catch (error) { errors.push(error); }
202
+ if (initializingFixtures) errors.push(new Error('Native fixture initialization did not return every cleanup owner'));
203
+ proof.cleanupComplete = errors.length === 0;
200
204
  if (errors.length) { proof.passed = false; proof.cleanupErrors = errors.map(error => error.message); }
201
- await fs.writeFile(path.join(directory, `runtime-update-${process.platform}.json`), JSON.stringify(proof, null, 2) + '\n');
202
- await fs.rm(temporary, { recursive: true, force: true, maxRetries: 5 });
205
+ await fs.writeFile(path.join(temporary, 'proof.json'), JSON.stringify(proof, null, 2) + '\n');
203
206
  if (errors.length) throw new AggregateError(errors, 'Native runtime examination cleanup failed');
204
207
  });
205
208
  const archive = path.join(temporary, 'baseline.tgz');
@@ -217,9 +220,11 @@ test('the real native Shell service replaces its authorized package and survives
217
220
  assert.deepEqual(shippedRoot, examination.roots[shippedRoot.signed.version - 1], 'The package must carry the actual reviewed trust root');
218
221
  const npm = path.join(path.dirname(createRequire(import.meta.url).resolve('npm/package.json')), 'bin/npm-cli.js');
219
222
  await runIsolated(process.execPath, [npm, 'ci', '--omit=dev', '--strict-allow-scripts', '--no-audit', '--no-fund'], packageRoot, new AbortController().signal);
223
+ initializingFixtures = true;
220
224
  network = await startNetworkView({ temporary, immutable: view.immutable, previous: view.previous, candidate: view.candidateView,
221
225
  events: view.events, hint: { id: examination.id, sequence: examination.candidate.target.sequence }, serviceEnvironment: true });
222
226
  service = await nativeInstalledService({ packageRoot, label: examination.lane });
227
+ initializingFixtures = false;
223
228
  const baseline = await until('Verified installed baseline and native service health', async () => {
224
229
  const state = journal(service.identity); const status = await service.status();
225
230
  return state.policy?.active?.digest === view.baseline.digest && state.policy.activeHealthy
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@amalgm/shell",
3
- "version": "0.1.164-preview.34226938713",
3
+ "version": "0.1.164-preview.34240142223",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@amalgm/shell",
9
- "version": "0.1.164-preview.34226938713",
9
+ "version": "0.1.164-preview.34240142223",
10
10
  "license": "UNLICENSED",
11
11
  "dependencies": {
12
12
  "@amalgm/agents": "0.2.3-preview.34176069706",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amalgm/shell",
3
- "version": "0.1.164-preview.34226938713",
3
+ "version": "0.1.164-preview.34240142223",
4
4
  "description": "The Amalgm runtime host assembled from the published @amalgm SDK capabilities.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -151,11 +151,11 @@
151
151
  }
152
152
  ]
153
153
  },
154
- "gitHead": "bb88f9e662f3fa9eaaf2750248400e7d1ad86380",
154
+ "gitHead": "898af33d9eb9c7b8cfb73cfe8fe0562e486925da",
155
155
  "amalgmSource": {
156
156
  "repository": "amalgm-inc/amalgm-shell",
157
157
  "branch": "preview",
158
- "commit": "bb88f9e662f3fa9eaaf2750248400e7d1ad86380",
159
- "occurrence": "34226938713"
158
+ "commit": "898af33d9eb9c7b8cfb73cfe8fe0562e486925da",
159
+ "occurrence": "34240142223"
160
160
  }
161
161
  }