@amalgm/shell 0.1.164-main.34246520761 → 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 -83
  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,26 +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
- Public package selection is complete only when the ordinary registry endpoint
85
- observes the authorized version and integrity within one fixed deadline; cached
86
- predecessors do not authorize another tag write or extend that deadline.
87
61
 
88
62
  Shell owns both the internal `@amalgm/shell` package and the public `amalgm`
89
63
  distribution. One tested build supplies both archives; only package identity,
@@ -92,12 +66,12 @@ native examinations before either becomes a managed public candidate. Public
92
66
  metadata records the exact internal archive it was derived from; no wrapper,
93
67
  copied Engine implementation or second dependency graph supplies runtime behavior.
94
68
 
95
- At the target boundary, release assembly selects exact registry artifacts from the same managed branch
96
- into a signed installation recipe. Compatible brick changes reuse the existing
97
- Shell artifact without a Shell dependency-update PR or lockfile edit. A product
98
- owns changes to libraries embedded in its own artifact. Reconciliation preserves
99
- human edits, coalesces candidates, and never republishes unchanged components
100
- 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.
101
75
 
102
76
  1. **The shell implements host effects; SDKs own domain decisions.** Shell
103
77
  assembles published capabilities, implements their native effect ports,
@@ -136,40 +110,16 @@ uses another data home. Windows Task Scheduler owns a hidden action in that
136
110
  account's logged-in session; the action owns controller exit recovery using
137
111
  Core's restart policy. Scheduler launch retries alone cannot recover a program
138
112
  that started successfully and then exited.
139
- The stable bootstrap retains a bounded result for each controller launch role,
140
- including failures before the controller can open its own log.
141
- Before launching any controller, the Windows action joins its own unnamed,
142
- non-inheritable, kill-on-close Job Object; every descendant remains inside that
143
- lifetime boundary, and only action termination releases its owning handle.
144
- Failed job admission refuses launch, and stopping one action releases its parked
145
- bootstrap and descendants without discovering or signalling remembered PIDs.
146
113
  Native service descriptions preserve literal arguments, environment values and
147
114
  working directories through the owning OS parser. Each directive uses its own
148
115
  grammar; unrepresentable input is refused before replacing a service description.
149
- Windows Scheduler requests travel as structured stdin, and both encoded
150
- PowerShell invocations are admitted within the native command-line length bound.
151
116
  Native Watch opens the OS-resolved physical directory; portable or short-name
152
117
  aliases cannot become a second path grammar inside a kernel handle.
153
118
  Persistent SQLite handles use the OS namespace for the selected file, including
154
119
  Windows extended paths; a host API's legacy path limit cannot select another file.
155
- Durability acknowledgements require the native flush to succeed through a handle
156
- with the access it requires; a failed flush cannot become a successful install
157
- or materialization. Each writer owns the durability of the files it creates.
158
- Content equality alone cannot acknowledge durable materialization; Verify
159
- re-establishes the affected file and directory flushes before completion.
160
- The staging barrier composes a writer's acknowledgement only while its exact
161
- physical file remains unchanged, and a retry obtains fresh writer evidence.
162
- Git materialization requires Git 2.36 or newer to establish its declared writer
163
- durability before immutable object files are closed.
164
- An active Shell installation from before the ownership protocol must exit once
165
- before its saved account can move to this controller; a remembered PID is
166
- neither a stop capability nor permission to start a second writer beside it.
167
- Engine's root-level device control file identifies an earlier installation
168
- format. Shell refuses that format before installing or starting an owner, or
169
- initializing machine or user ground; status and removal remain available.
170
- Stopping its process does not convert its credentials, journals or control files
171
- into current state or portable content. An owner-certified conversion must
172
- 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.
173
123
 
174
124
  Every UI enters one finite setup operation. It serializes installation setup,
175
125
  selects the server-approved account, and either proves adoption without changing
@@ -185,8 +135,6 @@ changes it. Examinations inject their credential store explicitly.
185
135
  Upgrading the runtime means bumping a version, not merging code. Registry
186
136
  proof runs under the supported Node executable that launched the gate;
187
137
  ambient package-manager shims are not runtime evidence.
188
- The selected Node executable also owns dependency install scripts; an OS
189
- service's ambient search path cannot substitute another Node version.
190
138
 
191
139
  3. **Parity gates each released service.** The target begins with
192
140
  `amalgm-engine`'s runtime surface, then subtracts only owner-approved
@@ -203,25 +151,17 @@ changes it. Examinations inject their credential store explicitly.
203
151
  decisions receive effects explicitly and never discover ambient process
204
152
  state or disk on their own.
205
153
 
206
- 5. **Bricks release independently; one installation owner adopts the graph.**
207
- Shell carries allowed component identities and supported host contracts,
208
- without bundling brick implementations or pinning their runtime versions.
209
- A separately signed recipe selects exact compatible artifacts and dependency
210
- graphs; compatible brick changes reuse unchanged Shell and UI artifacts.
211
- Registry tags locate candidates; fresh TUF metadata authorizes adoption.
212
- Bricks never update themselves, and one coherent tested graph governs each
213
- activation and its recovery. Artifact identity is separate from process
214
- lifetime: a changed in-process implementation requires draining its actual
215
- process cohort even when Shell's bytes stay unchanged. Release proof checks
216
- actual dependency resolutions and public contracts; it never forces all
217
- consumers onto one untested Core version.
218
-
219
- Accepted transition: current Shell packages still bundle a pinned SDK graph,
220
- and the current updater stages one Shell archive. A brick change may therefore
221
- temporarily require a checked Shell release while the boundary is completed;
222
- this does not block initial delivery. The
223
- [release contract](docs/release-environments-plan.md#current-release-contract--2026-09-08)
224
- 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.
225
165
 
226
166
  The installed runtime follows the [update axioms](src/updates/README.md).
227
167
  Each public managed package declares its controller, process and state-format
@@ -523,8 +463,7 @@ Local profiles retain their selected sources and do not follow managed targets.
523
463
  or corrupt chunk, and proves the ordered whole-artifact hash. Durability
524
464
  is one explicit barrier over the whole staged tree — every file,
525
465
  duplicate, and repository — that must succeed before entity rows are
526
- committed or ground is revealed; ordinary streamed files defer their flush
527
- to that barrier. A
466
+ committed or ground is revealed; no staged file is fsynced on its own. A
528
467
  failed barrier leaves the intent retained, no rows committed, no visible
529
468
  destination, and staging re-proven as untrusted on retry. Links and
530
469
  repository transports keep their type-specific cache and adapter rails.
@@ -1052,7 +991,7 @@ Local profiles retain their selected sources and do not follow managed targets.
1052
991
 
1053
992
  65. **A Shell release is tested as the artifact users install.** The release
1054
993
  gate packs Shell, installs that tarball with the exact public Amalgm
1055
- artifacts selected by the installation recipe, and exercises authorization
994
+ dependency versions declared by its manifest, and exercises authorization
1056
995
  through Chat using package exports only. Sibling repositories and source
1057
996
  paths are never an integration-test dependency.
1058
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);