@ai-ecoverse/slicc-kernel 1.143.5 → 1.143.6

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/README.md CHANGED
@@ -754,6 +754,13 @@ The proxy answers `200` with `Content-Type: application/vnd.slicc.raw-fetch`: a
754
754
 
755
755
  **Renaming a directory on the OPFS root** ([#1122](https://github.com/ai-ecoverse/slicc-kernel/issues/1122)): Chrome's OPFS can move a file but not a directory. So the kernel renames a directory by creating the new tree and moving each file into it (copying a file only if it can't move, e.g. while it's open elsewhere), then removing the old tree. Within the kernel the rename is atomic to readers: a lookup or listing under the old or the new name, or of either parent, waits until the whole tree has moved. So a stage-then-rename updater (pnpm's, an atomic config write) is never seen half there. Another tab sharing the root sees it all at once too (see below). A root whose directories can move (the Node memory root) renames them in one step. **If the page reloads or crashes mid-rename**, some files are already in the new tree and the rest are still in the old one. So before the first move, the kernel writes the rename's intent (`{ from, to }`) to its metadata store, and clears it after the last. At the next boot, when no other tab shares the root (the same rule as for the boot's other repair of stale metadata), the kernel finishes every recorded rename: it moves what's left into the new tree, removes the old name, and clears the record. The moved directory and its files keep their owner, mode and inode, as after a rename that wasn't stopped. While another tab shares the root, a kernel finishes only renames whose kernel is gone ([#1141](https://github.com/ai-ecoverse/slicc-kernel/issues/1141)): the renaming kernel holds a Web Lock (`slicc-rename:<id>`, named in the record) for the whole rename, and the browser releases it when that tab closes or its worker ends. A kernel that boots beside a peer, and every kernel that sees a peer go, finishes each recorded rename whose lock is free, taking that lock while it does, so two survivors never replay the same rename and a live kernel's rename is never touched. A record without a lock (written by an older kernel, or where Web Locks are missing) waits for a boot that's alone, as before. While another tab finishes a rename, this kernel's readers wait as for its own, and so do the other tabs'. A kernel whose wait for another tab's rename ends with the lock free and the record still there finishes the rename itself, under that lock, and a boot that's alone takes a record's lock too, so a third tab that boots meanwhile waits for it.
756
756
 
757
+ **A rename by another process** ([#1257](https://github.com/ai-ecoverse/slicc-kernel/issues/1257)): as on Linux, where a descriptor names the file and not its path, a program's open file follows a rename of that file, or of a directory above it, by another process.
758
+ - **The journal:** the kernel keeps the last 4096 renames it applied, each from and to a path, and counts them in the change buffer it shares with its processes. A rename in another tab on the same root reaches it through the sidecar's move notice.
759
+ - **Catching up:** before a WASI or Emscripten program uses an open file's path (a read, a write, a seek, an `fsync`, a close, an open), it reads that count once. If another process renamed something since, and the program holds files open, it asks the kernel once for the renames of the paths it holds and moves its open files to their new names. The kernel answers only about the paths the program sent, and, under `users: 'enforce'`, drops every rename whose old or new name the program couldn't look up (search permission on each directory above it), so no process learns names in a directory it can't search. A program's own rename moves the count without asking, when nothing else came in between.
760
+ - **A late rename:** a write that finds its file's name gone asks for renames once more and tries again at the new name before it fails.
761
+ - **Watches:** a typed watch (`fs.watch`) follows a rename in another tab of its root or a directory above it, as it does for a rename in this kernel ([#1356](https://github.com/ai-ecoverse/slicc-kernel/issues/1356)), so this tab's later changes there arrive.
762
+ - **Limits:** a program that falls more than 4096 renames behind, or holds a file by a name that goes through a symlink to the renamed path, keeps the old name, and its next write fails with `ENOENT` as before. A file removed while open isn't moved by a later rename of its old name.
763
+
757
764
  **Directory renames across tabs** ([#1140](https://github.com/ai-ecoverse/slicc-kernel/issues/1140)): a tab sharing the root sees a directory rename in another tab all at once, as the renaming kernel's own readers do.
758
765
  - **The notice:** before the first file moves, the renaming kernel tells the other kernels on the root, and waits until each has answered. A kernel answers once the calls it has in flight under either name, or on either parent, are done (a file rename into or out of the tree included, a symlink by the link's own path, calls that began before the other kernel joined too, and calls through a symlink into the tree, and an open write session there counts until it's closed). From then on, its calls under either name, and its listings of either parent, wait until the rename's Web Lock is released. So a listing in the other tab shows the old tree or the new one, never a mix, and every write that returned success there is in the new tree.
759
766
  - **A missed notice:** a kernel also checks the held Web Locks while it shares the root, and reads the recorded renames when it sees a rename lock it doesn't know. Only a call starts a check: it runs in the background once the last answer is 50 ms old, and a call waits for it only when that answer is 100 ms old. An idle tab makes no checks. A rename lock whose record isn't written yet holds the call until the record appears or the lock is released. A call through a symlink whose target is in the renaming tree waits as one under the tree does. So a kernel that missed the notice, or joined the root in the middle of a rename, trusts "no rename in flight" for at most 100 ms.
@@ -764,7 +771,7 @@ The proxy answers `200` with `Content-Type: application/vnd.slicc.raw-fetch`: a
764
771
  - **Alone on the root,** nothing else runs: no notice, no ack, and a call costs one counter.
765
772
  - **Limits:**
766
773
  - A kernel without `/proc/sys/kernel/slicc_rename_acks` (older than #1140) doesn't answer and isn't waited for ([#1309](https://github.com/ai-ecoverse/slicc-kernel/issues/1309)). It can still see a tree mid-move, and a write it has in flight when the rename starts can land in the old copy and be lost.
767
- - An open descriptor doesn't follow a rename of its directory by another process, in this tab or another ([#1257](https://github.com/ai-ecoverse/slicc-kernel/issues/1257)). Its next write fails with `ENOENT`, and nothing is lost silently.
774
+ - An open descriptor follows a rename by another process, of its file or of a directory above it ([#1257](https://github.com/ai-ecoverse/slicc-kernel/issues/1257), see "A rename by another process" above), also when the rename was in another tab.
768
775
  - A peer's call that is already in flight and then reaches into the renaming tree through a call of its own (a rename removing the name it replaces) waits for the rename, while the rename waits for that peer's answer. After the 10 s cap the rename fails with `EBUSY`, nothing has moved, and the peer's call goes on.
769
776
 
770
777
  A lookup follows at most 40 symlinks, as Linux does: the 41st is `ELOOP`, for WASI programs, JS programs and the kernel API alike, with or without `..` in a link's target. Emscripten programs resolve paths in their glue, which stops one link earlier, at 40.
@@ -1293,7 +1300,7 @@ The tools in `tools/cert-review/`:
1293
1300
  - and record, which re-hashes the candidate's artifact against the pack job's sha256.
1294
1301
  - **The verdict** goes into the commit status on the pull request's head and a comment on the pull request. Certified is green. A FAIL is red with the line, and the pull request needs a fix. **INCOMPLETE** (a job that didn't succeed, no verdict, a suite without results) is red with `re-run the failed jobs`, never a silent pass. Main moving on after a review doesn't trigger another one; a push does.
1295
1302
  - **Merge groups only look up.** On `merge_group`, the job lists the group's pull requests (its merge commits) and requires, for each one, a green status on the head being merged, or no behaviour change. Otherwise it's red, naming the pull request. Then it adds the group's downstream verdict. The group's `ci / test` covers the combined tree.
1296
- - **Downstream, once per merge group:** the `merge group pack` job packs the group's tree with `main`'s tools (`x.y.z-group.<run>`), and `downstream bios canary` calls slicc-bios's `canary.yml` at a pinned full sha (`42cdf3a8…`, never a tag) with that tarball, bound by artifact id, digest and sha256, on the `sweep`, `guard-prompt`, `guard-lagging`, `k4-upgrade`, `k4-agent-modes` and `link-serve` shards, each run once, on the runner the `canary runner` job picks: `.github/runner.mjs`'s `decide()` from `main`, as `ci.yml` uses it, gives `vars.CERT_CANARY_RUNNER` only to a merge group whose entries are all same-repo PRs, and `ubuntu-latest` otherwise, without the variable, or if the decider fails (bios honours a runner only for merge groups and pushes to main in ai-ecoverse, so rolling back is unsetting the variable). The verdict line names the shards, or their count past 32 characters (`6 shards`), so a status description (140 characters) keeps the reason. `downstream.mjs` judges its result (`downstreamOf` in `lib.mjs`), and the lookup requires it. The `merge group` job reads that result as `result.json` from the canary's own artifact, downloaded by the call's `artifact-id` with `digest-mismatch: error`, not from the `result` output: that output can be up to 512 KB, and one environment variable holds 128 KB, so passing it that way stopped the step from starting at all (`Argument list too long`, runs 39166647175 and 39168823799). A missing or unreadable artifact leaves no result, so the group is INCOMPLETE:
1303
+ - **Downstream, once per merge group:** the `merge group pack` job packs the group's tree with `main`'s tools (`x.y.z-group.<run>`), and `downstream bios canary` calls slicc-bios's `canary.yml` at a pinned full sha (`b59afdf6…`, never a tag) with that tarball, bound by artifact id, digest and sha256, on the `sweep`, `guard-prompt`, `guard-lagging`, `k4-upgrade`, `k4-agent-modes` and `link-serve` shards, each run once, on the runner the `canary runner` job picks: `.github/runner.mjs`'s `decide()` from `main`, as `ci.yml` uses it, gives `vars.CERT_CANARY_RUNNER` only to a merge group whose entries are all same-repo PRs, and `ubuntu-latest` otherwise, without the variable, or if the decider fails (bios honours a runner only for merge groups and pushes to main in ai-ecoverse, so rolling back is unsetting the variable). The verdict line names the shards, or their count past 32 characters (`6 shards`), so a status description (140 characters) keeps the reason. `downstream.mjs` judges its result (`downstreamOf` in `lib.mjs`), and the lookup requires it. The `merge group` job reads that result as `result.json` from the canary's own artifact, downloaded by the call's `artifact-id` with `digest-mismatch: error`, not from the `result` output: that output can be up to 512 KB, and one environment variable holds 128 KB, so passing it that way stopped the step from starting at all (`Argument list too long`, runs 39166647175 and 39168823799). A missing or unreadable artifact leaves no result, so the group is INCOMPLETE:
1297
1304
  - **PASS:** the candidate passes, `downstream: PASS bios … N pass`;
1298
1305
  - **FAIL:** a regression, or a failing test (by the counts, even without its rows) is **not certified**, and the group is red;
1299
1306
  - **INCOMPLETE** blocks too: no result, a result from another bios than the pinned one, a missing shard, a shard that exited without a failing test, a refused candidate, 0 tests run, or no test passed;