@amalgm/shell 0.1.164-main.34239894158 → 0.1.164-preview.34182506414

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 (44) hide show
  1. package/PURPOSE.md +22 -80
  2. package/dist/durability-barrier.d.ts +1 -17
  3. package/dist/durability-barrier.js +12 -57
  4. package/dist/durability-barrier.js.map +1 -1
  5. package/dist/entity-apply-host.js +12 -44
  6. package/dist/entity-apply-host.js.map +1 -1
  7. package/dist/git-command.js +0 -4
  8. package/dist/git-command.js.map +1 -1
  9. package/dist/git-repository-host.d.ts +0 -2
  10. package/dist/git-repository-host.js +24 -76
  11. package/dist/git-repository-host.js.map +1 -1
  12. package/dist/machine-host.js +0 -2
  13. package/dist/machine-host.js.map +1 -1
  14. package/dist/service/adapter.d.ts +0 -1
  15. package/dist/service/adapter.js +1 -1
  16. package/dist/service/adapter.js.map +1 -1
  17. package/dist/service/cli.js +0 -4
  18. package/dist/service/cli.js.map +1 -1
  19. package/dist/service/keepalive.js +0 -2
  20. package/dist/service/keepalive.js.map +1 -1
  21. package/dist/service/windows-action.d.ts +1 -8
  22. package/dist/service/windows-action.js +2 -81
  23. package/dist/service/windows-action.js.map +1 -1
  24. package/dist/service/windows-task.d.ts +2 -8
  25. package/dist/service/windows-task.js +6 -11
  26. package/dist/service/windows-task.js.map +1 -1
  27. package/dist/service/windows.js +5 -6
  28. package/dist/service/windows.js.map +1 -1
  29. package/dist/updates/artifacts.js +1 -8
  30. package/dist/updates/artifacts.js.map +1 -1
  31. package/dist/updates/bootstrap.js +8 -45
  32. package/dist/updates/bootstrap.js.map +1 -1
  33. package/dist/user-ground-host.js +1 -3
  34. package/dist/user-ground-host.js.map +1 -1
  35. package/examinations/native-service.mjs +10 -51
  36. package/examinations/network-view.mjs +22 -76
  37. package/examinations/runtime-upgrade.native-test.mjs +8 -79
  38. package/npm-shrinkwrap.json +44 -44
  39. package/package.json +14 -14
  40. package/dist/installation/layout.d.ts +0 -5
  41. package/dist/installation/layout.js +0 -18
  42. package/dist/installation/layout.js.map +0 -1
  43. package/examinations/network-view.native-test.mjs +0 -159
  44. package/examinations/runtime-examination-process.mjs +0 -58
package/PURPOSE.md CHANGED
@@ -6,9 +6,6 @@ while its host can execute, recover without opening a UI, contain a failing
6
6
  workload, and leave the machine's resources available for useful work. Shell
7
7
  owns composition, process boot, and native host effects; Core and the product
8
8
  SDKs own the decisions those effects execute.
9
- At the completed brick/mortar boundary, compatible bricks advance through independently selected artifacts while Shell
10
- remains the mortar; publishing a brick must not require publishing unchanged
11
- Shell code.
12
9
 
13
10
  ## Operating axioms
14
11
 
