@ai-ecoverse/slicc-kernel 1.110.1 → 1.110.2
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 +26 -13
- package/dist/kernel-worker.js +43 -16
- package/dist/kernel-worker.js.map +2 -2
- package/dist/meta.js +10 -1
- package/dist/meta.js.map +2 -2
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +33 -15
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +1 -1
- package/dist/types/fs/meta.d.ts +6 -4
- package/dist/types/fs/opfs.d.ts +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -931,13 +931,18 @@ Releases are cut by semantic-release on every push to `main`, in two jobs, and o
|
|
|
931
931
|
|
|
932
932
|
### Certification (`cert-review`)
|
|
933
933
|
|
|
934
|
-
|
|
934
|
+
Every change is certified in its pull request, before it merges (`cert-review-pr.yml`, next section), and `cert-review` is a required check of the merge queue, so every commit on `main`, and every release built from it, was certified first. A release is then **verified on npm** (`release / verify on npm` in `release.yml`, `tools/cert-review/verify.mjs`), in code, for every tag the run made:
|
|
935
|
+
|
|
936
|
+
- It waits up to an hour for npm to serve the version (an accepted publish can take a while to appear), then downloads the tarball and checks `dist.integrity` (sha512), the SLSA v1 provenance (the tarball's sha512 is its subject; `release.yml` on `refs/heads/main`, a GitHub-hosted builder; `gitCommit` = `gitHead` = the tag, on `main`), and `npm audit signatures`. A version that `release-recover.mjs` skipped because npm's `latest` is newer (#837) has nothing to verify.
|
|
937
|
+
- It then writes `certified @ai-ecoverse/slicc-kernel@<v> sha256=<64 hex> (covers <first>–<v>; ci: <run>; pr-certified: <each merged pull request's cert-review>; known: <open known-issue issues>)` into the GitHub release's notes, between `<!-- cert-verify -->` markers. homescoop's ci reads exactly that prefix and re-hashes the npm tarball against it. `covers` starts after the last version npm serves, so a skipped version is covered by the next. Notes that already carry the identical line stay unchanged.
|
|
938
|
+
- A mismatch writes no line. The job is red with a `supply-chain alert`, and so is a line already in the notes with another sha256.
|
|
939
|
+
|
|
940
|
+
The tools in `tools/cert-review/`:
|
|
935
941
|
|
|
936
|
-
- **plan:** works out the version, the previous tag, the commit and the base (`base.mjs`). The base is the newest earlier release whose `cert-review` commit status is `success`, or, until there is one, the release `base.json` names (the cert lane's last line). CI never reads the npm `certified` dist-tag.
|
|
937
942
|
- **delta** (`delta.mjs`): lists the pull requests merged between the two tags (their merge commits, plus `pr` for a planted branch). Each gets its conventional-commit kind (`fix`, `feat`, `perf` and `revert` are behaviour changes) and the integration test files and titles it adds or changes. The titles are read from the pull request's patch: the added `test(…)` lines and the hunk headers git puts above changed lines. Escapes are decoded as JavaScript does. A title git cut short matches as a prefix, and each `${…}` in a template-literal title matches anything there, so `` `a forked child of a ${kind} WASIX program…` `` matches the rows it ran as. A title made only of placeholders is skipped.
|
|
938
943
|
- **suite** (`suite.mjs`, `reporter.mjs`) runs on the bench pool, without secrets. It takes the tag's tree with its own dependencies and Chromium, and replaces `dist/` with the **published npm tarball** (for a planted branch, its own `npm pack`). The test command runs in a session of its own (`detached`), and `suite.mjs` notes a `SIGINT` (`ignored` in its summary) instead of dying of it. That way a stray signal to the step's process group can't cut the run short, while a `SIGTERM` (a real cancel) still ends it. The log is written as the run goes, and a `SIGTERM` still writes the summary with the results so far (`exit: 'signal SIGTERM'`). A run that left only its `.jsonl` (the runner was lost) is read from that, as `exit: 'no summary'`. The NEGATIVE step runs even when the release suite's step failed. Before it reports, `suite.mjs` re-runs once just the test files that left no result (no rows, a cancelled row, or a file-level failure, such as a lost process), with a second `node --test` over those files, and merges their results (`retried`, `firstExit`, `unfinished` in the summary). That holds for the release suite and for each NEGATIVE run. A fix whose test passes on its baseline stays `no-negative`.
|
|
939
|
-
- The
|
|
940
|
-
- The base job runs the
|
|
944
|
+
- The candidate job runs the pull request's whole `test/integration/` suite on its packed build.
|
|
945
|
+
- The base job runs `main`'s own suite on `main`'s packed build, then the NEGATIVE (#861): `main`'s tree, with all of the pull request's `test/**` changes applied (test files, helpers and fixtures), on `main`'s build. So a fix in `tools/` fails there just as a fix in `src/` does. A test file that fails to load on `main` (a module or export that only the pull request adds) leaves no result for its titles, which is `prev-not-run`, never a NEGATIVE.
|
|
941
946
|
- Each run uses the tree's own `npm test` command, with a reporter that records every test and subtest with its status and error.
|
|
942
947
|
- **kernel-compat** (`compat.mjs`) supplies the base-set and catalog evidence: homescoop's kernel-compat run for the release.
|
|
943
948
|
- Its reference kernel is the certified base, not slicc-bios's pin. cert-review passes `--reference <base>`, and `kernel-compat.yml` takes a `reference` input that it hands to homescoop's workflow (homescoop#504), recorded in compat.json. So it uses a dispatched run named `Kernel compat v<version> ref <base>` (kernel-compat has no run of its own after Release). Its `candidate` job checks that npm serves the version, or takes a packed tarball from another run (`run`, `artifact`, `sha256`, for PR builds `x.y.z-pr.N.M`), and prints `@ai-ecoverse/slicc-kernel@<version>`, which compat.mjs matches. It dispatches one when none has finished and waits up to 150 minutes; package reruns carry the same reference.
|
|
@@ -948,24 +953,22 @@ Releases are cut by semantic-release on every push to `main`, in two jobs, and o
|
|
|
948
953
|
- `negative` (a fix's test fails on the previous dist and passes on the release) or `passes` (a feature's test passes);
|
|
949
954
|
- otherwise a finding: `no-test`, `no-negative`, `fails-on-release`, `not-run`, `prev-not-run` or `no-titles`.
|
|
950
955
|
|
|
951
|
-
It also diffs the two suites test by test (`regressed`, `newFailing`, `fixed`, `stillFailing`, `removed`), flags a suite run that didn't finish (`suite-incomplete`), and adds the kernel-compat findings. A release suite that didn't finish is a `missing-evidence` finding that always blocks `certified`, with the remedy `re-run the failed jobs`. That covers a signal, a timeout, a cut-off run, no results, or any test file without a result. Its base tests that didn't run are `unrun`, never `removed`. INCOMPLETE is not FAIL: record writes `cert-ci: INCOMPLETE <name>@<version> (<n> files without results: …; re-run the failed jobs)`,
|
|
956
|
+
It also diffs the two suites test by test (`regressed`, `newFailing`, `fixed`, `stillFailing`, `removed`), flags a suite run that didn't finish (`suite-incomplete`), and adds the kernel-compat findings. A release suite that didn't finish is a `missing-evidence` finding that always blocks `certified`, with the remedy `re-run the failed jobs`. That covers a signal, a timeout, a cut-off run, no results, or any test file without a result. Its base tests that didn't run are `unrun`, never `removed`. INCOMPLETE is not FAIL: record writes `cert-ci: INCOMPLETE <name>@<version> (<n> files without results: …; re-run the failed jobs) (ci: …; known: …)`, with the known issues as on a certified line, and the `cert-review` status is `error`, which means re-run, where `failure` means a real defect. A defect found in code (a compat crash) still FAILs. A base suite that is missing or didn't finish is a `missing-evidence` finding. It blocks `certified` in code (record FAILs it) unless the release suite is complete and all-pass and the delta removes or renames no test file and drops no test title (`dropped` per row). Either way the line ends with `base suite incomplete (<n> results)`. Claude, on Bedrock with the key only in that step, then reads those artifacts and `instructions.txt` (which has the roll-forward FAIL list verbatim) and writes `verdict.json` and `comment.md`. Its tools are reading files, `gh pr view`, and `gh issue list`/`view`. `gh issue create` is allowed only for a release on `main`; otherwise, and for an issue in another repository, it lists the issue under `elsewhere` for the cert lane to file.
|
|
952
957
|
- **record** (`record.mjs`), in code, with read-only permissions:
|
|
953
958
|
- It re-downloads the tarball and checks `dist.integrity` (sha512) and the sha256, the SLSA v1 provenance (the tarball is its subject; `release.yml`, `refs/heads/main`, a GitHub-hosted builder; commit = `gitHead` = the tag), `npm audit signatures`, and that the commit is on `main`. A problem there, a `compat-crash`, or a planted build is a FAIL whatever the review says.
|
|
954
|
-
- It adds the **known issues as of the release**: every issue labelled `known-issue` (open or closed) and every one in `known.json`, unless it was fixed before the release. That holds when `known.json`'s `fixedIn` names an earlier or equal version, or the merged pull request that closed it is an ancestor of the tag, or it was closed (with no closing pull request) before the release commit. An issue closed as not planned or as a duplicate is dropped.
|
|
959
|
+
- It adds the **known issues as of the release**: every issue labelled `known-issue` (open or closed) and every one in `known.json`, unless it was fixed before the release. An issue in another repository, whether listed or named by the reviewer, counts only while it is open. That holds when `known.json`'s `fixedIn` names an earlier or equal version, or the merged pull request that closed it is an ancestor of the tag, or it was closed (with no closing pull request) before the release commit. An issue closed as not planned or as a duplicate is dropped.
|
|
955
960
|
- The line is `certified @ai-ecoverse/slicc-kernel@<v> sha256=<…> (ci: <run>; known: …)`. Any test gaps from the findings (`no-test`, `no-negative`, `prev-not-run`, `no-titles`, `not-run`) are appended in code as `test gaps: #<pr> <kind>`, so a fix without a NEGATIVE is never certified silently.
|
|
956
|
-
- **
|
|
957
|
-
|
|
958
|
-
Moving the `certified` dist-tag stays manual. Automating it needs an npm token that may change dist-tags, in a step that re-hashes the tarball and tags only on a `certified` line.
|
|
961
|
+
- **verify** (`verify.mjs`) is the `release / verify on npm` job above.
|
|
959
962
|
|
|
960
|
-
### Certification in the pull request (`cert-review
|
|
963
|
+
### Certification in the pull request (`cert-review`)
|
|
961
964
|
|
|
962
|
-
`.github/workflows/cert-review-pr.yml` certifies a pull request before it merges, so `main`, and with it npm's `latest`, only gets certified changes.
|
|
965
|
+
`.github/workflows/cert-review-pr.yml` certifies a pull request before it merges, so `main`, and with it npm's `latest`, only gets certified changes. It reports as the `cert-review` commit status, a required check of `main`'s merge queue. Every same-repo pull request gets one (`not behaviour`, `waits for merge-ok`, the review's verdict, or the self-guard's hold). **A pull request from a fork gets none**, because fork code never reaches the pools or the key, so it can't enter the queue: only Lars merges one, after reading it.
|
|
963
966
|
|
|
964
|
-
- **Which pull requests:** those that change shipped behaviour, by a conservative path filter (`behaviourFiles` in `lib.mjs`): `src/`, `build.mjs`, `package.json` and its lock, `tsconfig*.json`, `.releaserc*`, `tools/release-recover.mjs`, the workflows that build or publish (`release.yml`, `ci.yml`), and the reviewer's own code (`tools/cert-review/`, `cert-review*.yml`, `.github/runner.mjs`). **Main's code decides:** every job checks out the pull request's base and fetches only the trees under test, so the path filter, pack, suite, findings, the instructions and record all run from `main`. A pull request that changes the reviewer is reviewed by `main`'s reviewer. The workflow file of a `pull_request` run still comes from the pull request, so **pull
|
|
967
|
+
- **Which pull requests:** those that change shipped behaviour, by a conservative path filter (`behaviourFiles` in `lib.mjs`): `src/`, `build.mjs`, `package.json` and its lock, `tsconfig*.json`, `.releaserc*`, `tools/release-recover.mjs`, the workflows that build or publish (`release.yml`, `ci.yml`), and the reviewer's own code (`tools/cert-review/`, `cert-review*.yml`, `.github/runner.mjs`). **Main's code decides:** every job checks out the pull request's base and fetches only the trees under test, so the path filter, pack, suite, findings, the instructions and record all run from `main`. A pull request that changes the reviewer is reviewed by `main`'s reviewer. The workflow file of a `pull_request` run still comes from the pull request, so a change to the reviewer is held by the **certification self-guard**, the shared composite action `slicc-shared-web/.github/actions/cert-self-guard` at a pinned sha. Its light `self-guard` job runs on every pull request event and every merge group. When the diff touches the reviewer (`.github/workflows/cert*.yml`, `tools/cert-review/**`, `.github/runner.mjs` and the guard's own paths), `cert-review` is red with `reviewer change: needs coordinator review (cert-review-approved on this head)` and no review runs. The label is the Kernel Coordinator's alone: they add `cert-review-approved` after reading the change, and the review then starts with `merge-ok`. The approval is bound to the head's arrival on GitHub, and a push removes the label. A merge group that holds an unapproved reviewer change is red too. If the guard doesn't finish, the status is an error that asks to re-run the failed jobs. **The residual:** the pull request's YAML still decides what runs, so the guard catches an accidental edit, not a deliberate rewrite of this workflow. The pull request that adds this workflow can't pass its own check, because its base has no tools yet. Any other pull request gets a green status that says `not behaviour: <files>`, so the skip is recorded.
|
|
965
968
|
- **When:** only for a same-repo pull request with `merge-ok`, when the label is added and on every push while it is set. Each pull request has its own concurrency group, and a newer push cancels the older review. A behaviour change without `merge-ok` gets a red status, `waits for merge-ok to be reviewed`. Other labels don't start or cancel anything. Fork code never reaches the pools or the key.
|
|
966
969
|
- **What:** the checkout is the event's default `refs/pull/N/merge`, the pull request merged onto `main` at review time, so a review is of what would land, without pushing to the branch. Both sides are packed in the same run, on a GitHub-hosted runner, with no secrets: the candidate (the merge ref, `x.y.z-pr.N.<run>`) and the base (its first parent, `main`). Then:
|
|
967
970
|
- the candidate's whole suite, and the base's;
|
|
968
|
-
- the pull request's new or changed test files on the
|
|
971
|
+
- the pull request's new or changed test files on `main`'s tree, with the pull request's `test/**` applied, and `main`'s build (the NEGATIVE: a fix's test must fail on `main` and pass on the candidate);
|
|
969
972
|
- kernel-compat on the candidate's artifact (#812), against npm's `latest`;
|
|
970
973
|
- the review (Claude through `claude-code-action/base-action`, with the Bedrock key in that step only, no issue filing);
|
|
971
974
|
- and record, which re-hashes the candidate's artifact against the pack job's sha256.
|
|
@@ -1100,6 +1103,16 @@ A threaded WASIX C program for `test/integration/thread-signal.test.mjs` (#520).
|
|
|
1100
1103
|
|
|
1101
1104
|
**Build:** `WASIX_SYSROOT_28=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 (`-pthread`) and binaryen's `wasm-opt` on `PATH`. `bin/sigthread.wasm` is 44,788 bytes, sha256 `5781fa39d0a0032cd9b214b71de1f15f589b40e4cecd6862362048dd27f0db56`.
|
|
1102
1105
|
|
|
1106
|
+
### `spintest`
|
|
1107
|
+
|
|
1108
|
+
A WASIX C program and a JS command for `test/integration/transport-spin.test.mjs`, which guards the sync transport's paths from #856 (#864).
|
|
1109
|
+
- `spintest rounds N` does N 1-byte write+read rounds on a pipe, a different byte each round, and prints `N rounds in order` if every byte came back as sent. These are fast replies, which take the transport's spin.
|
|
1110
|
+
- `spintest wait` prints `waiting` and blocks in a 1-byte `read` of its terminal, a reply that comes only when the test types. The test measures its own process's CPU (all threads) while the read blocks.
|
|
1111
|
+
- `spintest big M` writes an M MiB file and reads it back in 1 MiB `read` calls, each of which must be whole and match. A 1 MiB read fills the transport window exactly.
|
|
1112
|
+
- `spinread PATH` (`bin/spinread.mjs`, a JS command) reads a whole file through the synchronous `ctx.sync.fs.readFile` and prints its length and a 31-hash. A file over 1 MiB is a reply larger than the window, so it comes in several chunks.
|
|
1113
|
+
|
|
1114
|
+
**Build:** `WASIX_SYSROOT_28=/path/to/wasix-sysroot ./build.sh`, with `wasixcc` 0.4.7 and binaryen's `wasm-opt` on `PATH`. `bin/spintest.wasm` is 50,095 bytes, sha256 `be7659422f187a97fac412ac1dd2ec9d412e8dcd067da88157960352ded6178b`.
|
|
1115
|
+
|
|
1103
1116
|
### `longwait`
|
|
1104
1117
|
|
|
1105
1118
|
A WASIX C program for `test/integration/long-timers.test.mjs` (#767).
|
package/dist/kernel-worker.js
CHANGED
|
@@ -219,13 +219,22 @@ var IndexedDbMeta = class {
|
|
|
219
219
|
this.cache.set(path, next3 ?? null);
|
|
220
220
|
}
|
|
221
221
|
async updateMany(changes) {
|
|
222
|
+
if (changes.length === 0) return;
|
|
223
|
+
this.settleRemoves();
|
|
222
224
|
const [transaction, store] = this.transaction("readwrite");
|
|
225
|
+
const next3 = /* @__PURE__ */ new Map();
|
|
223
226
|
for (const { path, change } of changes) {
|
|
224
227
|
const request2 = store.get(path);
|
|
225
|
-
request2.onsuccess = () =>
|
|
228
|
+
request2.onsuccess = () => {
|
|
229
|
+
const entry = change(request2.result);
|
|
230
|
+
next3.set(path, entry ?? null);
|
|
231
|
+
if (entry) store.put(entry);
|
|
232
|
+
else store.delete(path);
|
|
233
|
+
};
|
|
226
234
|
}
|
|
227
235
|
await settle(transaction, () => void 0);
|
|
228
236
|
this.changed({ paths: changes.map(({ path }) => path) });
|
|
237
|
+
for (const [path, entry] of next3) this.cache.set(path, entry);
|
|
229
238
|
}
|
|
230
239
|
close() {
|
|
231
240
|
this.channel.close();
|
|
@@ -1021,6 +1030,12 @@ function sidecar(meta) {
|
|
|
1021
1030
|
});
|
|
1022
1031
|
const names = meta.fresh ? [...META_OPS, "fresh"] : META_OPS;
|
|
1023
1032
|
const ops = Object.fromEntries(names.map((op) => [op, guarded2(op)]));
|
|
1033
|
+
const { updateMany } = meta;
|
|
1034
|
+
ops.updateMany = updateMany ? (changes) => updateMany.call(meta, changes).catch((err) => {
|
|
1035
|
+
throw translate(err, changes[0]?.path ?? "/", "sidecar", false);
|
|
1036
|
+
}) : async (changes) => {
|
|
1037
|
+
for (const { path, change } of changes) await ops.update(path, change);
|
|
1038
|
+
};
|
|
1024
1039
|
if (meta.absent) ops.absent = meta.absent.bind(meta);
|
|
1025
1040
|
const { removeFile } = meta;
|
|
1026
1041
|
if (removeFile)
|
|
@@ -1503,14 +1518,20 @@ var OpfsFs = class {
|
|
|
1503
1518
|
this.flushing.file.unref?.();
|
|
1504
1519
|
}
|
|
1505
1520
|
async flushFileTimes() {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1521
|
+
const pending = [...this.fileTimes];
|
|
1522
|
+
const stamps = [];
|
|
1523
|
+
for (const [path] of pending) stamps.push(await this.modified(path).catch(() => 0));
|
|
1524
|
+
await this.batched(
|
|
1525
|
+
pending.map(([path, ms], i) => ({
|
|
1509
1526
|
path,
|
|
1510
|
-
(entry) => this.fileTimes.get(path) !== ms || this.explicitWins(entry, ms) ? entry : { ...entry, path, mtimeMs: ms, mtimeFor, ctimeMs: ms }
|
|
1511
|
-
)
|
|
1527
|
+
change: (entry) => this.fileTimes.get(path) !== ms || this.explicitWins(entry, ms) ? entry : { ...entry, path, mtimeMs: ms, mtimeFor: stamps[i], ctimeMs: ms }
|
|
1528
|
+
}))
|
|
1529
|
+
);
|
|
1530
|
+
for (const [path, ms] of pending)
|
|
1512
1531
|
if (this.fileTimes.get(path) === ms) this.fileTimes.delete(path);
|
|
1513
|
-
|
|
1532
|
+
}
|
|
1533
|
+
batched(changes) {
|
|
1534
|
+
return this.meta.updateMany(changes);
|
|
1514
1535
|
}
|
|
1515
1536
|
timesOf(path) {
|
|
1516
1537
|
return this.dirTimes.get(path) ?? this.fileTimes.get(path);
|
|
@@ -1542,15 +1563,21 @@ var OpfsFs = class {
|
|
|
1542
1563
|
});
|
|
1543
1564
|
}
|
|
1544
1565
|
async flushDirTimes() {
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1566
|
+
while (this.dirDirty.size > 0) {
|
|
1567
|
+
const pending = [];
|
|
1568
|
+
for (const path of this.dirDirty) {
|
|
1569
|
+
const ms = this.dirTimes.get(path);
|
|
1570
|
+
if (ms !== void 0) pending.push([path, ms]);
|
|
1571
|
+
}
|
|
1572
|
+
this.dirDirty.clear();
|
|
1573
|
+
await this.batched(
|
|
1574
|
+
pending.map(([path, ms]) => ({
|
|
1575
|
+
path,
|
|
1576
|
+
change: (entry) => (entry?.timesAt ?? -1) >= ms ? entry : { ...entry, path, mtimeMs: ms, mtimeFor: 0, ctimeMs: ms }
|
|
1577
|
+
}))
|
|
1552
1578
|
);
|
|
1553
|
-
|
|
1579
|
+
for (const [path, ms] of pending)
|
|
1580
|
+
if (this.dirTimes.get(path) === ms) this.dirTimes.delete(path);
|
|
1554
1581
|
}
|
|
1555
1582
|
}
|
|
1556
1583
|
async snapshot(handle) {
|
|
@@ -6146,7 +6173,7 @@ function satisfies(version, range2) {
|
|
|
6146
6173
|
}
|
|
6147
6174
|
|
|
6148
6175
|
// src/version.ts
|
|
6149
|
-
var KERNEL_VERSION = true ? "1.110.
|
|
6176
|
+
var KERNEL_VERSION = true ? "1.110.2" : "0.0.0-dev";
|
|
6150
6177
|
var OS_RELEASE = "6.0.0-slicc";
|
|
6151
6178
|
function procVersion() {
|
|
6152
6179
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|