@ai-ecoverse/slicc-kernel 1.141.1 → 1.141.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 +3 -8
- package/dist/kernel-worker.js +41 -10
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +96 -54
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +41 -10
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +96 -54
- package/dist/process-worker.js.map +3 -3
- package/dist/types/kernel/proc-info.d.ts +2 -0
- package/dist/types/launcher.d.ts +2 -1
- package/dist/types/process/wasi/wasi-procfs.d.ts +9 -0
- package/dist/types/realm/sync-fs-wire.d.ts +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1121,7 +1121,7 @@ A character device (`0666`), there in every `users` mode, through which programs
|
|
|
1121
1121
|
|
|
1122
1122
|
### `/proc`
|
|
1123
1123
|
|
|
1124
|
-
Every process of the kernel has `/proc/<pid>/` with `cmdline`, `comm`, `stat`, `statm` and `status`, in the formats procps reads, and `exe`, a link to the file the process was started from: the glue or `.wasm` that ran, as resolved (a copy run by its own path names the copy, a symlink its target) (#1264). For WASI and WASIX programs, as for Emscripten ones, `stat`, `open` and reading `/proc/self/exe` act on the program file, while `lstat` and `readlink` show the link (#1273). As on Linux without ptrace access, reading another process's `exe` link is `EACCES` unless the reader is root or its uid and gid match all of the target's real, effective and saved ids; `cmdline` and `status` stay readable, and `/proc/self` is a link to the `/proc/<pid>` of the process that reads it, as on Linux (`readlink /proc/self` is its pid, the one it was exec'd under included). They come from the kernel's process table when they are opened, so a terminal sees the processes of every client. A process that exits while a program reads the `/proc` directory doesn't fail or cut short the listing: its pid is still listed, and opening its files fails with `ENOENT`, which `ps`, `pgrep` and `pidof` skip, as on Linux; an exec'd program shows under the pid its parent knows. `/proc/uptime`, `/proc/loadavg`, `/proc/stat` and `/proc/meminfo` are there too, and `/proc/mounts`. `/proc/version` names the kernel, Linux-shaped, with the slicc-kernel version that built it: `Linux version 6.0.0-slicc (slicc-kernel <version>) #1 SMP PREEMPT_DYNAMIC`. `/proc/sys/kernel/osrelease` is `6.0.0-slicc` and `/proc/sys/kernel/ostype` is `Linux`. The version is the one npm publishes, set when the package is built (`0.0.0-dev` when run from the sources). A JS program reads it as `ctx.kernel.version`. A process with no parent in the kernel (one a page, a client or the Node entry started) has parent 1, as children of init do, in `getppid()`, `/proc/<pid>/stat` and `ps` alike, and so does a process whose parent has ended, waited for or not (`getppid()` asks the kernel each time; a shell's `$PPID` keeps the value it started with, as on Linux). Pids, parents, process groups, sessions, command lines, terminals, start and boot times are real, and so are the credentials in `status` (`Uid:`, `Gid:`, `Groups:`; a process with a tag, see `kernel.run`, also has `Tag:\t<tag>` after `Gid:`, shown to root and to the same uid only, with newlines, tabs and carriage returns written `\n`, `\t` and `\r`) and each process's memory: the size of its wasm memory (`VSZ` and `RSS` alike, as wasm has no paging), which `/proc/meminfo` counts as used. CPU times are measured per process, its threads and the images it exec'd included: time a worker spends waiting for the kernel's answer is system time (at most 20 ms of each call; the rest of a longer one, such as a write into a full pipe, counts as blocked), time it spends blocked (a read, a `poll` or `select` that waits, a sleep, `wait`, a futex, while stopped) is neither, from the moment the call actually starts to wait (a call that could wait but answers at once, such as a `select` with a zero timeout or a ready descriptor, a `waitpid` with `WNOHANG`, or a futex wait whose value has already changed, is system time), and the rest is user time; a process's times stop when it ends, a call still in flight then adds nothing, so a loop that never calls the kernel shows as user time while it runs; they show in `/proc/<pid>/stat`, `ps` and `ps()`, and the times of the children a process has waited for (theirs and their own waited-for children's, as POSIX counts them) show as its `cutime` and `cstime` in `/proc/<pid>/stat`; a stopped process is `T` there, in `/proc/<pid>/status` (`T (stopped)`) and in `ps()`; a WASI or WASIX program's `CLOCK_PROCESS_CPUTIME_ID` and `CLOCK_THREAD_CPUTIME_ID` (`clock_time_get`) count only the time its thread runs, its own waits and blocked kernel calls left out (a sleep is not CPU time), per thread for now, so a threaded program's process clock is its calling thread's; and `/proc/stat` counts every process of the kernel, the reaped ones included, and `/proc/loadavg` averages the processes not blocked, sampled every 5 seconds. A JS program's time isn't measured yet. `MemTotal` (from `navigator.deviceMemory`, else 4 GiB) is a placeholder. A process that isn't root sees only its own user's processes (see [Users](#users)). WASI and WASIX programs see the same `/proc`, read-only (opening a file in it for writing, or linking one of its files elsewhere, fails with `EROFS`), from every thread, also through a symlink that points into it (where every change, `mkdir`, `rename`, `unlink`, `chmod`, a hard link from it or into it, and so on, fails too: with `EROFS` when the root file system has a physical `/proc`, which stays hidden and untouched, and otherwise with the `ENOENT` of the missing directory, since that check is only paid for when a physical `/proc` exists; with one, a symlink whose target passes `..` after a file or a missing name, such as `/f/../proc`, fails with `ENOTDIR` or `ENOENT`, as on Linux), and `/` lists it, so a WASI program gets a preopen for it; `/proc/self` is the program's own pid there too (the pid an exec'd program took over). WASI and WASIX programs also have `/proc/<pid>/task/<tid>/stat` for each of a process's threads (the main thread is tid 1), whose state is `S` while the thread is blocked in the kernel (a `sem_wait`, a blocking read, a sleep, a futex) and `R` otherwise (`T` and `Z` as for the process). JS programs read the same `/proc` through `ctx.fs` and `ctx.open` (see [JS programs](#js-programs)). Neither has `/proc/<pid>/fd` yet.
|
|
1124
|
+
Every process of the kernel has `/proc/<pid>/` with `cmdline`, `comm`, `stat`, `statm` and `status`, in the formats procps reads, and `exe`, a link to the file the process was started from: the glue or `.wasm` that ran, as resolved (a copy run by its own path names the copy, a symlink its target) (#1264). For WASI and WASIX programs, as for Emscripten ones, `stat`, `open` and reading `/proc/self/exe` act on the program file, while `lstat` and `readlink` show the link (#1273). A symlink to it is followed the same way, and once the program's name is moved away or holds another file, `exe` no longer opens it: `readlink` ends in ` (deleted)` and `stat` and `open` are `ENOENT` (#1317). As on Linux without ptrace access, reading another process's `exe` link is `EACCES` unless the reader is root or its uid and gid match all of the target's real, effective and saved ids; `cmdline` and `status` stay readable, and `/proc/self` is a link to the `/proc/<pid>` of the process that reads it, as on Linux (`readlink /proc/self` is its pid, the one it was exec'd under included). They come from the kernel's process table when they are opened, so a terminal sees the processes of every client. A process that exits while a program reads the `/proc` directory doesn't fail or cut short the listing: its pid is still listed, and opening its files fails with `ENOENT`, which `ps`, `pgrep` and `pidof` skip, as on Linux; an exec'd program shows under the pid its parent knows. `/proc/uptime`, `/proc/loadavg`, `/proc/stat` and `/proc/meminfo` are there too, and `/proc/mounts`. `/proc/version` names the kernel, Linux-shaped, with the slicc-kernel version that built it: `Linux version 6.0.0-slicc (slicc-kernel <version>) #1 SMP PREEMPT_DYNAMIC`. `/proc/sys/kernel/osrelease` is `6.0.0-slicc` and `/proc/sys/kernel/ostype` is `Linux`. The version is the one npm publishes, set when the package is built (`0.0.0-dev` when run from the sources). A JS program reads it as `ctx.kernel.version`. A process with no parent in the kernel (one a page, a client or the Node entry started) has parent 1, as children of init do, in `getppid()`, `/proc/<pid>/stat` and `ps` alike, and so does a process whose parent has ended, waited for or not (`getppid()` asks the kernel each time; a shell's `$PPID` keeps the value it started with, as on Linux). Pids, parents, process groups, sessions, command lines, terminals, start and boot times are real, and so are the credentials in `status` (`Uid:`, `Gid:`, `Groups:`; a process with a tag, see `kernel.run`, also has `Tag:\t<tag>` after `Gid:`, shown to root and to the same uid only, with newlines, tabs and carriage returns written `\n`, `\t` and `\r`) and each process's memory: the size of its wasm memory (`VSZ` and `RSS` alike, as wasm has no paging), which `/proc/meminfo` counts as used. CPU times are measured per process, its threads and the images it exec'd included: time a worker spends waiting for the kernel's answer is system time (at most 20 ms of each call; the rest of a longer one, such as a write into a full pipe, counts as blocked), time it spends blocked (a read, a `poll` or `select` that waits, a sleep, `wait`, a futex, while stopped) is neither, from the moment the call actually starts to wait (a call that could wait but answers at once, such as a `select` with a zero timeout or a ready descriptor, a `waitpid` with `WNOHANG`, or a futex wait whose value has already changed, is system time), and the rest is user time; a process's times stop when it ends, a call still in flight then adds nothing, so a loop that never calls the kernel shows as user time while it runs; they show in `/proc/<pid>/stat`, `ps` and `ps()`, and the times of the children a process has waited for (theirs and their own waited-for children's, as POSIX counts them) show as its `cutime` and `cstime` in `/proc/<pid>/stat`; a stopped process is `T` there, in `/proc/<pid>/status` (`T (stopped)`) and in `ps()`; a WASI or WASIX program's `CLOCK_PROCESS_CPUTIME_ID` and `CLOCK_THREAD_CPUTIME_ID` (`clock_time_get`) count only the time its thread runs, its own waits and blocked kernel calls left out (a sleep is not CPU time), per thread for now, so a threaded program's process clock is its calling thread's; and `/proc/stat` counts every process of the kernel, the reaped ones included, and `/proc/loadavg` averages the processes not blocked, sampled every 5 seconds. A JS program's time isn't measured yet. `MemTotal` (from `navigator.deviceMemory`, else 4 GiB) is a placeholder. A process that isn't root sees only its own user's processes (see [Users](#users)). WASI and WASIX programs see the same `/proc`, read-only (opening a file in it for writing, or linking one of its files elsewhere, fails with `EROFS`), from every thread, also through a symlink that points into it (where every change, `mkdir`, `rename`, `unlink`, `chmod`, a hard link from it or into it, and so on, fails too: with `EROFS` when the root file system has a physical `/proc`, which stays hidden and untouched, and otherwise with the `ENOENT` of the missing directory, since that check is only paid for when a physical `/proc` exists; with one, a symlink whose target passes `..` after a file or a missing name, such as `/f/../proc`, fails with `ENOTDIR` or `ENOENT`, as on Linux), and `/` lists it, so a WASI program gets a preopen for it; `/proc/self` is the program's own pid there too (the pid an exec'd program took over). WASI and WASIX programs also have `/proc/<pid>/task/<tid>/stat` for each of a process's threads (the main thread is tid 1), whose state is `S` while the thread is blocked in the kernel (a `sem_wait`, a blocking read, a sleep, a futex) and `R` otherwise (`T` and `Z` as for the process). JS programs read the same `/proc` through `ctx.fs` and `ctx.open` (see [JS programs](#js-programs)). Neither has `/proc/<pid>/fd` yet.
|
|
1125
1125
|
|
|
1126
1126
|
The kernel writes `/etc/passwd` and `/etc/group` when it starts (see [Users](#users)). While the root has none of them, or no `/etc/mtab`, reading them gets per-process ones: root is uid 0 and the realm user, uid 1000, is named after `USER`, or `web_user` (Emscripten's default) without one, so it matches `$USER`, with `HOME` as its home, so `id`, `whoami`, `ls -l` and `ps` show names. Nothing is written to OPFS, and a real file there wins.
|
|
1127
1127
|
|
|
@@ -1233,11 +1233,7 @@ Releases are cut by semantic-release on every push to `main`, in two jobs, and o
|
|
|
1233
1233
|
|
|
1234
1234
|
### Certification (`cert-review`)
|
|
1235
1235
|
|
|
1236
|
-
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.
|
|
1237
|
-
|
|
1238
|
-
- It waits up to an hour for npm to serve the version (an accepted publish can take a while to appear; 1.125.4 took 55 min). `release-recover` publishes a version it has to recover with a plain `npm publish --provenance` from the folder (so the packument keeps `gitHead`) and logs npm's own exit code and output; it fails only on npm's own non-zero exit, and npm's "cannot publish over the previously published version" on a re-run counts as already done. Whether npm serves the version is this job's to check. A version npm still doesn't serve after the hour, and doesn't list, while a newer one of ours is its `latest`, ends with `verify: SKIPPED <name>@<version>: never served by npm; superseded by <latest>` (the gate takes SKIPPED like PASS); only after the whole wait, since a slow version can appear late and must not go unverified. SKIPPED writes no certified line, so nothing downstream reads it as certified; if npm serves a skipped version later, the coordinator's merge watchdog notes it within 10 min (any version that appears below npm's `latest`), and the coordinator re-runs `release / verify on npm` in that version's Release run for the normal verdict, the certified line or the supply-chain alert. One npm still doesn't serve, with nothing newer or a version list it can't read, is a "not served yet, re-run" failure, never a supply-chain alert. It 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`. npm serves a version's attestations a little after the version itself, so a 404 from the attestations, a missing `dist.attestations`, an `npm view` or network error while looking for them, or `npm audit signatures`' E404 on them, is retried within the same hour: it's a supply-chain alert only once the version has been served for 30 min, its packument has stopped changing for 10, and the attestations still answer 404, or the hour has run out. Any other status, or attestations that don't match, fail at once. A version that `release-recover.mjs` skipped because npm's `latest` is newer (#837) has nothing to verify.
|
|
1239
|
-
- 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 at its merge>; 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. A pull request's `cert-review` is the newest finished one (success, failure or error) on its head at or before its merge, or the newest pending one if none had finished. A re-review that a later `ready_for_review`, `reopened` or `cert-review-approved` event starts on the same head, still pending at the merge or ending after it, doesn't change what the merge queue saw, and a status posted after the merge can't change a release's line.
|
|
1240
|
-
- 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.
|
|
1236
|
+
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. There is no post-publish verification job: being merged on `main` is the certification, and consumers read npm's own provenance (SLSA v1 from `release.yml` on `refs/heads/main`, a GitHub-hosted builder) and integrity, as bios's pin check does. `release-recover` publishes a version semantic-release left unpublished with a plain `npm publish --provenance`, failing only on npm's own non-zero exit.
|
|
1241
1237
|
|
|
1242
1238
|
The tools in `tools/cert-review/`:
|
|
1243
1239
|
|
|
@@ -1260,14 +1256,13 @@ The tools in `tools/cert-review/`:
|
|
|
1260
1256
|
- 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.
|
|
1261
1257
|
- 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.
|
|
1262
1258
|
- The line is `certified @ai-ecoverse/slicc-kernel@<v> sha256=<…> (ci: <run>; known: …)`. Any test gaps from the findings (`no-test`, `no-negative`, `no-titles`, `not-run`) are appended in code as `test gaps: #<pr> <kind>`, so a fix without a NEGATIVE is never certified silently.
|
|
1263
|
-
- **verify** (`verify.mjs`) is the `release / verify on npm` job above.
|
|
1264
1259
|
|
|
1265
1260
|
### Certification in the pull request (`cert-review`)
|
|
1266
1261
|
|
|
1267
1262
|
`.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`, `draft: not reviewed`, 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.
|
|
1268
1263
|
|
|
1269
1264
|
- **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`. The merge group's lookup loads its tools from the protected branch itself (`merge_group.base_ref`), not from the group's `base_sha`, which can be the speculative commit of the group ahead and carry the reviewer changes of the pull requests queued before it; `base_sha` is only the lookup's input. 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 (on a pull request that isn't a draft). 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. Node's glob `**` skips dot segments, so the guard's paths list the dot forms of each reviewer directory too (`REVIEWER_GLOBS` in `lib.mjs`), and the job's `cert` step reads the guard's receipt and fails closed: a reviewer path the globs still miss (any path under `scripts/cert/`, `tools/cert-review/` or the guard's directory, by prefix) without the label on that head is a FAIL, and a PASS without a receipt, or with a member that lacks its `paths` or `touched` list, is INCOMPLETE. 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.
|
|
1270
|
-
- **A gate's silence never reads as a pass:** every tool a workflow gates on (`pr.mjs plan`, `same`, `lookup` and `notice`, `downstream.mjs`, `same.mjs`, `pack.mjs`, `delta.mjs`, `suite.mjs`, `compat.mjs`, `findings.mjs`, `record.mjs
|
|
1265
|
+
- **A gate's silence never reads as a pass:** every tool a workflow gates on (`pr.mjs plan`, `same`, `lookup` and `notice`, `downstream.mjs`, `same.mjs`, `pack.mjs`, `delta.mjs`, `suite.mjs`, `compat.mjs`, `findings.mjs`, `record.mjs`) ends with one verdict line, `<tool>: PASS <what>` or `<tool>: FAIL <why>`, from the shared `verdict()` in `lib.mjs`. `tools/cert-review/gate.sh [--either|--evidence] <tool> <command>` runs a tool, passes its output through, and exits 70 with an error when the tool's last line starting `<tool>: ` isn't a PASS or FAIL verdict, so a tool replaced by `true`, an early exit 0 or an empty run fails its step. By default the step passes only on PASS with exit 0; a PASS with a non-zero exit, or a FAIL with exit 0, fails it. `--either` keeps the tool's own exit, for tools whose FAIL is evidence that record judges later (suite, compat). `--evidence` needs only the verdict, for the NEGATIVE run on main's tree, which is expected to fail. `suite: PASS` means exit 0, no failures and every expected file has results; `compat: PASS` means a run turned up with no regressions. `prompt.mjs` writes the review prompt to stdout, which goes into a step output, so it prints no verdict; a missing prompt leaves the review without `verdict.json`, and record marks that INCOMPLETE. The gate reads the tool's stdout, and no tool passes the pull request's output there: suite pipes the tests' output into its log file, and compat prints only its own lines, so a test that prints `suite: PASS` can't stand in for a silent suite. A tool that let such output through would need its verdict on a separate descriptor. Because the workflow of a pull request runs main's tools, the tools gain their verdicts first, and the workflows call them through `gate.sh` in the pull request after that.
|
|
1271
1266
|
- **Evidence comes by id from the job that made it:** the jobs that run the pull request's code (`pack candidate`, which builds it, and both `suite` legs, which run its tests) share the run's artifact store with the jobs that don't, and code in them can reach the runner's artifact token. So no consumer trusts a name or a pattern. Each producer job hands on its artifact's `artifact-id` and `artifact-digest` as job outputs, consumers download by `artifact-ids` with `digest-mismatch: error`, and no upload uses `overwrite`, so a second upload of a name fails instead of replacing it. The base build has its own job, `pack base`, which runs only main's code, so the candidate's job can't speak for it; it restores no Actions cache either, because the candidate job shares the run's cache scope (npm would refuse a tampered tarball against the lockfile's integrity anyway). `tools/cert-review/artifacts.mjs` lists the run's artifacts and fails when one in our namespaces (`kernel-tgz-`, `cert-review-`) wasn't uploaded by its producer, with that producer's id, name and digest, or when a producer's artifact is gone. `same` runs it on the two tarballs before it compares them, so the "not behaviour (identical dist)" skip can't be bought with a planted base tarball. `record` runs it on every artifact before it writes the line, and a failure there makes the record INCOMPLETE. **What this protects:** artifacts of jobs that run no pull-request code (the base build, the delta, kernel-compat, the same-dist evidence, the review's verdict and evidence) can't be forged or replaced. **What it doesn't:** the candidate's own artifacts, its tarball and both suites' results (the base leg runs the pull request's tests on main's tree, for the NEGATIVE), stay the pull request's by design, because code that runs on a runner can always lie about its own results. They're consumed by their exact names, at most one each. That's the same deliberate-malice residual as the workflow-rewrite one above.
|
|
1272
1267
|
- **Decide by what ships:** when a pull request's behaviour files are only `package.json` and/or `package-lock.json` (a Renovate devDependency bump, say), both sides are packed as usual, and then the `same dist` job (`same.mjs`, in code) compares the two tarballs before anything else runs. It checks the same paths, entry types (a symlink isn't a file) and modes. `package.json` must be equal once `devDependencies`, `version` and `gitHead` are removed, so `scripts` (they ship, a consumer's install runs `postinstall`, and `prepack`/`prepublishOnly` can make the published build differ from the packed one), runtime dependencies, `exports`, `bin`, `files` and `engines` must all match. Every other file must be byte-identical once each side's **exact** version string (build.mjs bakes it into `dist`) is replaced by one placeholder. A version string in one more place, or any looser match, is a difference. Source maps are compared as JSON without their `mappings`, whose columns the longer or shorter version string shifts; the code itself is compared byte for byte. If all of that matches, `cert-review` is green with `not behaviour (identical dist): <candidate sha256> = <base sha256> after normalising <v1>/<v2>`, and the suites, kernel-compat, the review and the record are skipped. `same.json` (uploaded as an artifact) names the rule and both tarballs' full sha256. Any difference, or a `same dist` job that doesn't finish, means the full review. The kernel has no runtime `dependencies`, since esbuild bundles everything into `dist`, so a devDependency that changes the product shows up as a byte difference. A test-only devDependency is checked by `ci / test`, on the pull request and on the merge group.
|
|
1273
1268
|
- **When:** every same-repo pull request that isn't a draft, on `opened`, `ready_for_review`, `reopened` and every push (`synchronize`). Draft status replaces the old `merge-ok` label: **mark a pull request ready = it gets reviewed, and merges on green**; a pull request held for anything stays a draft. A draft that changes shipped behaviour gets a red status, `draft: not reviewed, mark it ready for review`, and no review. The plan reads whether the pull request is a draft live from the API, not from the event: a push and `ready for review` within a second can leave only the push's run, whose payload still says draft (#1224, #1217). A draft flag it can't read is an `error`, never a review or a pass; `converted_to_draft` cancels a running review (one concurrency group per pull request) and sets that red status, so an older result on the same head can't satisfy the check. Each pull request has its own concurrency group, and a newer push cancels the older review. Labels other than `cert-review-approved` don't start or cancel anything. Fork code never reaches the pools or the key.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -7128,7 +7128,7 @@ function satisfies(version, range2) {
|
|
|
7128
7128
|
}
|
|
7129
7129
|
|
|
7130
7130
|
// src/version.ts
|
|
7131
|
-
var KERNEL_VERSION = true ? "1.141.
|
|
7131
|
+
var KERNEL_VERSION = true ? "1.141.2" : "0.0.0-dev";
|
|
7132
7132
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7133
7133
|
function procVersion() {
|
|
7134
7134
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -13601,6 +13601,10 @@ function mayTrace(viewer, target) {
|
|
|
13601
13601
|
const uid = of.ruid === by.euid && of.euid === by.euid && of.suid === by.euid;
|
|
13602
13602
|
return uid && of.rgid === by.egid && of.egid === by.egid && of.sgid === by.egid;
|
|
13603
13603
|
}
|
|
13604
|
+
function fileIdentity(ino, mtimeMs) {
|
|
13605
|
+
if (ino === void 0 || mtimeMs === void 0 || !Number.isFinite(mtimeMs)) return void 0;
|
|
13606
|
+
return `${ino}:${Math.trunc(mtimeMs)}`;
|
|
13607
|
+
}
|
|
13604
13608
|
|
|
13605
13609
|
// src/fs/pages.ts
|
|
13606
13610
|
var PAGE = 64 * 1024;
|
|
@@ -14161,12 +14165,7 @@ var ProcTree = class {
|
|
|
14161
14165
|
const exe = () => this.info(Number(top))?.exe === void 0 ? [] : ["exe"];
|
|
14162
14166
|
return { kind: "dir", names: () => [...PID_NAMES, ...exe(), "task"] };
|
|
14163
14167
|
}
|
|
14164
|
-
if (leaf === "exe")
|
|
14165
|
-
const info = this.info(Number(top));
|
|
14166
|
-
if (info?.exe === void 0) return void 0;
|
|
14167
|
-
const denied2 = !mayTrace(this.ownInfo(), info);
|
|
14168
|
-
return { kind: "link", target: info.exe, ...denied2 ? { denied: denied2 } : {} };
|
|
14169
|
-
}
|
|
14168
|
+
if (leaf === "exe") return this.exe(Number(top));
|
|
14170
14169
|
if (!PID_NAMES.includes(leaf)) return void 0;
|
|
14171
14170
|
return {
|
|
14172
14171
|
kind: "file",
|
|
@@ -14178,6 +14177,13 @@ var ProcTree = class {
|
|
|
14178
14177
|
}
|
|
14179
14178
|
};
|
|
14180
14179
|
}
|
|
14180
|
+
exe(pid) {
|
|
14181
|
+
const info = this.info(pid);
|
|
14182
|
+
if (info?.exe === void 0) return void 0;
|
|
14183
|
+
const denied2 = !mayTrace(this.ownInfo(), info);
|
|
14184
|
+
const id2 = info.exeId === void 0 ? {} : { id: info.exeId };
|
|
14185
|
+
return { kind: "link", target: info.exe, ...denied2 ? { denied: denied2 } : {}, ...id2 };
|
|
14186
|
+
}
|
|
14181
14187
|
ownInfo() {
|
|
14182
14188
|
const processes2 = this.listing().processes;
|
|
14183
14189
|
return processes2.find((p) => p.tid === this.pid) ?? processes2.find((p) => p.pid === this.pid);
|
|
@@ -14243,10 +14249,18 @@ var ProcTree = class {
|
|
|
14243
14249
|
return node.names();
|
|
14244
14250
|
}
|
|
14245
14251
|
escape(path) {
|
|
14252
|
+
return this.escapeTo(path)?.target;
|
|
14253
|
+
}
|
|
14254
|
+
escapeTo(path) {
|
|
14246
14255
|
const node = this.node(this.parts(path, true));
|
|
14247
14256
|
if (node?.kind !== "link" || !node.target.startsWith("/")) return void 0;
|
|
14248
14257
|
if (node.denied) throw syncError("EACCES", path);
|
|
14249
|
-
return node.target;
|
|
14258
|
+
return node.id === void 0 ? { target: node.target } : { target: node.target, id: node.id };
|
|
14259
|
+
}
|
|
14260
|
+
linkTo(path) {
|
|
14261
|
+
const node = this.node(this.parts(path, false));
|
|
14262
|
+
if (node?.kind !== "link" || node.id === void 0 || node.denied) return void 0;
|
|
14263
|
+
return { target: node.target, id: node.id };
|
|
14250
14264
|
}
|
|
14251
14265
|
readlink(path) {
|
|
14252
14266
|
const node = this.find(path, false);
|
|
@@ -27928,8 +27942,14 @@ function blobModuleUrl(source) {
|
|
|
27928
27942
|
const url = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
|
|
27929
27943
|
return { url, revoke: () => URL.revokeObjectURL(url) };
|
|
27930
27944
|
}
|
|
27945
|
+
function exeIdOf(exeId) {
|
|
27946
|
+
return exeId === void 0 ? {} : { exeId };
|
|
27947
|
+
}
|
|
27931
27948
|
function describe(req, tty) {
|
|
27932
|
-
const exe =
|
|
27949
|
+
const exe = {
|
|
27950
|
+
...req.exe === void 0 ? {} : { exe: req.exe },
|
|
27951
|
+
...req.exeId === void 0 ? {} : { exeId: req.exeId }
|
|
27952
|
+
};
|
|
27933
27953
|
return { argv: [req.argv0, ...req.args], tty, started: Date.now(), ...exe };
|
|
27934
27954
|
}
|
|
27935
27955
|
function linksOf2(req) {
|
|
@@ -29075,6 +29095,15 @@ var Launcher = class {
|
|
|
29075
29095
|
const optional = `${root}-names${beside.slice(root.length)}`;
|
|
29076
29096
|
return await this.base.exists(optional) ? optional : void 0;
|
|
29077
29097
|
}
|
|
29098
|
+
exeId(target) {
|
|
29099
|
+
if (target.abi !== "wasi" || (target.copy ?? target.glue) !== target.wasm) {
|
|
29100
|
+
return Promise.resolve({});
|
|
29101
|
+
}
|
|
29102
|
+
return this.base.stat(target.wasm).then(
|
|
29103
|
+
(st) => exeIdOf(fileIdentity(st.ino, st.mtime.getTime())),
|
|
29104
|
+
() => ({})
|
|
29105
|
+
);
|
|
29106
|
+
}
|
|
29078
29107
|
async program(target, env, caller) {
|
|
29079
29108
|
if (target.abi === "js" && target.wasm === SUDO_PATH) {
|
|
29080
29109
|
return { abi: "js", glue: SUDO_SOURCE, path: SUDO_PATH };
|
|
@@ -29122,11 +29151,12 @@ var Launcher = class {
|
|
|
29122
29151
|
const planned = chosen(asked, env);
|
|
29123
29152
|
const { target, args } = planned;
|
|
29124
29153
|
let program;
|
|
29154
|
+
let exeId;
|
|
29125
29155
|
await this.pnpmReady();
|
|
29126
29156
|
try {
|
|
29127
29157
|
const caller = { pid: req.ppid ?? 0, uid: req.cred.euid, tag: req.tag };
|
|
29128
29158
|
if (this.integrity) await this.checkedSources(planned, caller);
|
|
29129
|
-
program = await this.program(target, env, caller);
|
|
29159
|
+
[program, exeId] = await Promise.all([this.program(target, env, caller), this.exeId(target)]);
|
|
29130
29160
|
} catch (err) {
|
|
29131
29161
|
await req.fds.closeAll();
|
|
29132
29162
|
throw err;
|
|
@@ -29145,6 +29175,7 @@ var Launcher = class {
|
|
|
29145
29175
|
program,
|
|
29146
29176
|
argv0: target.argv0,
|
|
29147
29177
|
exe: target.copy ?? target.glue,
|
|
29178
|
+
...exeId,
|
|
29148
29179
|
args,
|
|
29149
29180
|
env,
|
|
29150
29181
|
cwdSlot
|