@ai-ecoverse/slicc-bios 1.39.9 → 1.39.11
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
|
@@ -201,7 +201,7 @@ The Files panel lists `/opt/grammars/node_modules` but doesn't scan it.
|
|
|
201
201
|
|
|
202
202
|
**Release notes (slicc-spectrum#458):** each update check also gives the Kernel, UI, Agent and Tailscale rows a `releases` list from their GitHub repositories (`slicc-kernel`, `slicc-spectrum`, `slicc-agent`, `wasm-tailscale`), and each optional package one from the repository its npm version document names, when that is on GitHub ([`os/releases.js`](src/seed/releases.js)). A row gets the release it has installed and every newer one up to the version it offers, newest first, at most 30. Each release has its `version` (the tag without the `v`, or without the package's name in a shared repository such as homescoop), its `url` and its `notes`, the release body unchanged (the `<!-- cert-verify -->` block included, which spectrum turns into a Certified badge) up to 64 KB. Spectrum renders the notes; bios only fetches them. bios asks GitHub's unauthenticated API (`/repos/<repo>/releases?per_page=100`, page by page down to the installed version, at most 5 pages), without credentials, a referrer or any header but `Accept` and `If-None-Match`. It keeps each list in IndexedDB (`slicc-releases`) for a day and revalidates it with its ETag at every check, and GitHub doesn't count a 304 against its 60 requests an hour. A 403 or 429 backs off until `Retry-After`, the rate limit's reset or 15 minutes, and leaves `releases` out, so the row looks as it does without notes, with no error. The page sets no `connect-src`, so these requests need no change to its CSP.
|
|
203
203
|
|
|
204
|
-
Below the system rows, Install / Update offers **Optional packages** (spectrum ≥ 1.42.0): tools that seven doesn't preinstall. homescoop's packages (`wasm-*`, `wasi-*`, `wasix-*`) come at npm's `latest`, which under cert-review v2 is certified, since homescoop publishes only from a reviewed main through its ladder. The four that other repos publish, slicc-jsh, mcpcat, wasi-slicc (slicc-cli's) and sliccy-browser (slicc-cdp's), stay bios pins in [`src/packages/optional/package.json`](src/packages/optional/package.json) and its `package-lock.json`, so
|
|
204
|
+
Below the system rows, Install / Update offers **Optional packages** (spectrum ≥ 1.42.0): tools that seven doesn't preinstall. homescoop's packages (`wasm-*`, `wasi-*`, `wasix-*`) come at npm's `latest`, which under cert-review v2 is certified, since homescoop publishes only from a reviewed main through its ladder. The four that other repos publish, slicc-jsh, mcpcat, wasi-slicc (slicc-cli's) and sliccy-browser (slicc-cdp's), stay bios pins in [`src/packages/optional/package.json`](src/packages/optional/package.json) and its `package-lock.json`, so Renovate bumps them and CI tests each bump (`pins.test.mjs` checks each is its own repository's release from main); the page can't check those repos' CI runs itself (GitHub's API allows 60 unauthenticated requests an hour), so a main-built release alone doesn't reach seven. The catalog, [`src/packages/optional/catalog.json`](src/packages/optional/catalog.json), lists each package by its npm name, with only what npm can't tell: its `id` and `label`, and optionally `requires` (other ids), `notes` (bios's own caveats, one line each, shown next to npm's description), `helpers` (commands to leave out of the list until homescoop marks them `helper: true`), `range` and `abi` (Python's, below) and `kind: "project"`. A project keeps its `description`, `commands` and `size`, since npm doesn't know it.
|
|
205
205
|
|
|
206
206
|
- **What npm tells (`os/packages.js`):** at boot, and at an update check at most every ten minutes, bios fetches each entry's version doc from the registry from the page (`https://registry.npmjs.org/<name>/latest`, which sends CORS `*`; the kernel's network path isn't needed), all in parallel and each with a 15-second timeout. A pinned entry's doc is its pin's (`/<name>/<pin>`), so only its description, commands and size come from npm. It takes `version` (the row's `offered`), `description` (npm's text, verbatim), the commands (the keys of `slicc.commands`, without one marked `helper: true` or listed in `helpers`, and only names a shell can run), `dist.unpackedSize` (the row's `size`, now what the package unpacks to on disk, its own files only, so git reads 56 MB where the old tarball-based size read 19 MB; the row says `unpacked: true`) and `dist.integrity`. An entry with a `range` (Python's is `3.15.0`) takes the highest version in range from the abbreviated packument instead, where the range is matched against the upstream X.Y.Z with homescoop's `-N` build ignored, so `3.15.0` offers 3.15.0-3 but never a 3.15.1 or 3.16 (pydata's builds are for one Python ABI: homescoop keeps the ABI within one X.Y.Z, and its py-* ranges stop at the next patch). With `abi`, a version whose `slicc.python.abi` differs isn't offered.
|
|
207
207
|
- **Cache:** the last good answer for each entry is kept in `var/lib/slicc/optional/npm.json`, so the panel works offline and shows at once at boot; a later fetch replaces it, an entry that fails keeps its cached answer, and holds only what the rows show. A passing provenance verdict is remembered only in the tab's memory, for the listing (never a refusal, never in OPFS, which kernel processes can write, and never for an install, which always asks npm again).
|
|
@@ -209,7 +209,7 @@ Below the system rows, Install / Update offers **Optional packages** (spectrum
|
|
|
209
209
|
- **The gate** (one function, `gate()` in `os/packages.js`, for the panel's Install and Update, a user's Update and [Your packages](#your-packages)) checks every package in the lock pnpm made. Each must be one of:
|
|
210
210
|
- an exact `name@version` with that integrity in the base set's lockfile (`src/packages/package-lock.json`), the catalog's pins (`src/packages/optional/package-lock.json`) or `src/packages/certs.json`, which is the only way a pinned entry passes; or
|
|
211
211
|
- an `@ai-ecoverse/*` package whose locked integrity equals the registry's `dist.integrity` for that exact version, and that npm's stored SLSA v1 provenance (`dist.attestations`, which bios trusts as npm stores it) binds to that integrity (its subject's sha512) and to its publisher's repository and release workflow at `refs/heads/main`. The publisher is bios's own, never read from npm: homescoop's `ladder-build.yml` or `publish.yml` for the `wasm-*`, `wasi-*`, `wasix-*` and `py-*` packages, and [`sources.json`](src/packages/optional/sources.json) for slicc-cdp, whose rule is `certified: "tag"`, its npm dist-tag `certified` (from the abbreviated packument), since its main released versions its cert-review didn't pass. A package with no publisher there (slicc-jsh, mcpcat, sliccy-browser at another version than its pin, or wasi-slicc, whose provenance is slicc-cli's, not homescoop's) is refused.
|
|
212
|
-
- Whatever matched, homescoop's certification exceptions then apply, exact matches included: a `fail` verdict refuses that version, and a `requires` verdict refuses it unless every pin of what it requires, in the lock and the base set's lockfile (never `certs.json`'s history), is in range. bios bundles `certification-exceptions.json` at
|
|
212
|
+
- Whatever matched, homescoop's certification exceptions then apply, exact matches included: a `fail` verdict refuses that version, and a `requires` verdict refuses it unless every pin of what it requires, in the lock and the base set's lockfile (never `certs.json`'s history), is in range. bios bundles `certification-exceptions.json` at the homescoop commit `pins.test.mjs` pins ([`exceptions.json`](src/packages/optional/exceptions.json); `pins.test` keeps it byte for byte that file) and unions it with homescoop main's (`raw.githubusercontent.com`, CORS `*`), which can only deny or constrain, never grant: a tampered or missing live file is at worst a denial, and the bundled copy decides alone when it can't be fetched. Finally a package whose `engines['slicc-kernel']` or kernel peer range the base set's kernel doesn't meet is refused.
|
|
213
213
|
- Anything that isn't `@ai-ecoverse/*` and isn't in the base set is refused; homescoop packages have no third-party dependencies.
|
|
214
214
|
- **Listing:** each entry's latest goes through the same rules (with its exact `dependencies`) when it's fetched. If latest fails, bios walks back through older versions (in range, not deprecated, at most five; a version whose doc is broken is skipped) and offers the newest that passes, with `newer: { version, reason }` naming latest's blocker. A pinned entry is never walked back. If none passes, the row is `withheld` with that reason, offers no Install or Update, and an installed copy keeps Remove.
|
|
215
215
|
|
|
@@ -367,7 +367,7 @@ seven can also join a tailnet, with a patched build of Tailscale's wasm client (
|
|
|
367
367
|
- **Pairing without CDP (`slicc open`, `slicc pair`):** `os/pair.js` is seven's side of the link spec's §1.3, for a seven in an everyday browser that no CLI controls. It needs slicc ≥ 1.39.0; the tests run the pinned `@ai-ecoverse/slicc` devDependency. `slicc open` opens `<page>#slicc-pair=<port>.<token>.<node-fp>`, and `slicc pair` prints it for the **Link this computer** field under Linked devices (`pairComputer(url)`, spectrum's pairing block, spectrum ≥ 1.88.1). Seven strips the fragment at once, opens the Network panel, and asks *Link SLICC on this computer?* with the node's short fingerprint (`ab12…ef`), which slicc prints too. Only **Link** (`confirmPairing()`) sends a loopback request: one `POST http://127.0.0.1:<port>/slicc/v1/pair` with the token and a non-trickled offer (ICE gathering waits 10 s at most, then the offer goes with what it has), from a page certificate (ECDSA P-256) kept per origin in IndexedDB (`slicc-follow`, key `loopback`). Seven uses the answer only if its fingerprint is the URL's, then pins `{ port, name }` by that fingerprint in `localStorage` (`slicc-os.paired`). It never pairs over ports 9222–9229 or below 1024. Every loopback `POST` gets 15 s (2 minutes while Chrome's Local Network Access prompt is open, which shows as `links.permission`), and a missed deadline reads as nothing answering. A tab that isn't running SLICC keeps the link until it follows the tab that is, then hands the attempt over; under a version skew between the tabs it says so instead and never pairs on its own. A tab that takes over reads the pins again. Refusals read as a sentence in the block: a wrong, used or expired link (`403`), nothing answering (or an origin slicc refuses), a browser that blocks local network access, and an impostor's fingerprint.
|
|
368
368
|
- **Browsers:** Chrome, Edge and Firefox reach `http://127.0.0.1` from an https page. WebKit (Safari, and every browser on iOS) blocks it as mixed content, so there an attempt fails at once with "Safari doesn’t let an https page reach slicc on this computer…", which points to the join command (the tray path) or another browser.
|
|
369
369
|
- **The device:** keyed by the node's fingerprint, local (the automatic exit, folders, kernel services), but with no `agent` cap, so no remote control: `slicc prompt` never goes over a pairing. Its row stays while slicc isn't running. After a reload, a lost link or a restart of slicc, the tab running SLICC posts `{ offer }` to `/slicc/v1/signal` on the remembered port, then 17117–17126, with backoff from 1 s to 30 s (and at once on `online` or when the tab shows), and takes only an answer with the pinned fingerprint. A `403` on the pinned port means slicc forgot this seven (`slicc unpair`), and the offline row says so until it's paired again. **Unlink** opens `unlink` on the live link (cap `unlink-loopback`), then forgets the pin, even when slicc isn't running or the `unlink` fails: slicc's own pin is harmless then (seven never signals again) until `slicc unpair` removes it.
|
|
370
|
-
- **Tests:** `test/integration/pair.test.mjs` runs the real `slicc pair` and `slicc open` with no `--cdp`: kernel `curl` and a `--mount` folder over the link, the debugging ports refused through it, a reload and a restart of slicc that link again without a new URL, Unlink after which `slicc unpair` lists nothing, a wrong and a used token, a stopped slicc, ports 9222 and 80 refused before any request, the address bar, a second tab (which keeps the device once the first closes), a second tab under a version skew, and a slicc that forgot this seven.
|
|
370
|
+
- **Tests:** `test/integration/pair.test.mjs` runs the real `slicc pair` and `slicc open` with no `--cdp`: kernel `curl` and a `--mount` folder over the link, the debugging ports refused through it, a reload and a restart of slicc that link again without a new URL, Unlink after which `slicc unpair` lists nothing, a wrong and a used token, a stopped slicc, ports 9222 and 80 refused before any request, the address bar, a second tab (which keeps the device once the first closes), a second tab under a version skew, and a slicc that forgot this seven.
|
|
371
371
|
- **Before HELLO:** each stream channel is its own SCTP stream, so a node's first OPEN can reach the page ahead of its HELLO on the control channel, the order a `slicc prompt`, `abort` or `watch` sometimes arrives in. The page holds up to 64 such frames, 256 KiB in all (one stream's credit window), and serves them in order once the HELLO arrives, as the CLI does for the page's; frames held on a channel that closed meanwhile are dropped. A frame on the control channel before HELLO, a 65th held frame or byte past 256 KiB, a held frame that turns out malformed, and no HELLO within 10 s still close the link: before its HELLO a peer is anonymous.
|
|
372
372
|
- **Half-open links:** a link that hasn't said HELLO within 30 s of its offer (a follower that gave up on a bootstrap, say) is closed, and its peer connection with it. A device's row follows its link that said HELLO, never a newer half-open one; with only a half-open link left, a known device shows as away. Until a link says HELLO its row shows only that it's connecting, under its bootstrap id: no *Asks to follow*, no Allow and no switches, because its device isn't known yet. So Allow, which keeps the grant with the device's certificate, always finds the device it was clicked for. Unlinking a device drops every link it has and tells the ones that said HELLO.
|
|
373
373
|
- **Names:** each link gets an address from `198.18.57.0/24` (the first is `.11`) and the name `<host>.slicc.internal`, from the CLI's hostname, made DNS-safe and unique (`lars-mbp-2`). A link keeps its name when it reconnects, also after a reload and in any order, while the name is free: the page remembers each CLI process's name (by device certificate and process id) in `localStorage` (`slicc-os.link-labels`). A CLI that restarts is a new process, so it may come back as `lars-mbp-2` while its old link is still closing; once that link is gone, the new one takes the name back (and its SSH host key is written under it). Port 22 there is the CLI's built-in SSH server, and any other port is the CLI machine's loopback, under the CLI's policy.
|
|
@@ -477,7 +477,7 @@ A server running inside the kernel (vite, `python -m http.server`, impeccable `l
|
|
|
477
477
|
|
|
478
478
|
A second test on its own tailnet sends kernel `bash`'s WebSocket through the proxy (`/dev/tcp/127.0.0.1/3128`) and the Tailscale exit to the peer's echo server (text, binary, the subprotocol and a close from each end), sees a handshake to the silent port answered `502` after a 5 s `handshakeMs`, and sees a vault placeholder in the URL or a header refused before anything reaches the peer.
|
|
479
479
|
|
|
480
|
-
|
|
480
|
+
On CI (`CI` set), or with `TAILSCALE_E2E=1`, these tests run with every `npm test`: on Linux (x64 or arm64) `test/integration/tailnet.mjs` downloads headscale 0.29.4 and tailscale 1.104.1, checks each against its pinned sha256 and keeps them in `node_modules/.cache/tailnet/`; elsewhere set `HEADSCALE`, `TAILSCALED` and `TAILSCALE`, or they skip. To run just these: `TAILSCALE_E2E=1 node --test --test-global-setup=test/integration/global.mjs --test-name-pattern 'stage B' test/integration/tailscale.test.mjs` (`TS_SHOTS=<dir>` also saves screenshots of the Network panel and the Install / Update row).
|
|
481
481
|
- **Plain `localhost` and `127.0.0.1` stay the real machine.** The service worker passes them through untouched. Inside the kernel it's the other way round: `localhost` is the kernel loopback, and `host.slicc.internal` reaches the machine. The kernel exports the page-visible name as `SLICC_PAGE_LOOPBACK` (`kernel.localhost`) and `SLICC_PAGE_LOOPBACK_URL` (`http://{port}.kernel.localhost`), so tools can print the right URL. With a kernel port (below) other than 80 they carry it: `kernel.localhost:17180` and `http://{port}.kernel.localhost:17180`.
|
|
482
482
|
- **Why the port goes in the name.** `kernel.localhost:8400` would resolve to the machine's real port 8400 whenever the service worker doesn't answer, as for a navigation or a WebSocket, and reach the wrong server. `8400.kernel.localhost` lands on the kernel port, 80 by default (below), which is closed or answered by a local slicc's kernel listener or [slicc-node](https://github.com/ai-ecoverse/slicc-node)'s kernel tunnel, and it gives every kernel port its own origin. The service worker routes by the name alone and ignores the URL's port, so `http://8400.kernel.localhost:17180/` reaches the same kernel server.
|
|
483
483
|
- **How a request gets there.** A seven page's request to `*.kernel.localhost`, with any method, reaches the service worker. `*.localhost` is potentially trustworthy, so an `https` page loads it without a mixed-content block. The service worker answers it itself, so nothing goes to the network, no Local Network Access prompt is shown, and there's no CORS preflight. It hands the request to the `/os/` tab: the requesting one, or else the most recently focused. That tab calls `kernel.loopbackFetch(request, { port })` and transfers the streamed response body back.
|
|
@@ -536,55 +536,23 @@ Limits:
|
|
|
536
536
|
|
|
537
537
|
## Certified packages
|
|
538
538
|
|
|
539
|
-
Seven certifies a gated package (slicc-kernel, slicc-cdp, slicc-clipboard, slicc-jsh, mcpcat, and the `wasi-*`, `wasix-*`, `wasm-*` and `py-*` packages) by its integrity: a version is certified when [`src/packages/certs.json`](src/packages/certs.json) (the historic list, with its recorded `exception`s) or one of the lockfiles seven ships (the base set, `/opt/agent`, the optional catalog's pins, Tailscale and the catalog's projects) carries it with that integrity. `src/seed/layer.js`'s `certifiedSet()` reads that union in the page, and a user's or a project's install that needs anything else is refused, except an optional catalog package that passes [the gate](#optional-packages), with the dependencies it pins exactly, so a user's own add of a catalog package passes or fails as the panel's Install does. homescoop's certification exceptions apply to every version there, a `certs.json` or lockfile match included, and when they can't be read the version is refused. A pin on main is certified because
|
|
539
|
+
Seven certifies a gated package (slicc-kernel, slicc-cdp, slicc-clipboard, slicc-jsh, mcpcat, and the `wasi-*`, `wasix-*`, `wasm-*` and `py-*` packages) by its integrity: a version is certified when [`src/packages/certs.json`](src/packages/certs.json) (the historic list, with its recorded `exception`s) or one of the lockfiles seven ships (the base set, `/opt/agent`, the optional catalog's pins, Tailscale and the catalog's projects) carries it with that integrity. `src/seed/layer.js`'s `certifiedSet()` reads that union in the page, and a user's or a project's install that needs anything else is refused, except an optional catalog package that passes [the gate](#optional-packages), with the dependencies it pins exactly, so a user's own add of a catalog package passes or fails as the panel's Install does. homescoop's certification exceptions apply to every version there, a `certs.json` or lockfile match included, and when they can't be read the version is refused. A pin on main is certified because it reached main through the merge queue, where every integration test ran on it, and because `pins.test.mjs` checks every gated pin is its own repository's release from main. `test/integration/pins.test.mjs` checks the union covers every gated pin with its integrity (Tailscale's project included), and fails when a pinned package's `engines.slicc-kernel` or kernel peer range isn't met by the pinned kernel.
|
|
540
540
|
|
|
541
|
-
**
|
|
542
|
-
- **Always:** the registry's integrity equals the lockfile's (or the line's).
|
|
543
|
-
- **slicc-kernel, slicc-jsh, mcpcat, wasi-slicc (slicc-cli's):** npm provenance names the tarball and `<repo>/.github/workflows/release.yml` at `refs/heads/main`; that commit is on main and carries `cert-review` success: a `cert-review` commit status from `github-actions[bot]` (what cert-review v2 writes from slicc-shared-web v1.27.1, and what these repos' own merge groups wrote before), or a v1.27.0 check run from app 15368 whose `external_id` names a `merge_group` run of `.github/workflows/cert-review.yml` (its plain path or the org-required `@refs/heads/main` one) on that commit. A version-only release commit is judged by its parent. The kernel also passes on its npm `certified` tag. wasi-slicc counts only from 1.44.1, its first PR-certified version, and never 1.44.4, whose release run failed (slicc-cli coordinator). A status whose `target_url` is a run attempt counts only if that run is a `merge_group` run of the repository's `cert-review.yml` on that commit; once a repository is listed in `V2_STATUS` (when it moves to v1.27.1), only such statuses count. Residual until then: the bare commit-status rule trusts any same-repo workflow with `statuses: write`, so it trusts that repo's collaborators; the org require-workflows ruleset and the cert-path guard are what bind it.
|
|
544
|
-
- **slicc-kernel and wasi-slicc (slicc-cli's) from `.github/workflows/ci.yml`** (slicc-shared-web's pipeline, `PIPELINE` in `release-state.mjs`): npm provenance names the tarball and the repository's `.github/workflows/ci.yml` at `refs/heads/main`, that commit is on main, and it carries successful `ci / integration` and `ci / review` check runs from app 15368 whose run (by their `details_url`) is a `merge_group` run of `ci.yml` on that commit, so the release went through the merge queue. No `cert-review` status is needed. Older releases, from `release.yml`, are judged by the rule above, and wasi-slicc's version rules (from 1.44.1, never 1.44.4) apply either way.
|
|
545
|
-
- **slicc-cdp, slicc-clipboard, wasm-tailscale:** the npm `certified` dist-tag is this version.
|
|
546
|
-
- **homescoop (`wasm-*`, `wasi-*`, `wasix-*`, `py-*`):** npm provenance names the tarball and homescoop's `ladder-build.yml` (or `publish.yml`) at `refs/heads/main`, any version, and homescoop's `certification-exceptions.json`, read at the commit pinned in `release-state.mjs`, neither fails the version (`verdict: fail`) nor requires a kernel bios doesn't pin (`verdict: requires`). An unreadable or unknown-shaped file fails closed.
|
|
547
|
-
It runs twice: in `ci / test` for quick feedback, and as cert-review's code check, from main's tree with the pull request's head as data, so the pull request's own code can't make it pass. A failure names the package and why, and the PR stays red until it pins a certified version. An `@ai-ecoverse` lockfile entry that doesn't resolve to a registry tarball with its integrity (a `tarball:` resolution, an alias, a link) fails it.
|
|
541
|
+
**Provenance** (`pins.test.mjs`, an ordinary integration test): every gated pin in a lockfile seven ships has npm's SLSA provenance for that exact tarball (its subject's sha512 is the lockfile's integrity), built on `refs/heads/main` of its own repository: slicc-kernel, slicc-jsh, slicc-cdp, slicc-clipboard, mcpcat, wasm-tailscale, slicc-cli for wasi-slicc, and homescoop for the other `wasm-*`, `wasi-*`, `wasix-*` and `py-*`. A homescoop pin that the bundled `certification-exceptions.json` withdraws, or whose `requires` the pins don't meet, fails it too. The test names each pin that fails and why.
|
|
548
542
|
|
|
549
543
|
### Renovate keeps the pins current
|
|
550
544
|
|
|
551
545
|
[`renovate.json`](renovate.json) bumps every `@ai-ecoverse/*` pin in the base set (`src/packages/package.json`), the optional catalog's four non-homescoop pins (`src/packages/optional/package.json`) and `/opt/agent` (`src/packages/agent/package.json`), with their npm and pnpm lockfiles. The catalog's homescoop entries aren't pinned, so they need no bumps; the Python data tools' project (`src/packages/optional/projects/pydata`) is pinned by hand, from the dependency dashboard.
|
|
552
546
|
|
|
553
547
|
- **PRs:** homescoop's packages (`wasi-*`, `wasix-*`, `wasm-*`, `py-*`, `wasi-pnpm` included) come as one PR a day, between 00:00 and 06:00 UTC. The kernel (its base-set and `/opt/agent` pins together), slicc-cdp, slicc-agent, slicc-spectrum and slicc-jsh each get their own PR, and so does every other package. The grammars and Tailscale projects stay on the dependency dashboard.
|
|
554
|
-
- **Automerge
|
|
548
|
+
- **Automerge once CI is green:** Renovate turns on auto-merge, so a PR goes through the merge queue once its required checks, `ci / integration` and `ci / review`, are green (see [CI](#ci)). No bot writes to a Renovate branch: the shipped lockfiles are part of what seven certifies, so a certified pin needs no `certs.json` line.
|
|
555
549
|
- **Known breaks** are `packageRules` with `allowedVersions`, each with its reason and issue in `description`. Today the only one is wasi-pnpm skipping the deprecated 12.9.1-11 and 12.9.1-12 (homescoop#1128). wasm-bash's hold below 5.3.0-20 went with the pin of kernel 1.135.2, which has slicc-kernel#1146. bash 5.3.0-20 and later count as certified only with kernel 1.130.2 or later (homescoop#1230), and -21 declares `engines.slicc-kernel >=1.130.2`. `test/integration/renovate.test.mjs` checks the config, the gate and the queue in plain node.
|
|
556
550
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
[`.github/workflows/cert-review.yml`](.github/workflows/cert-review.yml) calls slicc-shared-web's cert-review v2 on every non-draft pull request into main and every merge group, and sets one `cert-review` commit status on the head (by `github-actions[bot]`, linked to its run attempt; slicc-shared-web v1.29.1). [`cert-rerun.yml`](.github/workflows/cert-rerun.yml) re-runs the newest cert-review run when a pull request is marked ready or gains or loses `cert-review-approved` (an org required workflow starts only on opened, synchronize and reopened), with `actions: write` only. A pull request moved onto main needs a push (or a close and reopen), so that CI and its evidence run against main. Inspect, the evidence and release-state compare against main as GitHub merged it (the merge ref's first parent), never the event's base.sha, which goes stale when main moves. Pull-request code runs only in CI: the `ci / cert evidence` job (`pull_request`, a read-only token, no secrets) re-runs each test file the shards failed on the head's tree until it passes (at most three times) and, only if it failed every time, on main's, each with its own slicc CLI, within a 20-minute budget for all re-runs and scenarios (files with an open `flaky-test` issue are skipped, except those covering data integrity, security or the guard: `CLASSES` in evidence.mjs), so it never holds the CI run for hours. A file a shard failed and a re-run passed is a head flake, a finding, and a fail in those three classes. A spent budget or a missing result fails `ci / cert evidence` (`evidence.mjs check`), and `ci / test` needs that job, runs each changed gated pin's scenario ([`scripts/cert/scenarios/<package>.txt`](scripts/cert/scenarios/), one command per line, with `# pass:` lines saying what output passes) in seven on both trees through `test:cert`, all in the `cert-evidence` artifact ([`scripts/cert/evidence.mjs`](scripts/cert/evidence.mjs)). A separate `ci / cert inspect` job runs no pull-request code: from main's scripts, with the head checked out only as data, it inspects each changed pin's registry tarballs and records release-state in the `cert-inspect` artifact, which the caller marks as upstream data (`evidence-notes`), so injection-shaped text in a dependency's code is a note for the review, not a failure. cert-review fetches that artifact and the shards' artifacts, from this PR's CI run and main's, by id and digest; the review reads them with Read, Glob and Grep only, against the rules in [`scripts/cert/review.txt`](scripts/cert/review.txt). A gated pin with no scenario is a known gap ("can't judge <package> in seven"). Changes to `scripts/cert/**`, `scripts/ci/**` or any workflow need the coordinator's `cert-review-approved`.
|
|
560
|
-
|
|
561
|
-
### Canary
|
|
562
|
-
|
|
563
|
-
[`.github/workflows/canary.yml`](.github/workflows/canary.yml) answers "is bios main green with these candidate versions?" before anyone asks for a pin. It never pushes, pins or opens a PR: it reports.
|
|
564
|
-
|
|
565
|
-
- **Triggers:** `workflow_dispatch` and `workflow_call` with `overrides`, a JSON list of `name@version` (`["@ai-ecoverse/slicc-kernel@1.81.0"]`), and optionally `runner`, `shards` (names from [`scripts/canary/shards.json`](scripts/canary/shards.json); `follow-chat`, `remote-pydata`, `users-agent-ui`, `packages-update-cdp`, `guard`, `guard-set-extension-prompt`, `guard-set-extension-lagging`, `guard-set-proxy-prompt`, `guard-set-proxy-lagging`, `k4-link`, `user-packages-keys-favicon` and `rest` by default; a shard's `env` in that file, such as `GUARD_VARIANTS`, is set for its tests, or `sweep` for just the base-set sweep and the certs check), `expect` and `bios-ref` (any branch or sha of slicc-bios: the shard jobs check it out and lay this ref's `scripts/canary`, and `sweep.test.mjs` if it has none, over it; a ref whose `pins.test.mjs` has no canary mode gets the candidates in its working copy of `certs.json` instead, and the report still lists them as candidates). A proof in `proofs.json` can name its own `ref` the same way. An hourly `schedule` checks npm's `latest` of every kernel, jsh, cdp, agent, `wasm-*`, `wasi-*`, `wasix-*` and `py-*` package that a `src/packages/**/package.json` pins, and runs once with every newer one as the overrides, only when that list differs from the last scheduled canary's (its inputs are kept in the Actions cache). The same key covers what the optional catalog offers (each entry's npm `latest`, or the highest version in its `range`), so a new catalog release alone runs the catalog's shards (`packages-update-cdp`, `remote-pydata` and `rest`) against main's pins, since the catalog's tests take the version npm offers. A scheduled canary that ends red has the separate [`canary-issue.yml`](.github/workflows/canary-issue.yml) (on `workflow_run`, the only workflow here with `issues: write`, so callers of the reusable canary grant nothing more) file one open `known-issue` titled *canary: the scheduled canary is red*, or comment on it while it's open, with the verdict, the run and the catalog's offers ([`issue.mjs`](scripts/canary/issue.mjs)). homescoop's live certification-exceptions.json is the immediate fix: it denies a bad release at once. A `pull_request` trigger, filtered to the canary's own files, runs the proof in [`scripts/canary/proofs.json`](scripts/canary/proofs.json) from one plan job, cancelling a superseded run: kernel 1.78.0 on the `sweep` shard must regress grep, sed, jq, xz, findutils, diffutils and patch with exit 70 (kernel #483). A second proof packs `@ai-ecoverse/slicc-cdp@1.26.2` with `npm pack`, uploads it from the plan job, and runs it as a candidate artifact on `packages-update-cdp` and `guard` with `repeat: 3`.
|
|
566
|
-
- **A candidate from another repo's run:** `candidate-package` (the package it replaces, `@ai-ecoverse/slicc-cdp`), `candidate-artifact` (an artifact holding one `npm pack` tarball of it), `candidate-run` (the run that uploaded it), `candidate-sha256` (the tarball's) and optionally `candidate-repo` (the caller's repository by default). Each shard job downloads the artifact with `github.token`, which under `workflow_call` is the caller's, so the caller's job needs `actions: read`. [`override-pins.mjs`](scripts/canary/override-pins.mjs) checks the tarball's sha256 before it reads anything and refuses a mismatch, then checks the tarball is that package with an exact version. It seeds the harness download cache with the bytes at the npm URL of that version, and rewrites the pins and locks through a local registry that serves npm's packuments with that version added, so an unpublished `x.y.z-pr.N.M` locks like a published one (npm's lock gets the registry URL back). The tests get the same packument (#599): each test process of a candidate run imports [`packuments.mjs`](scripts/canary/packuments.mjs), which checks the seeded tarball against `pins.json`'s sha256 and integrity again (refusing a mismatch, so no test runs) and, whenever the harness fetches that package's packument from npm, adds the version with the manifest from the tarball's own `package.json`, `dist.tarball` at the seeded npm URL and `dist.integrity` the tarball's sha512. The `dist-tags` stay npm's, so the candidate never becomes `latest`, and every other request passes through unchanged. Seven's in-kernel pnpm, installing the optional catalog's `slicc` say, then resolves the candidate like a published version. Everything else stays a registry version. The report lists it as `candidate (artifact <run>/<name>, sha256 …)`.
|
|
567
|
-
- **Binding the candidate to its upload:** a call that names a candidate also passes `candidate-artifact-id` and `candidate-artifact-digest`, the `artifact-id` and `artifact-digest` outputs of the step that uploaded it (the digest is the zip's SHA-256, not the tarball's `candidate-sha256`; a `sha256:` prefix is allowed). `candidate-binding` is `strict` by default, and the plan refuses a candidate without both values. Only an explicit `candidate-binding: legacy` takes a candidate by name, as before; runs that name no candidate, such as the schedule and the PR proofs, don't set it. Before anything from the tested ref runs, [`scripts/canary/bind.mjs`](scripts/canary/bind.mjs) reads `GET /repos/{repo}/actions/artifacts/{id}` and `GET /repos/{repo}` and refuses the artifact unless its id, name, `workflow_run.id`, `workflow_run.repository_id` and digest are the bound ones and it hasn't expired. The shard then downloads it by `artifact-ids` with `digest-mismatch: error`, and the tarball's sha256 check stays. The PR proof's tarball is bound the same way, to the plan job's upload step.
|
|
568
|
-
- **The canary's own scripts:** every checkout of its tools (the plan and report jobs, and the `scripts/canary` laid over the tested ref) is `job.workflow_sha`, the commit of the `canary.yml` that defines the job. Under `workflow_call` that is the called workflow's commit, not the caller's (`github.workflow_sha`), so a caller pinned to `canary.yml@<sha>` runs that commit's scripts, whatever `main` holds. The plan job prints `canary tools: PASS|FAIL` and fails unless it checked out that commit. `bios-ref` only picks the tested tree, and an empty one is that same commit.
|
|
569
|
-
- **Callers pinned before the bindings (#523):** a `canary.yml` from before it checks out `main`'s scripts and passes no `CANDIDATE_BINDING`, `CANDIDATE_ARTIFACT_ID` or `CANDIDATE_ARTIFACT_DIGEST` at all, so main's plan takes its candidate by name, as `legacy`, and prints `canary plan: legacy binding, the calling canary.yml passes no candidate-binding`. Its report passes `--prefix <label>-` instead of `--bindings` and `--download`: the report then reads only the planned `<label>-canary-shard-<run>-<shard>` directories of that call's download, and refuses any other directory. The current `canary.yml` always passes `candidate-binding`, so a call through it without an id and a digest stays strict, even with an empty `candidate-binding`.
|
|
570
|
-
- **Digests:** every digest the canary emits is the uploader's `artifact-digest` output, 64 lowercase hex digits with no prefix. The REST API's `digest` field is `sha256:` followed by the same digits, so strip that prefix before comparing.
|
|
571
|
-
- **`artifact-id`, `artifact-digest` and `artifacts`** are `workflow_call` outputs. The first two are the `canary` report artifact's, from the report job's upload step. `artifacts` is a JSON list of `{ role, name, id, digest, attempt }` covering every artifact the call uploaded, in this order: `plan` (the plan job's `canary-inputs` on a schedule, or the PR proof's tarball), then for each planned shard, in plan order, `shard` (`<label>-canary-shard-<run>-<shard>`) and `files` (`<label>-canary-files-<run>-<shard>`, present only when the shard left files), then `report` (`<label>-canary`). Names follow from the trusted plan. Each id and digest is the output of the step that uploaded that artifact, never an API lookup, and a `shard` or `files` row appears only once the report has checked it against the API. `attempt` is the `github.run_attempt` of the job that uploaded it: a job carried over from an earlier attempt keeps its number, and ids come only from the job outputs of the current attempt.
|
|
572
|
-
- **The shards reach the report by their upload steps:** a matrix job's outputs collapse to the last job's, so each shard job writes its rows to its own output, `binding_<strategy.job-index>` (the shard job declares `binding_0` to `binding_15`, and the plan refuses more than 16 jobs). The report job reads them all with `toJSON(needs.shard.outputs)`, checks each planned shard's `shard` and `files` artifacts against the API the same way as the candidate (with this run and this repository), and downloads only the verified `shard` ids. The shard jobs ran the candidate, so this API check, not the shard job, is what ties each id to the one artifact of that name in this run. It refuses a binding for a shard the plan didn't make, a row whose name isn't the planned one (a forged extra suffix), a directory in the download that no verified binding names, a `shard.json` holding another shard, and any download that failed its digest check. Each refusal turns the canary red with `FAIL refused <name>`, and the bindings step prints its own `canary bindings: PASS|FAIL` verdict, which the report requires.
|
|
573
|
-
- **`repeat`** (1 to 10, default 1): each selected test file runs that many times with the candidates, and the files with a failure run that many times on main's pins. A test failing in every candidate run and no baseline run is a regression, one failing in every run of both is a failure on both, and anything mixed is flaky. Each row carries its counts, `candidate 2/3, baseline 0/3`.
|
|
574
|
-
- **`result`**, a `workflow_call` output next to `ok`: the JSON summary (`ok`, `counts`, `regressions`, `both`, `flaky`, `failures`, `missingShards`, `unexplainedExits`, `refused`, `candidates`, `overrides`, `repeat`) plus the `canary` artifact's name, URL, id and digest. Those last two are a convenience: `artifact-id`, `artifact-digest` and `artifacts` are the authoritative values. A plan that refuses its inputs still gets a report, `canary: FAIL the plan refused: …`.
|
|
575
|
-
- **Permissions:** `contents: read` and `actions: read`, no secrets and no `id-token`. A caller outside bios runs the plan with its inputs: the PR proofs and the hourly npm check run only for bios's own triggers.
|
|
576
|
-
- **Jobs:** one plan job, one job per shard of each run, and one report job, because the queue for a runner costs more than the work.
|
|
577
|
-
- **Runner:** fork code never runs on the org's Cloud Run pools. A same-repo `pull_request` (the proofs from our own branches), or a `schedule`, `workflow_dispatch`, `workflow_run` or `release` run on `main` (any `bios-ref`), puts the shard jobs on the `runner` input, else the `CERT_RUNNER` repository variable (`cloud-run-bench`), and the light plan and report jobs on `CERT_LIGHT_RUNNER` (`cloud-run`). Everything else runs on `ubuntu-latest`, and the `runner` input is ignored: fork pull requests, and dispatches from any other branch. Under `workflow_call` the same check reads the caller's event. The pools run with "no new privileges", so `sudo` can't become root there: the shard job installs Chromium's system libraries only when `sudo -n true` works, and otherwise relies on the image's.
|
|
578
|
-
- **Pins:** [`scripts/canary/override-pins.mjs`](scripts/canary/override-pins.mjs) `name@version …` fetches each tarball through the harness download cache (so the tests find it there), checks it against npm's sha512 and records its sha256. It sets the version in every `package.json` under `src/packages` that pins the package and lets npm (`--package-lock-only`) and pnpm 12 (`--lockfile-only`) rewrite that directory's lockfiles, so a dependency that moves with the package (an agent release taking a newer slicc-browsers) moves in the lock too. A package that is only a dependency in a pnpm lock is forced with an `overrides` entry in that directory's `pnpm-workspace.yaml`. One that is only a dependency in an npm lock (sliccy-browser's own slicc-cdp in the optional catalog) is left alone and reported as held. The catalog's homescoop entries aren't pinned, so they aren't candidates here; seven offers their npm `latest` as it is. It writes `node_modules/.cache/slicc-canary/pins.json`, whose `candidates` are the gated packages the locks now pin that `certs.json` doesn't certify.
|
|
579
|
-
- **Certs check:** with `SLICC_CANARY` set to that file, `pins.test.mjs` reports those candidates as "candidate, not certified" instead of failing on them; anything else uncertified still fails. Without it (normal CI) the check is unchanged.
|
|
580
|
-
- **Tests:** the named shards in `shards.json` (`ci.yml` assigns files to shards at random instead, see [CI](#ci)), run by [`scripts/canary/shard.mjs`](scripts/canary/shard.mjs) with the overrides. The test files with a failure run again on main's pins, and the ones that passed there run once more with the overrides, so each failure is a regression (passes on main's pins, fails with the overrides), a failure on both, or flaky. Tests that assert a pinned version by value (`jsh.test.mjs` for slicc-jsh, `update.test.mjs` and `reuse.test.mjs` for wasm-bash) regress whenever that package is overridden.
|
|
581
|
-
- **Output:** a `canary` artifact with `canary.json` (`<proof>/canary.json` for the proofs), `{ biosRef, overrides: [{ name, version, sha256 }], candidates, held, shards: [{ shard, wallMs, phases }], results: [{ test, status, baseline, retry, verdict, log }] }`, and a job summary with the overrides, a note when a slicc-kernel override of 1.78.0 or later meets a ref whose `src/` calls `createKernel` without a `users` mode (that kernel then defaults to `enforce`, and `i`, `npm` and `npx` by path exit 126 until #405 and kernel #495; [`scripts/canary/users.mjs`](scripts/canary/users.mjs) finds the calls, and `canary.json` lists them as `kernels`), the wall time of each shard and the failures split into regressions, failures on both, and flaky ones. The report job fails on a failure without overrides, on a missing shard, on a shard that exited nonzero with no failing test (a crash or a throwing teardown), when an `expect` entry (a `{ test, log }` pair of substrings) matches no regression, or on a regression no `expect` entry matches, unless the run sets `strict: false`. `test/integration/canary.test.mjs` checks these rules.
|
|
582
|
-
|
|
583
|
-
`test/integration/sweep.test.mjs`, the base-set sweep, runs in both workflows. It boots the BIOS, checks that each base-set package in OPFS has the version `package-lock.json` pins, and runs every command its `slicc.commands` lists with `--version` or a trivial call under `timeout 60`, then real grep, egrep, sed -i, xz, jq, find, xargs, diff, cmp, patch, awk, gzip, bzip2 and tar work. Each command is its own subtest named `<package> · <call>`. The clipboard commands run `--help` (`xclip -help`). Known exits: `false` 1, `bunzip2`/`bzcat --version` 2 (upstream), `i --version` 2 (argument parsing; 126 is #495), and `chcon`/`runcon` 1 (listed by coreutils, not built: "unknown program"). `[` runs as bash's builtin, since `timeout` can't exec it.
|
|
551
|
+
`test/integration/sweep.test.mjs`, the base-set sweep, runs with the other integration tests. It boots the BIOS, checks that each base-set package in OPFS has the version `package-lock.json` pins, and runs every command its `slicc.commands` lists with `--version` or a trivial call under `timeout 60`, then real grep, egrep, sed -i, xz, jq, find, xargs, diff, cmp, patch, awk, gzip, bzip2 and tar work. Each command is its own subtest named `<package> · <call>`. The clipboard commands run `--help` (`xclip -help`). Known exits: `false` 1, `bunzip2`/`bzcat --version` 2 (upstream), `i --version` 2 (argument parsing; 126 is #495), and `chcon`/`runcon` 1 (listed by coreutils, not built: "unknown program"). `[` runs as bash's builtin, since `timeout` can't exec it.
|
|
584
552
|
|
|
585
553
|
### The npm package
|
|
586
554
|
|
|
587
|
-
`@ai-ecoverse/slicc-bios` on npm carries only the manifests a consumer needs to test against what seven ships: `src/packages/package.json`, `package-lock.json`, `pnpm-lock.yaml` and `certs.json`, `src/packages/agent/package.json` and `pnpm-lock.yaml`, and `src/packages/optional/package.json`, `package-lock.json` (the four non-homescoop pins), `catalog.json`, `sources.json` and `exceptions.json`. It has no runtime code and no dependencies, and those paths stay where they are.
|
|
555
|
+
`@ai-ecoverse/slicc-bios` on npm carries only the manifests a consumer needs to test against what seven ships: `src/packages/package.json`, `package-lock.json`, `pnpm-lock.yaml` and `certs.json`, `src/packages/agent/package.json` and `pnpm-lock.yaml`, and `src/packages/optional/package.json`, `package-lock.json` (the four non-homescoop pins), `catalog.json`, `sources.json` and `exceptions.json`. It has no runtime code and no dependencies, and those paths stay where they are. `ci.yml` releases it from `main` only, after lint, through slicc-shared-web's pipeline: semantic-release reads the conventional commits, tags `v<version>`, writes the GitHub release and publishes to npm with provenance through npm's trusted publishing (`fledgling.workflow` is `ci.yml`). The deploy to seven (`deploy.yml`) is separate and doesn't wait for it.
|
|
588
556
|
|
|
589
557
|
## Develop
|
|
590
558
|
|
|
@@ -597,11 +565,11 @@ npm run lint
|
|
|
597
565
|
|
|
598
566
|
`npm start` serves `src/` on <http://127.0.0.1:8080/>. There is no build step. To change what the BIOS installs, edit `src/packages/package.json` and refresh both lockfiles in that directory with `npm install --package-lock-only` and `npx pnpm@12 install --lockfile-only`; npm and pnpm resolve the dependency tree, the BIOS and the updater only replay it. A test fails when the two lockfiles pin different packages. `npm install` also installs the [lefthook](https://lefthook.dev) pre-commit hook, which runs Biome, the no-comment check, the no-unit-tests-in-git guard, and the diff-coverage check described below.
|
|
599
567
|
|
|
600
|
-
`npm test` runs the integration tests with `node --test` against
|
|
568
|
+
`npm test` runs the integration tests with `node --test` against Chrome for Testing (installed by `slicc-cdp install-browser` in `pretest`), driven directly over the DevTools protocol by the [harness from slicc-shared-web](https://github.com/ai-ecoverse/slicc-shared-web#integration-test-harness); `test/integration/chrome.mjs` only says what to serve and which CDN requests to intercept. It sets `SLICC_CDP_PORTS=9300-9309` unless it's already set, for every test run, so nothing in a test run attaches to a developer's own Chrome on 127.0.0.1:9222–9229 (slicc-cdp 1.27 and later look there by default). Every run leaves behind:
|
|
601
569
|
|
|
602
570
|
- `coverage/` (with `SLICC_COVERAGE=1` only) – V8 coverage of the page, the shared worker, and the service worker as `lcov.info` plus an HTML report. Without it the harness attaches no coverage recorder, and CI doesn't turn it on: nothing reads integration coverage, and without the recorder CI runs pages and workers as production does.
|
|
603
571
|
- `artifacts/<suite>/<test>/` – a screenshot of each tab at the end of the test, a `console.log` of every page and worker, and the minidump of any renderer or browser crash.
|
|
604
|
-
- `artifacts/hotspots.md` –
|
|
572
|
+
- `artifacts/hotspots.md` – it says that profiling is off unless you turned it on.
|
|
605
573
|
|
|
606
574
|
CPU profiling is off by default, in CI too: V8's sampler interrupts every page and worker thread every 100 µs with a signal, and that is where the renderer crashes in [#114](https://github.com/ai-ecoverse/slicc-bios/issues/114) faulted. Run `SLICC_PROFILE=1 npm test` to get a `.cpuprofile` per page and worker for every document and test, the kernel's workers included (open them in the DevTools Performance panel or [speedscope](https://www.speedscope.app)). `hotspots.md` then lists the functions in `src/` with the most self time across all profiles. The profiles are recorded with coverage switched on, so absolute timings run high. Use them to compare functions with each other rather than as real-world numbers.
|
|
607
575
|
|
|
@@ -609,21 +577,12 @@ No test calls a real model. Every test serves an empty `packages/agent/` project
|
|
|
609
577
|
|
|
610
578
|
### CI
|
|
611
579
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
On a pull request, the plan first reads the files the pull request changes (`git diff HEAD^1 HEAD` on its merge commit) and picks a mode ([`scripts/ci/scope.mjs`](scripts/ci/scope.mjs)):
|
|
615
|
-
|
|
616
|
-
- **docs**: only Markdown or `docs/` changed. Lint runs, and neither the shards nor `ci / vendors` run.
|
|
617
|
-
- **tests**: only `test/integration/*.test.mjs` files changed, and perhaps `durations.json`. Only those files run, in shards; `ci / vendors` runs when a vendors test changed.
|
|
618
|
-
- **catalog**: only `src/packages/optional/**` or `certs.json` changed. The pins, optional, npm, packages, unshadow and renovate rows run, plus the rows of each changed package (a changed `catalog.json`, `retired.json` or `sources.json` entry, by its key).
|
|
619
|
-
- **bump**: only pins and lockfiles changed (the base set, `/opt/agent`, grammars, Tailscale, the optional catalog's pins, root devDependencies) and `certs.json`. Pins, the boot smoke and renovate run, plus the rows of each changed package, and the catalog rows when an optional package changed.
|
|
620
|
-
- **full**: everything else, and every merge queue run, push to `main` and dispatch. A `package.json` that changed beyond its dependencies, a lockfile without a pin change, a changed helper or fixture, and a package without rows all run the full suite.
|
|
621
|
-
|
|
622
|
-
[`scripts/ci/scopes.json`](scripts/ci/scopes.json) maps each package name or pattern to its rows (`full` for the harness, Playwright and the installer's tar, `vendors` for the slicc CLI). The plan prints its mode and why in the job summary, and `ci / test` passes only when the jobs that mode skips were skipped and every other job passed: `ci: PASS docs plan, 0 shards, skipped integration and vendors`. The merge queue always runs everything, so `main` is never less tested.
|
|
580
|
+
[`.github/workflows/ci.yml`](.github/workflows/ci.yml) is one caller of [slicc-shared-web's pipeline](https://github.com/ai-ecoverse/slicc-shared-web#pipeline), pinned to the release `@ai-ecoverse/slicc-shared-web` has in `devDependencies` (Renovate moves both in one PR). It runs on pull requests, the merge queue and pushes to `main`:
|
|
623
581
|
|
|
624
|
-
|
|
582
|
+
- **Pull requests and the merge queue:** lint, then every `test/integration/*.test.mjs` split by the last runs' JUnit timings over 12 pool shards (`ci / integration`), then on a pull request the Claude and Codex reviews (`ci / review`) with this repository's `review-prompt`. The shared guard vectors run as one file per path (extension, proxy), target group (`seven`, `vault`, `vault-x`, `apex` and the rest, `test/integration/guard-groups.mjs`) and detach variant (`lagging` or the others), 20 files of 2 to about 12 minutes, so the split can balance them. The slicc CLI tests use `node_modules/.bin/slicc` (or `SLICC_CLI`).
|
|
583
|
+
- **`main`:** semantic-release publishes the npm package (see [The npm package](#the-npm-package)). [`deploy.yml`](.github/workflows/deploy.yml) deploys every push to `main` and `bb/**` (see [Hosting](#hosting)).
|
|
625
584
|
|
|
626
|
-
|
|
585
|
+
Require `ci / integration` and `ci / review`. There is no retry: a flaky test fails the run.
|
|
627
586
|
|
|
628
587
|
## Hosting
|
|
629
588
|
|
|
@@ -633,7 +592,7 @@ The hosts are served by the [sliccy-ai](https://github.com/ai-ecoverse/sliccy-ai
|
|
|
633
592
|
|
|
634
593
|
- [`edge/publish.mjs`](edge/publish.mjs) `<branch>` uploads `src/` to `seven/` for `main` and to `branches/<label>/` for every other branch, and deletes what the previous upload had and this one doesn't. A manifest next to it (`seven.json`, `branches/<label>.json`) records which branch owns the label, so two branches that map to the same label can't overwrite each other.
|
|
635
594
|
- The bucket expires everything under `branches/` 30 days after upload. Every push uploads all files again, so a branch stays up while it's active and disappears a month after its last push. Nothing needs to be cleaned up when a branch is deleted.
|
|
636
|
-
- [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml) publishes on every push to `main` or a `bb/**` branch, on
|
|
595
|
+
- [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml) publishes on every push to `main` or a `bb/**` branch, on `ubuntu-latest`. A newer push to a `bb/**` branch cancels its running publish; a publish of `main` is never cancelled.
|
|
637
596
|
- **`main`** publishes `seven/` with `CLOUDFLARE_API_TOKEN`, an account token with Workers R2 Storage Write. That token could overwrite `seven/`, so it lives only in the `production` environment, whose deployment branch policy allows `main` alone. A branch that changes the workflow can't read it.
|
|
638
597
|
- **A `bb/**` branch** holds no secret. It runs in the `preview` environment, with no approval step and `id-token: write`. `edge/publish.mjs` asks GitHub for the job's OIDC token (audience `https://sliccy.ai`) and publishes through the sliccy-ai worker's `/api/publish/*` route. The route checks the token's signature, issuer, audience, expiry, repository and branch, then derives `branches/<label>/` from the branch itself. A branch can only ever write its own prefix, and never `seven/`. It first writes a manifest listing both the previous upload's files and this one's, so a publish cancelled midway leaves every file it uploaded on the list for the next publish to delete. Then files go up eight at a time, then deletions, then the final manifest.
|
|
639
598
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-bios",
|
|
3
|
-
"version": "1.39.
|
|
3
|
+
"version": "1.39.11",
|
|
4
4
|
"description": "Slim loader that boots SLICC into OPFS and serves the UI from there",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"fledgling": {
|
|
12
12
|
"provider": "github",
|
|
13
|
-
"workflow": "
|
|
13
|
+
"workflow": "ci.yml",
|
|
14
14
|
"repo": "ai-ecoverse/slicc-bios",
|
|
15
15
|
"publish": true
|
|
16
16
|
},
|
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"prepare": "slicc-install-hooks",
|
|
39
39
|
"start": "node test/integration/chrome.mjs",
|
|
40
|
-
"pretest": "
|
|
41
|
-
"test": "node --test --test-concurrency=1 --test-timeout=900000 --test-global-setup=test/integration/global.mjs
|
|
42
|
-
"test:cert": "node --test --test-concurrency=1 --test-timeout=${CERT_BUDGET:-8400000} --test-global-setup=node_modules/@ai-ecoverse/slicc-shared-web/harness/global.mjs test/integration/cert.test.mjs",
|
|
40
|
+
"pretest": "slicc-cdp install-browser",
|
|
41
|
+
"test": "node --test --test-concurrency=1 --test-timeout=900000 --test-global-setup=test/integration/global.mjs 'test/integration/*.test.mjs'",
|
|
43
42
|
"test:unit": "mcr -c node_modules/@ai-ecoverse/slicc-shared-web/mcr.config.mjs node --test 'test/unit/**/*.test.mjs'",
|
|
44
43
|
"lint": "slicc-lint",
|
|
45
44
|
"format": "biome check --write ."
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
48
47
|
"@ai-ecoverse/slicc": "1.47.2",
|
|
49
|
-
"@ai-ecoverse/slicc-browsers": "1.18.
|
|
48
|
+
"@ai-ecoverse/slicc-browsers": "1.18.7",
|
|
49
|
+
"@ai-ecoverse/slicc-cdp": "1.30.25",
|
|
50
50
|
"@ai-ecoverse/slicc-node": "2.5.1",
|
|
51
|
-
"@ai-ecoverse/slicc-shared-web": "
|
|
51
|
+
"@ai-ecoverse/slicc-shared-web": "2.10.1",
|
|
52
52
|
"@ai-ecoverse/slicc-spectrum": "1.104.1",
|
|
53
53
|
"@ai-ecoverse/slicc-vault": "1.15.1",
|
|
54
54
|
"@biomejs/biome": "2.5.15",
|
|
@@ -57,9 +57,7 @@
|
|
|
57
57
|
"lefthook": "2.2.1",
|
|
58
58
|
"modern-tar": "0.8.5",
|
|
59
59
|
"monocart-coverage-reports": "2.13.2",
|
|
60
|
-
"playwright-core": "1.64.1",
|
|
61
60
|
"semantic-release": "25.0.9",
|
|
62
|
-
"semver": "7.8.5",
|
|
63
61
|
"wrangler": "4.151.0",
|
|
64
62
|
"ws": "8.22.0"
|
|
65
63
|
}
|
|
@@ -6,22 +6,22 @@
|
|
|
6
6
|
"": {
|
|
7
7
|
"name": "slicc-bios-optional",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@ai-ecoverse/mcpcat": "0.
|
|
9
|
+
"@ai-ecoverse/mcpcat": "0.20.4",
|
|
10
10
|
"@ai-ecoverse/slicc-jsh": "0.52.5",
|
|
11
11
|
"@ai-ecoverse/sliccy-browser": "0.1.34",
|
|
12
12
|
"@ai-ecoverse/wasi-slicc": "1.47.2"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"node_modules/@ai-ecoverse/mcpcat": {
|
|
16
|
-
"version": "0.
|
|
17
|
-
"resolved": "https://registry.npmjs.org/@ai-ecoverse/mcpcat/-/mcpcat-0.
|
|
18
|
-
"integrity": "sha512-
|
|
16
|
+
"version": "0.20.4",
|
|
17
|
+
"resolved": "https://registry.npmjs.org/@ai-ecoverse/mcpcat/-/mcpcat-0.20.4.tgz",
|
|
18
|
+
"integrity": "sha512-GaK4LeMjpRT40nnOm/ZtTUKNzvtg0A4+AC/T62Fclivq5oTIBirrZG2USkthAGpFlS3BJDIT5NXaq3OdCSNt4Q==",
|
|
19
19
|
"license": "Apache-2.0",
|
|
20
20
|
"bin": {
|
|
21
21
|
"mcpcat": "dist/host/mcpcat.mjs"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
24
|
+
"node": ">=26",
|
|
25
25
|
"slicc-kernel": ">=1.65.2"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"description": "The optional packages that seven pins because their publisher isn't homescoop's ladder: slicc-jsh, mcpcat, wasi-slicc (slicc-cli) and sliccy-browser (slicc-cdp). Release-state and cert review certify these pins; every other catalog entry is npm's latest behind the runtime gate. Never installed as a project: the panel runs pnpm add -g for each one.",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@ai-ecoverse/mcpcat": "0.
|
|
6
|
+
"@ai-ecoverse/mcpcat": "0.20.4",
|
|
7
7
|
"@ai-ecoverse/slicc-jsh": "0.52.5",
|
|
8
8
|
"@ai-ecoverse/sliccy-browser": "0.1.34",
|
|
9
9
|
"@ai-ecoverse/wasi-slicc": "1.47.2"
|