@@ -18,9 +15,6 @@ defines the terms and examinations; the [September 2026 audit](docs/operating-au
18
15
  records implementation gaps and historical evidence.
19
16
  The [release environments plan](docs/release-environments-plan.md) records the
20
17
  agreed CI/CD and update evolution; its target behavior is not yet delivered.
21
- The owner permits temporary bundled Shell releases for brick changes during
22
- boundary cleanup. Independent brick adoption remains the target, and Engine
23
- migration is out of scope because no existing Engine users need it.
24
18
 
25
19
  - **O1 — Lifetime follows installation intent.** One installation owner keeps
26
20
  the enabled runtime available independently of its launching UI or terminal
@@ -64,23 +58,6 @@ Release tooling remains available inside each owner repository so a developer
64
58
  or CI job never needs access to another private product repository.
65
59
  Registry credentials are available only to the final protected publication job,
66
60
  after every native examination proves the exact candidate bytes.
67
- Native examination fixtures own their process pipes and test servers independently
68
- of the installed service, and release those resources even when service removal fails.
69
- Fixture cleanup preserves the original failure and every cleanup failure; installed
70
- state remains available when its native owner cannot be removed.
71
- A fixture's process lease covers its final writes, and its native release fences
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.
75
- A fixture publishes file-based readiness only after its complete evidence is
76
- atomically visible to the observing process.
77
- Native certification runs independent fixture files serially on its shared host,
78
- while each fixture retains the concurrent load required by its examination.
79
- The certifying producer owns fixture orchestration and loads production adapters
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.
84
61
 
85
62
  Shell owns both the internal `@amalgm/shell` package and the public `amalgm`
86
63
  distribution. One tested build supplies both archives; only package identity,
@@ -89,12 +66,12 @@ native examinations before either becomes a managed public candidate. Public
89
66
  metadata records the exact internal archive it was derived from; no wrapper,
90
67
  copied Engine implementation or second dependency graph supplies runtime behavior.
91
68
 
92
- At the target boundary, release assembly selects exact registry artifacts from the same managed branch
93
- into a signed installation recipe. Compatible brick changes reuse the existing
94
- Shell artifact without a Shell dependency-update PR or lockfile edit. A product
95
- owns changes to libraries embedded in its own artifact. Reconciliation preserves
96
- human edits, coalesces candidates, and never republishes unchanged components
97
- because a notification or check was repeated.
69
+ Consumer assembly reads dependencies from each declared package manifest and
70
+ selects only registry candidates from the same managed branch. It changes exact
71
+ pins and their locks in one reproducible composition change; the consumer's
72
+ complete checks gate merging that exact tree against its unchanged base. Reconciliation keeps one
73
+ pending change per branch, preserves human edits, and never republishes an
74
+ unchanged consumer merely because a notification or check was repeated.
98
75
 
99
76
  1. **The shell implements host effects; SDKs own domain decisions.** Shell
100
77
  assembles published capabilities, implements their native effect ports,
@@ -133,40 +110,16 @@ uses another data home. Windows Task Scheduler owns a hidden action in that
133
110
  account's logged-in session; the action owns controller exit recovery using
134
111
  Core's restart policy. Scheduler launch retries alone cannot recover a program
135
112
  that started successfully and then exited.
136
- The stable bootstrap retains a bounded result for each controller launch role,
137
- including failures before the controller can open its own log.
138
- Before launching any controller, the Windows action joins its own unnamed,
139
- non-inheritable, kill-on-close Job Object; every descendant remains inside that
140
- lifetime boundary, and only action termination releases its owning handle.
141
- Failed job admission refuses launch, and stopping one action releases its parked
142
- bootstrap and descendants without discovering or signalling remembered PIDs.
143
113
  Native service descriptions preserve literal arguments, environment values and
144
114
  working directories through the owning OS parser. Each directive uses its own
145
115
  grammar; unrepresentable input is refused before replacing a service description.
146
- Windows Scheduler requests travel as structured stdin, and both encoded
147
- PowerShell invocations are admitted within the native command-line length bound.
148
116
  Native Watch opens the OS-resolved physical directory; portable or short-name
149
117
  aliases cannot become a second path grammar inside a kernel handle.
150
118
  Persistent SQLite handles use the OS namespace for the selected file, including
151
119
  Windows extended paths; a host API's legacy path limit cannot select another file.
152
- Durability acknowledgements require the native flush to succeed through a handle
153
- with the access it requires; a failed flush cannot become a successful install
154
- or materialization. Each writer owns the durability of the files it creates.
155
- Content equality alone cannot acknowledge durable materialization; Verify
156
- re-establishes the affected file and directory flushes before completion.
157
- The staging barrier composes a writer's acknowledgement only while its exact
158
- physical file remains unchanged, and a retry obtains fresh writer evidence.
159
- Git materialization requires Git 2.36 or newer to establish its declared writer
160
- durability before immutable object files are closed.
161
- An active Shell installation from before the ownership protocol must exit once
162
- before its saved account can move to this controller; a remembered PID is
163
- neither a stop capability nor permission to start a second writer beside it.
164
- Engine's root-level device control file identifies an earlier installation
165
- format. Shell refuses that format before installing or starting an owner, or
166
- initializing machine or user ground; status and removal remain available.
167
- Stopping its process does not convert its credentials, journals or control files
168
- into current state or portable content. An owner-certified conversion must
169
- retire that layout before Shell can open it.
120
+ An active installation from before the ownership protocol must exit once before
121
+ its saved account can move to this controller; a remembered PID is neither a
122
+ stop capability nor permission to start a second writer beside it.
170
123
 
171
124
  Every UI enters one finite setup operation. It serializes installation setup,
172
125
  selects the server-approved account, and either proves adoption without changing
@@ -182,8 +135,6 @@ changes it. Examinations inject their credential store explicitly.
182
135
  Upgrading the runtime means bumping a version, not merging code. Registry
183
136
  proof runs under the supported Node executable that launched the gate;
184
137
  ambient package-manager shims are not runtime evidence.
185
- The selected Node executable also owns dependency install scripts; an OS
186
- service's ambient search path cannot substitute another Node version.
187
138
 
188
139
  3. **Parity gates each released service.** The target begins with
189
140
  `amalgm-engine`'s runtime surface, then subtracts only owner-approved
@@ -200,25 +151,17 @@ changes it. Examinations inject their credential store explicitly.
200
151
  decisions receive effects explicitly and never discover ambient process
201
152
  state or disk on their own.
202
153
 
203
- 5. **Bricks release independently; one installation owner adopts the graph.**
204
- Shell carries allowed component identities and supported host contracts,
205
- without bundling brick implementations or pinning their runtime versions.
206
- A separately signed recipe selects exact compatible artifacts and dependency
207
- graphs; compatible brick changes reuse unchanged Shell and UI artifacts.
208
- Registry tags locate candidates; fresh TUF metadata authorizes adoption.
209
- Bricks never update themselves, and one coherent tested graph governs each
210
- activation and its recovery. Artifact identity is separate from process
211
- lifetime: a changed in-process implementation requires draining its actual
212
- process cohort even when Shell's bytes stay unchanged. Release proof checks
213
- actual dependency resolutions and public contracts; it never forces all
214
- consumers onto one untested Core version.
215
-
216
- Accepted transition: current Shell packages still bundle a pinned SDK graph,
217
- and the current updater stages one Shell archive. A brick change may therefore
218
- temporarily require a checked Shell release while the boundary is completed;
219
- this does not block initial delivery. The
220
- [release contract](docs/release-environments-plan.md#current-release-contract--2026-09-08)
221
- defines the required transition and distinguishes it from existing evidence.
154
+ 5. **Bricks never update themselves; the shell updates.** Every direct `@amalgm/*`
155
+ dependency is pinned to an exact version — no ranges. Publishing a brick
156
+ changes nothing on any user machine until the shell bumps its pin and ships
157
+ a new release through its lane's signed runtime authority. Registry tags
158
+ locate published artifacts; fresh TUF metadata selects installed adoption.
159
+ One version number, one atomic update, one combination that was
160
+ actually tested together — never a per-brick drift of versions in the wild.
161
+ The shrinkwrap fixes the transitive graph as well; direct pins do not
162
+ imply that every consumer resolves the same Core version. Release proof
163
+ checks those actual resolutions and their public contracts, and upgrades
164
+ affected consumers deliberately instead of forcing an untested override.
222
165
 
223
166
  The installed runtime follows the [update axioms](src/updates/README.md).
224
167
  Each public managed package declares its controller, process and state-format
@@ -520,8 +463,7 @@ Local profiles retain their selected sources and do not follow managed targets.
520
463
  or corrupt chunk, and proves the ordered whole-artifact hash. Durability
521
464
  is one explicit barrier over the whole staged tree — every file,
522
465
  duplicate, and repository — that must succeed before entity rows are
523
- committed or ground is revealed; ordinary streamed files defer their flush
524
- to that barrier. A
466
+ committed or ground is revealed; no staged file is fsynced on its own. A
525
467
  failed barrier leaves the intent retained, no rows committed, no visible
526
468
  destination, and staging re-proven as untrusted on retry. Links and
527
469
  repository transports keep their type-specific cache and adapter rails.
@@ -1049,7 +991,7 @@ Local profiles retain their selected sources and do not follow managed targets.
1049
991
 
1050
992
  65. **A Shell release is tested as the artifact users install.** The release
1051
993
  gate packs Shell, installs that tarball with the exact public Amalgm
1052
- artifacts selected by the installation recipe, and exercises authorization
994
+ dependency versions declared by its manifest, and exercises authorization
1053
995
  through Chat using package exports only. Sibling repositories and source
1054
996
  paths are never an integration-test dependency.
1055
997
 
@@ -17,22 +17,6 @@
17
17
  * libuv's fsync is F_FULLFSYNC on macOS and FlushFileBuffers on Windows,
18
18
  * where NTFS's own log carries the directory entries with their files. A
19
19
  * directory-only fsync never guarantees file contents and is never used.
20
- *
21
- * A writer may instead acknowledge a file it durably flushed before closing
22
- * its write handle. The barrier accepts that evidence only while the exact
23
- * physical file, size, timestamps, and mode remain unchanged. Git owns the
24
- * flush of its immutable objects; neither their names nor their read-only
25
- * permissions exempt them from this law. Evidence is invocation-local and
26
- * must be re-established when retained staging is materialized again.
27
- * Writer and barrier use the OS-native physical path, so filesystem aliases
28
- * cannot introduce a second identity for the same writer output.
29
20
  */
30
- /** An acknowledgement from the file's writer, never persisted as authority. */
31
- export interface DurableFile {
32
- readonly path: string;
33
- readonly identity: string;
34
- }
35
- /** Call only after the producer has successfully flushed this file. */
36
- export declare function recordFileDurability(path: string): DurableFile;
37
- export type DurabilityBarrier = (root: string, durableFiles?: readonly DurableFile[]) => Promise<void>;
21
+ export type DurabilityBarrier = (root: string) => Promise<void>;
38
22
  export declare const durabilityBarrier: DurabilityBarrier;
@@ -17,57 +17,21 @@
17
17
  * libuv's fsync is F_FULLFSYNC on macOS and FlushFileBuffers on Windows,
18
18
  * where NTFS's own log carries the directory entries with their files. A
19
19
  * directory-only fsync never guarantees file contents and is never used.
20
- *
21
- * A writer may instead acknowledge a file it durably flushed before closing
22
- * its write handle. The barrier accepts that evidence only while the exact
23
- * physical file, size, timestamps, and mode remain unchanged. Git owns the
24
- * flush of its immutable objects; neither their names nor their read-only
25
- * permissions exempt them from this law. Evidence is invocation-local and
26
- * must be re-established when retained staging is materialized again.
27
- * Writer and barrier use the OS-native physical path, so filesystem aliases
28
- * cannot introduce a second identity for the same writer output.
29
20
  */
30
21
  import { execFile } from "node:child_process";
31
- import { constants, lstatSync, realpathSync } from "node:fs";
32
- import { lstat, open, readdir, realpath } from "node:fs/promises";
33
- import { isAbsolute, join, relative, resolve, sep } from "node:path";
22
+ import { constants } from "node:fs";
23
+ import { lstat, open, readdir } from "node:fs/promises";
24
+ import { join } from "node:path";
34
25
  import { promisify } from "node:util";
35
26
  import { boundedForEach } from "./bounded-for-each.js";
36
27
  const execFileAsync = promisify(execFile);
37
28
  /** Handles in flight while a tree is flushed entry by entry. */
38
29
  const FLUSH_CONCURRENCY = 16;
39
- function fileIdentity(stats) {
40
- return [stats.dev, stats.ino, stats.size, stats.mtimeNs, stats.ctimeNs, stats.mode].join(":");
41
- }
42
- /** Call only after the producer has successfully flushed this file. */
43
- export function recordFileDurability(path) {
44
- const stats = lstatSync(path, { bigint: true });
45
- if (!stats.isFile())
46
- throw new Error(`durable writer output is not a regular file: ${path}`);
47
- const physical = realpathSync.native(path);
48
- return { path: physical, identity: fileIdentity(stats) };
49
- }
50
- async function validateDurableFiles(root, files) {
51
- const paths = new Set();
52
- for (const file of files) {
53
- const local = relative(root, file.path);
54
- if (resolve(file.path) !== file.path || !local || isAbsolute(local)
55
- || local === ".." || local.startsWith(`..${sep}`) || paths.has(file.path)) {
56
- throw new Error(`durable writer output is not a unique file within staging: ${file.path}`);
57
- }
58
- const stats = await lstat(file.path, { bigint: true });
59
- if (!stats.isFile() || fileIdentity(stats) !== file.identity || await realpath(file.path) !== file.path) {
60
- throw new Error(`durable writer output changed before the barrier: ${file.path}`);
61
- }
62
- paths.add(file.path);
63
- }
64
- return paths;
65
- }
66
30
  async function syncFilesystem(root) {
67
31
  await execFileAsync("sync", ["-f", root]);
68
32
  }
69
- async function flushEntry(path, writable) {
70
- const handle = await open(path, (writable ? constants.O_RDWR : constants.O_RDONLY) | constants.O_NOFOLLOW);
33
+ async function flushEntry(path) {
34
+ const handle = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
71
35
  try {
72
36
  await handle.sync();
73
37
  }
@@ -75,7 +39,7 @@ async function flushEntry(path, writable) {
75
39
  await handle.close();
76
40
  }
77
41
  }
78
- async function syncTree(root, durableFiles) {
42
+ async function syncTree(root) {
79
43
  const files = [];
80
44
  const directories = [root];
81
45
  for (let index = 0; index < directories.length; index += 1) {
@@ -84,30 +48,21 @@ async function syncTree(root, durableFiles) {
84
48
  const path = join(directory, entry.name);
85
49
  if (entry.isDirectory())
86
50
  directories.push(path);
87
- else if (entry.isFile() && !durableFiles.has(path))
51
+ else if (entry.isFile())
88
52
  files.push(path);
89
53
  }
90
54
  }
91
- // FlushFileBuffers requires a write-capable regular-file handle. Directory
92
- // handles retain their separate POSIX contract; Windows does not open them.
93
- await boundedForEach(files, FLUSH_CONCURRENCY, (path) => flushEntry(path, process.platform === "win32"));
94
- if (process.platform !== "win32") {
95
- await boundedForEach(directories, FLUSH_CONCURRENCY, (path) => flushEntry(path, false));
96
- }
55
+ const flushed = process.platform === "win32" ? files : [...files, ...directories];
56
+ await boundedForEach(flushed, FLUSH_CONCURRENCY, flushEntry);
97
57
  }
98
- export const durabilityBarrier = async (root, durableFiles = []) => {
58
+ const platformBarrier = process.platform === "linux" ? syncFilesystem : syncTree;
59
+ export const durabilityBarrier = async (root) => {
99
60
  try {
100
61
  const stats = await lstat(root);
101
62
  if (!stats.isDirectory() || stats.isSymbolicLink()) {
102
63
  throw new Error("staging root is not a physical directory");
103
64
  }
104
- const physical = await realpath(root);
105
- const acknowledged = await validateDurableFiles(physical, durableFiles);
106
- if (process.platform === "linux")
107
- await syncFilesystem(physical);
108
- else
109
- await syncTree(physical, acknowledged);
110
- await validateDurableFiles(physical, durableFiles);
65
+ await platformBarrier(root);
111
66
  }
112
67
  catch (error) {
113
68
  const detail = error instanceof Error ? error.message : String(error);
@@ -1 +1 @@
1
- {"version":3,"file":"durability-barrier.js","sourceRoot":"","sources":["../src/durability-barrier.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"durability-barrier.js","sourceRoot":"","sources":["../src/durability-barrier.ts"],"names":[],"mappings":"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"}
@@ -45,8 +45,7 @@ const portable = (row) => ({
45
45
  fileMode: portableFileMode(row.type, row.filesystemMode),
46
46
  });
47
47
  function fsyncFile(path) {
48
- // Windows FlushFileBuffers requires write access to a regular file.
49
- const fd = openSync(path, process.platform === "win32" ? "r+" : "r");
48
+ const fd = openSync(path, "r");
50
49
  try {
51
50
  fsyncSync(fd);
52
51
  }
@@ -54,48 +53,26 @@ function fsyncFile(path) {
54
53
  closeSync(fd);
55
54
  }
56
55
  }
57
- /** POSIX directory fsync makes the rename durable. Windows carries directory
58
- * entries through its file flush contract and does not open directory handles. */
56
+ /** Directory fsync makes the rename itself durable; platforms that refuse
57
+ * opening or syncing a directory (Windows) already order it themselves. */
59
58
  function fsyncDirectory(path) {
60
- if (process.platform === "win32")
59
+ let fd;
60
+ try {
61
+ fd = openSync(path, "r");
62
+ }
63
+ catch {
61
64
  return;
62
- const fd = openSync(path, "r");
65
+ }
63
66
  try {
64
67
  fsyncSync(fd);
65
68
  }
69
+ catch {
70
+ // EINVAL on filesystems without directory sync; the data fsync stands.
71
+ }
66
72
  finally {
67
73
  closeSync(fd);
68
74
  }
69
75
  }
70
- /** Presence and absence both belong to directory entries. A removed parent
71
- * cone is owned by its first surviving ancestor; only ENOENT allows climbing
72
- * to that owner. Moves settle the old parent as well as the new parent. */
73
- function fsyncAddressParents(addresses) {
74
- if (process.platform === "win32")
75
- return;
76
- const directories = new Set();
77
- for (const address of addresses) {
78
- let directory = dirname(address);
79
- while (true) {
80
- try {
81
- const stats = lstatSync(directory);
82
- if (!stats.isDirectory() || stats.isSymbolicLink()) {
83
- throw new Error(`Apply directory entry owner is not a physical directory: ${directory}`);
84
- }
85
- break;
86
- }
87
- catch (error) {
88
- const parent = dirname(directory);
89
- if (error.code !== "ENOENT" || parent === directory)
90
- throw error;
91
- directory = parent;
92
- }
93
- }
94
- directories.add(directory);
95
- }
96
- for (const directory of directories)
97
- fsyncDirectory(directory);
98
- }
99
76
  const tryLstat = (path) => {
100
77
  try {
101
78
  return lstatSync(path);
@@ -639,15 +616,6 @@ export class EntityApplyHost {
639
616
  this.#releaseGround(intent);
640
617
  return { kind: "local-change" };
641
618
  }
642
- // Matching visible bytes prove content, not durable completion. A
643
- // recovered already-target guard may have skipped reveal after its
644
- // rename became visible but its directory flush failed. Verify owns the
645
- // final acknowledgement, including pre-existing targets and retries.
646
- if (plan.target.status === "active"
647
- && (plan.target.type === "file.text" || plan.target.type === "file.binary")) {
648
- fsyncFile(context.targetPath);
649
- }
650
- fsyncAddressParents(this.applyAddresses(intent, plan));
651
619
  if (plan.target.type === "repo.git" && plan.target.status === "active") {
652
620
  const repository = await this.#repositoryRows(intent, plan, context);
653
621
  this.store.stageVerification(intent, repository.rows, repository.removals);