@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.
- package/PURPOSE.md +22 -80
- package/dist/durability-barrier.d.ts +1 -17
- package/dist/durability-barrier.js +12 -57
- package/dist/durability-barrier.js.map +1 -1
- package/dist/entity-apply-host.js +12 -44
- package/dist/entity-apply-host.js.map +1 -1
- package/dist/git-command.js +0 -4
- package/dist/git-command.js.map +1 -1
- package/dist/git-repository-host.d.ts +0 -2
- package/dist/git-repository-host.js +24 -76
- package/dist/git-repository-host.js.map +1 -1
- package/dist/machine-host.js +0 -2
- package/dist/machine-host.js.map +1 -1
- package/dist/service/adapter.d.ts +0 -1
- package/dist/service/adapter.js +1 -1
- package/dist/service/adapter.js.map +1 -1
- package/dist/service/cli.js +0 -4
- package/dist/service/cli.js.map +1 -1
- package/dist/service/keepalive.js +0 -2
- package/dist/service/keepalive.js.map +1 -1
- package/dist/service/windows-action.d.ts +1 -8
- package/dist/service/windows-action.js +2 -81
- package/dist/service/windows-action.js.map +1 -1
- package/dist/service/windows-task.d.ts +2 -8
- package/dist/service/windows-task.js +6 -11
- package/dist/service/windows-task.js.map +1 -1
- package/dist/service/windows.js +5 -6
- package/dist/service/windows.js.map +1 -1
- package/dist/updates/artifacts.js +1 -8
- package/dist/updates/artifacts.js.map +1 -1
- package/dist/updates/bootstrap.js +8 -45
- package/dist/updates/bootstrap.js.map +1 -1
- package/dist/user-ground-host.js +1 -3
- package/dist/user-ground-host.js.map +1 -1
- package/examinations/native-service.mjs +10 -51
- package/examinations/network-view.mjs +22 -76
- package/examinations/runtime-upgrade.native-test.mjs +8 -79
- package/npm-shrinkwrap.json +44 -44
- package/package.json +14 -14
- package/dist/installation/layout.d.ts +0 -5
- package/dist/installation/layout.js +0 -18
- package/dist/installation/layout.js.map +0 -1
- package/examinations/network-view.native-test.mjs +0 -159
- 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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
32
|
-
import { lstat, open, readdir
|
|
33
|
-
import {
|
|
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
|
|
70
|
-
const handle = await open(path,
|
|
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
|
|
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()
|
|
51
|
+
else if (entry.isFile())
|
|
88
52
|
files.push(path);
|
|
89
53
|
}
|
|
90
54
|
}
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":"AAAA
|
|
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
|
-
|
|
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
|
-
/**
|
|
58
|
-
*
|
|
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
|
-
|
|
59
|
+
let fd;
|
|
60
|
+
try {
|
|
61
|
+
fd = openSync(path, "r");
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
61
64
|
return;
|
|
62
|
-
|
|
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);
|