Repository navigation
build(repo): run all dependency code in the sandbox, systemfsoftware packages from Nix - #52
Merged
Merged
Conversation
This was referenced Oct 6, 2026
systemfsoftware-maker
force-pushed
the
lake1/csp
branch
from
October 6, 2026 17:28
a8a92e1 to
1b0c9a4
Compare
systemfsoftware-maker
force-pushed
the
lake1/nix-sandbox
branch
from
October 6, 2026 17:29
bbd435c to
65a809e
Compare
systemfsoftware-maker
marked this pull request as ready for review
October 6, 2026 19:52
systemfsoftware-maker
added this pull request to stack #54
October 6, 2026 19:52
systemfsoftware-maker
marked this pull request as draft
October 6, 2026 19:52
This was referenced Oct 6, 2026
systemfsoftware-maker
removed this pull request from stack #54
October 6, 2026 21:56
systemfsoftware-maker
added this pull request to stack #59
October 6, 2026 21:57
systemfsoftware-maker
force-pushed
the
lake1/csp
branch
from
October 6, 2026 22:13
1b0c9a4 to
9131d8a
Compare
systemfsoftware-maker
removed this pull request from stack #59
October 6, 2026 22:13
systemfsoftware-maker
added this pull request to stack #61
October 6, 2026 22:13
systemfsoftware-maker
force-pushed
the
lake1/csp
branch
from
October 6, 2026 22:15
9131d8a to
8a43bc2
Compare
systemfsoftware-maker
force-pushed
the
lake1/nix-sandbox
branch
from
October 6, 2026 22:22
65a809e to
209d19e
Compare
systemfsoftware-maker
force-pushed
the
lake1/csp
branch
from
October 6, 2026 22:39
8a43bc2 to
37ab4f3
Compare
systemfsoftware-maker
force-pushed
the
lake1/nix-sandbox
branch
2 times, most recently
from
October 6, 2026 22:44
dbd6a07 to
0dd6458
Compare
systemfsoftware-maker
force-pushed
the
lake1/csp
branch
from
October 6, 2026 23:46
37ab4f3 to
5bdc325
Compare
systemfsoftware-maker
force-pushed
the
lake1/nix-sandbox
branch
from
October 6, 2026 23:46
0dd6458 to
6a926d3
Compare
…anagement input Conductor ruling, cycle 71: since pnpm-release-management#11 (aa712d9), devshell: true runs nix develop --command sandbox -- changeset-management check with the release-tools in the caller's dev shell. The pinned input (5eb4c5d, followed from systemfsoftware main) already exports packages.<system>.release-tools, so the lock does not move
Conductor rulings, cycles 74 and 77: pnpm-release-management main 537d17c carries #14, #20 (the consumer store) and #24 (the linked-worktree change), so the starter's own copy of that change (nix/patches/sandbox-linked-worktree-git.patch) and its applyPatches wiring go. pnpm-release-management becomes a direct input on main, locked to 537d17c; systemfsoftware follows it, and its nixpkgs, comment-checker and importPnpmLock follow the starter's
starter-verify c73: withoutSandboxOnPath removed only the first directory that held sandbox, so with two launchers on PATH the hooks still found one and the refusal step failed falsely. It now drops every PATH entry that provides sandbox. Two launchers on PATH: the old helper fails the step, the new one passes; with the hooks' sandbox prefix stripped, the step fails
Conductor ruling, cycle 83: stryker-js-effect main f06f244 (#196) publishes its 17 members as tarballs with an index.json. The flake adds it as an input (locked to f06f244, following the starter's nixpkgs, comment-checker, importPnpmLock, pnpm-release-management and systemfsoftware), and .sfs-deps now holds both flakes' tarballs with one merged index.json. Every @systemfsoftware/stryker-* member is a file:.sfs-deps catalog entry with an override, the same mechanism as the systemfsoftware packages, plus one for the stryker-js-vm-runner npm alias that stryker-js 17.0.2 declares, so check:sfs-sources passes with no npm-sourced @systemfsoftware/* package
Contributor
There was a problem hiding this comment.
Verified: 9/9 green on 6dca2fa incl. sfs-sources (all @systemfsoftware/* from flakes), starter-verify delta met, 0 threads, hunt clean.
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 7, 2026
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 7, 2026
Conductor ruling, cycle 87: the stryker packages come from the stryker-js-effect flake since #52, so the message names both flakes
kiro-systemf Bot
pushed a commit
that referenced
this pull request
Oct 7, 2026
* ci(ci): move mutation to a release gate on main check:ci no longer runs stryker. The release gate on push to main plans one shard per workspace package that declares a mutation script, refuses an empty set, and runs each shard at break 100 on the fleet with its incremental report cached. The checker drops prioritizePerformanceOverAccuracy, which stryker-js 15 removes. Operator approval: Kiro, 2026-10-05 (GATE1) * ci(ci): run the release gate on github-hosted runners The plan and per-package mutation jobs move from the self-hosted fleet, which admits only private repositories, to ubuntu-latest. Mutation stays one parallel job per package. Operator approval: Kiro, 2026-10-05 (GATE1) * fix(ci): refuse a mutation shard whose mutate globs match no files A package with a mutation script whose mutate globs match nothing reports zero mutants and passes a break-100 threshold vacuously (review finding #9). Each package now declares its globs once, as stryker.mutate in package.json; its Stryker config reads them and the release gate's shard planner expands them. The planner fails red at plan time naming the package, its directory and its globs when they match no file, including a package with no declared globs. Its Deno tests are ordinary tests: a turbo root task, test:scripts, runs them through the dev shell's deno, and both pnpm test and check:ci run it, so a local check:ci covers the planner exactly as CI does * ci(ci): pass the release gate with a notice when there are no decisions to mutate A decision is a *.workflow.ts file. When no workspace package has one, the planner emits an empty shard list and a 'No decisions to mutate' notice, and the mutation job is skipped. When decisions exist and no package declares a mutation script, it still refuses the empty set, naming how many decisions went unmutated. Kiro ruling, 2026-10-06 * deps(deps): move to effect 4 stable at exact pins Every catalog entry is an exact version: effect 4.0.1, the systemfsoftware toolchain on its Effect 4 stable majors (recommended preset 4.0.0, stryker-js 15.0.1, vitest 2.0.0, gherkin-spec 7.0.1) and current tooling. minimumReleaseAge 1440 is explicit, so the policy is strict; the exclude list holds the effect name patterns and one exact entry per resolved systemfsoftware package. oxlint-tsgolint stays 7.0.2001, the exact peer the presets require. turbo 2.11 writes an agent guidance block into AGENTS.md unless agentGuidance is false, so turbo.json opts out. Grader package majors move here because they require Effect 4 stable (CONST-W3 declared) * build(repo): move lint, test and mutation settings to the root oxlint.shared.ts, vitest.shared.ts and stryker.shared.ts hold the house settings; each package config extends them and declares only its own mutate set and aliases. Both lint overrides are gone: preset 4.0.0 already accepts Gherkin step bodies and build-config imports. The root lint file is named oxlint.shared.ts so lint-staged's nearest-config walk never treats the repository root as a package. Turbo inputs track the shared files. Operator approval: Kiro, 2026-10-05 (GATE1) * build(repo): declare the effect/http unstable-api opt-in once at the root Root tsconfig.base.json extends the bundler/dom preset and allows exactly effect/http through the effect language service. effect 4.0.1 ships HTTP only as unstable and global-fetch-in-effect bans the fetch alternative. Operator approval: Kiro, 2026-10-05 (GATE1) * feat(repo): serve the starter site from one worker One Cloudflare Worker serves the TanStack Start site, defined with Alchemy and run locally by pnpm dev through alchemy dev. The e2e journeys run against that local app * ci(ci): run the e2e journeys against pnpm dev on linux and macos bin/journeys starts the app with pnpm dev, runs the journeys against it and stops it with one SIGINT to its process group, so alchemy's local sidecar is not orphaned. The dev port is strict, so a stale server fails the run instead of moving it to another port * refactor(repo): delete the hello seed package packages/starter and every trace of it leave in one commit: the README sections that describe it, the trusted-publisher note in .changeset, and the catalog entries no remaining package uses (rimraf, tsdown). npm publishing goes with it. The release job keeps capture, release-note assertion, tagging and GitHub Releases and drops build, OIDC and pnpm publish. A version in a manifest with no matching <name>@v<version> tag is owed; no script queries registry.npmjs.org. Operator approval: Kiro, 2026-10-05 (release.yml) * feat(repo): enforce a strict nonce csp with trusted types The Worker draws a fresh nonce per request, hands it to TanStack Start for its scripts and sends a strict Content-Security-Policy with Trusted Types. A browser journey in Chromium loads the home page under that policy with no violations, and removing the Trusted Types directive fails it * build(repo): run all dependency code in the sandbox, systemfsoftware packages from nix Every script that runs dependency code (install, build, test, dev, the journeys and the git hooks) goes through prm's sandbox launcher, systemfsoftware packages come from the systemfsoftware flake as tarballs, and the pnpm store is built from per-tarball fetches. check:sfs-sources stays red on the two Stryker packages, which still resolve from npm until systemfsoftware/stryker-js-effect ships them as flake outputs * feat(repo): deploy the site from one command pnpm run deploy deploys the Worker with Alchemy to the adopter's own Cloudflare account through bin/cloud, the one place Cloudflare credentials enter the sandbox; cloud stages keep their state in Cloudflare. A production domain is adopter configuration: SITE_DOMAIN, read by alchemy.run.ts on the prod stage. The template holds no credentials * ci(ci): pin the shared release tooling to main, as main does * build(repo): take systemfsoftware from main and pnpm-release-management from its lock systemfsoftware main 497dd37 fetches each workspace tarball as its own fixed-output derivation, so the tarballs match across linux and darwin; the pre-squash #606 commit hashed pnpm's whole store, which differs on darwin. pnpm-release-management now follows systemfsoftware's lock (5eb4c5d). The sandbox proofs take iplConfigHook and the whole nix tree, as prm's own flake does; the lockfile records the new tarballs' integrity * build(repo): build the sandbox's pnpm store with pnpm-release-management's consumer store pnpm 12 checks TLS with the platform verifier, which on macOS refuses mitm-cache's per-build certificate (UnknownIssuer on every registry tarball); prm's mkPnpmConsumerStore replays the fetches over plain HTTP for pnpm 12, still checking each tarball's lockfile integrity. The starter's own store derivation goes * ci(ci): run the release gate only on pushes to main starter-verify F10 and F13 (Kiro ruling, cycle 35): drop workflow_dispatch so mutation runs on push to main only; the README no longer claims Cell workflows, Cell.provide, tenant-bound store ports or compile-time phase markers, and no longer tells readers to run pnpm mutation locally * chore(repo): refuse to run the journeys when port 1337 is already taken starter-verify F6 (Kiro ruling, cycle 35): a stale server on :1337 would answer the journeys instead of this run's pnpm dev. Proof: with a python http.server on 127.0.0.1:1337, bin/journeys prints the refusal and exits 1; with the port free, pnpm journeys passes 2 of 2 * test(repo): drop the plain-fetch tests from the journeys suite starter-verify F9 (Kiro ruling, cycle 35): home.integration.test.ts fetched / without a browser, and request-timeout.integration.test.ts only tested the fetch fixture's own deadline. The strict-CSP browser journey already asserts / answers 200, and #62 adds the health journey. Nothing else imports site.fixture.ts, so it goes too. pnpm journeys: 1 of 1 passed; e2e typecheck and lint clean * chore(repo): restore commitlint.config.ts as main has it starter-verify F4 (Kiro ruling, cycle 35): commitlint.config.ts is a read-only evaluator surface, so #52 returns it byte-for-byte to main's version. No gate needs the edit: from a linked worktree, the sandboxed commit-msg hook still sees the staged files and refuses a fix(...) commit of this tooling-only diff (type-matches-diff-shape) * chore(repo): fail commitlint closed when git cannot read the index Kiro ruling (cycle 35, F4 follow-up), declared per CONST-W3 in #52: commitlint.config.ts is an evaluator surface, and this restores #52's tightening of it that F4 reverted. stagedFiles no longer catches git's failure and returns no files; it lets the error through, so an unreadable index fails the commit instead of passing type-matches-diff-shape as no staged files. The sandbox proof sandbox-proofs/git-hooks.test.ts:135 depends on it; with main's version it fails with 'git failed silently' * ci(ci): let the macos chromium download follow the chrome for testing redirect Kiro ruling (cycle 35), declared per CONST-W3 in #52: ci.yml's macOS Playwright install step gains exactly --allow-host storage.googleapis.com. cdn.playwright.dev answers Chrome for Testing builds with a 307 to storage.googleapis.com/chrome-for-testing-public/..., which the sandbox refused, so the download failed with 403 (run 37566060960, job 112614085077) after the macOS dev shell started building * build(repo): drop darwin from the flake and the toolchain Ryan's standing rule via Kiro (cycle 52): no macOS anywhere. The flake builds x86_64-linux and aarch64-linux only; dprint pins only Linux archives; the comment-checker sandbox loses its sandbox-exec branch; AGENTS.md says CI is Linux only * ci(ci): run the journeys on linux only Ryan's standing rule via Kiro (cycle 52): no macOS legs. The journeys job runs on ubuntu-latest alone * ci(ci): drop the macos chromium install step Ryan's standing rule via Kiro (cycle 52): journeys run on Linux only, where Chromium comes from the dev shell * build(repo): drop darwin from the sandbox patch and supported architectures Ryan's standing rule via Kiro (cycle 52): the linked-worktree sandbox patch keeps only its bubblewrap hunks (the seatbelt profile hunks go), and pnpm resolves optional packages for linux only. The macOS Playwright install step, with the storage.googleapis.com allowance, left with the merge of #49 * chore(repo): drop the journeys busy-port guard Conductor ruling, cycle 61, F4: inside the sandbox's network namespace the guard can never fire, and that isolation already keeps a stale host server from answering the journeys * build(repo): show which tests ran in the turbo test logs Conductor ruling, cycle 61, F6: the test task's outputLogs is new-only, so CI logs show each executed test * ci(ci): install for the changeset check through the dev shell's bootstrap Conductor ruling, cycle 61, F2: the plain install outside Nix cannot read the .sfs-deps tarballs (ERR_PNPM_TARBALL_READ_LOCAL_TARBALL). devshell: true makes prm's shared workflow run the starter's own bootstrap script, which installs through sandbox. Evaluator-surface edit at the conductor's direction (CONST-W3) * build(repo): drop the root mutation script no workflow calls Conductor ruling, cycle 61, F5: the release gate runs turbo run mutation per package directly; nothing calls the root pnpm mutation * deps(deps): move @effect/tsgo to 0.50.0 Conductor ruling, cycle 62: the same bump pnpm-release-management#31 took. effect and every @effect/* stay at 4.0.1; none resolves lower. tsgo 0.50.0 reports nothing new: typecheck and lint are clean uncached. @systemfsoftware/oxlint-config-recommended 4.0.0 still pulls its own @effect/tsgo 0.45.0 * build(repo): take systemfsoftware main 8a4b543 Conductor ruling, cycle 62: systemfsoftware input moves from 497dd37 to main 8a4b543 (#659, #660, the macOS drops). pnpm-release-management still follows the rev systemfsoftware main pins (5eb4c5d). The published tarballs are byte-identical: the sandboxed bootstrap installs against the unchanged lockfile * build(repo): name the changesets versioning strategy, move @effect/tsgo to 0.51.0 Conductor ruling, cycle 67: prm main (#9, ca932ae) now requires versioning.strategy. The starter versions each workspace package from .changeset intents, so it takes changesets. Followed prm README: "`changesets` versioning drives the changesets libraries per package: the assembled release plan decides each member's bump, workspace dependents move with it, and the consumed intents are removed". No other field is required under that strategy. @effect/tsgo 0.50.0 -> 0.51.0 (npm latest, 2026-10-07T09:37Z); effect stays 4.0.1. Uncached check:ci and journeys pass with no new diagnostics * build(repo): give the dev shell release-tools from the pnpm-release-management input Conductor ruling, cycle 71: since pnpm-release-management#11 (aa712d9), devshell: true runs nix develop --command sandbox -- changeset-management check with the release-tools in the caller's dev shell. The pinned input (5eb4c5d, followed from systemfsoftware main) already exports packages.<system>.release-tools, so the lock does not move * build(repo): take the sandbox from pnpm-release-management unpatched Conductor rulings, cycles 74 and 77: pnpm-release-management main 537d17c carries #14, #20 (the consumer store) and #24 (the linked-worktree change), so the starter's own copy of that change (nix/patches/sandbox-linked-worktree-git.patch) and its applyPatches wiring go. pnpm-release-management becomes a direct input on main, locked to 537d17c; systemfsoftware follows it, and its nixpkgs, comment-checker and importPnpmLock follow the starter's * test(repo): drop every sandbox launcher from PATH in the hooks proof starter-verify c73: withoutSandboxOnPath removed only the first directory that held sandbox, so with two launchers on PATH the hooks still found one and the refusal step failed falsely. It now drops every PATH entry that provides sandbox. Two launchers on PATH: the old helper fails the step, the new one passes; with the hooks' sandbox prefix stripped, the step fails * build(repo): take the stryker packages from the stryker-js-effect flake Conductor ruling, cycle 83: stryker-js-effect main f06f244 (#196) publishes its 17 members as tarballs with an index.json. The flake adds it as an input (locked to f06f244, following the starter's nixpkgs, comment-checker, importPnpmLock, pnpm-release-management and systemfsoftware), and .sfs-deps now holds both flakes' tarballs with one merged index.json. Every @systemfsoftware/stryker-* member is a file:.sfs-deps catalog entry with an override, the same mechanism as the systemfsoftware packages, plus one for the stryker-js-vm-runner npm alias that stryker-js 17.0.2 declares, so check:sfs-sources passes with no npm-sourced @systemfsoftware/* package * revert(repo): take lake1/deploy's merge back off lake1/nix-sandbox 7857171 (lake1/nix-sandbox merged into lake1/deploy) was pushed to lake1/nix-sandbox by mistake during the cycle 83 merge-up. This restores the layer's tree to fb4f8a1 exactly; lake1/deploy reverts this revert so it keeps its own content
kiro-systemf Bot
pushed a commit
that referenced
this pull request
Oct 7, 2026
…e release gate (#51) * ci(ci): move mutation to a release gate on main check:ci no longer runs stryker. The release gate on push to main plans one shard per workspace package that declares a mutation script, refuses an empty set, and runs each shard at break 100 on the fleet with its incremental report cached. The checker drops prioritizePerformanceOverAccuracy, which stryker-js 15 removes. Operator approval: Kiro, 2026-10-05 (GATE1) * ci(ci): run the release gate on github-hosted runners The plan and per-package mutation jobs move from the self-hosted fleet, which admits only private repositories, to ubuntu-latest. Mutation stays one parallel job per package. Operator approval: Kiro, 2026-10-05 (GATE1) * fix(ci): refuse a mutation shard whose mutate globs match no files A package with a mutation script whose mutate globs match nothing reports zero mutants and passes a break-100 threshold vacuously (review finding #9). Each package now declares its globs once, as stryker.mutate in package.json; its Stryker config reads them and the release gate's shard planner expands them. The planner fails red at plan time naming the package, its directory and its globs when they match no file, including a package with no declared globs. Its Deno tests are ordinary tests: a turbo root task, test:scripts, runs them through the dev shell's deno, and both pnpm test and check:ci run it, so a local check:ci covers the planner exactly as CI does * ci(ci): pass the release gate with a notice when there are no decisions to mutate A decision is a *.workflow.ts file. When no workspace package has one, the planner emits an empty shard list and a 'No decisions to mutate' notice, and the mutation job is skipped. When decisions exist and no package declares a mutation script, it still refuses the empty set, naming how many decisions went unmutated. Kiro ruling, 2026-10-06 * deps(deps): move to effect 4 stable at exact pins Every catalog entry is an exact version: effect 4.0.1, the systemfsoftware toolchain on its Effect 4 stable majors (recommended preset 4.0.0, stryker-js 15.0.1, vitest 2.0.0, gherkin-spec 7.0.1) and current tooling. minimumReleaseAge 1440 is explicit, so the policy is strict; the exclude list holds the effect name patterns and one exact entry per resolved systemfsoftware package. oxlint-tsgolint stays 7.0.2001, the exact peer the presets require. turbo 2.11 writes an agent guidance block into AGENTS.md unless agentGuidance is false, so turbo.json opts out. Grader package majors move here because they require Effect 4 stable (CONST-W3 declared) * build(repo): move lint, test and mutation settings to the root oxlint.shared.ts, vitest.shared.ts and stryker.shared.ts hold the house settings; each package config extends them and declares only its own mutate set and aliases. Both lint overrides are gone: preset 4.0.0 already accepts Gherkin step bodies and build-config imports. The root lint file is named oxlint.shared.ts so lint-staged's nearest-config walk never treats the repository root as a package. Turbo inputs track the shared files. Operator approval: Kiro, 2026-10-05 (GATE1) * build(repo): declare the effect/http unstable-api opt-in once at the root Root tsconfig.base.json extends the bundler/dom preset and allows exactly effect/http through the effect language service. effect 4.0.1 ships HTTP only as unstable and global-fetch-in-effect bans the fetch alternative. Operator approval: Kiro, 2026-10-05 (GATE1) * feat(repo): serve the starter site from one worker One Cloudflare Worker serves the TanStack Start site, defined with Alchemy and run locally by pnpm dev through alchemy dev. The e2e journeys run against that local app * ci(ci): run the e2e journeys against pnpm dev on linux and macos bin/journeys starts the app with pnpm dev, runs the journeys against it and stops it with one SIGINT to its process group, so alchemy's local sidecar is not orphaned. The dev port is strict, so a stale server fails the run instead of moving it to another port * feat(repo): preview every pull request and deploy production after the release gate In an adopter's copy, never the template, a same-repo pull request deploys as its pr-<N> stage, gets its URL in one PR comment, runs the e2e journeys against it and is destroyed when it closes. Main deploys production once every mutation shard passes, to SITE_DOMAIN when that repository variable is set. The deployed journeys run through pnpm journeys:deployed, whose sandbox reaches only the site and gets no Cloudflare credentials * refactor(repo): delete the hello seed package packages/starter and every trace of it leave in one commit: the README sections that describe it, the trusted-publisher note in .changeset, and the catalog entries no remaining package uses (rimraf, tsdown). npm publishing goes with it. The release job keeps capture, release-note assertion, tagging and GitHub Releases and drops build, OIDC and pnpm publish. A version in a manifest with no matching <name>@v<version> tag is owed; no script queries registry.npmjs.org. Operator approval: Kiro, 2026-10-05 (release.yml) * feat(repo): enforce a strict nonce csp with trusted types The Worker draws a fresh nonce per request, hands it to TanStack Start for its scripts and sends a strict Content-Security-Policy with Trusted Types. A browser journey in Chromium loads the home page under that policy with no violations, and removing the Trusted Types directive fails it * build(repo): run all dependency code in the sandbox, systemfsoftware packages from nix Every script that runs dependency code (install, build, test, dev, the journeys and the git hooks) goes through prm's sandbox launcher, systemfsoftware packages come from the systemfsoftware flake as tarballs, and the pnpm store is built from per-tarball fetches. check:sfs-sources stays red on the two Stryker packages, which still resolve from npm until systemfsoftware/stryker-js-effect ships them as flake outputs * feat(repo): deploy the site from one command pnpm run deploy deploys the Worker with Alchemy to the adopter's own Cloudflare account through bin/cloud, the one place Cloudflare credentials enter the sandbox; cloud stages keep their state in Cloudflare. A production domain is adopter configuration: SITE_DOMAIN, read by alchemy.run.ts on the prod stage. The template holds no credentials * ci(ci): deploy production after a passing or skipped mutation job With no decisions to mutate the release gate skips the mutation job, which used to skip the deploy that needs it. Production now deploys when the plan passed and the mutation job passed or was skipped, and never when either failed or the run was cancelled * ci(ci): pin the shared release tooling to main, as main does * build(repo): take systemfsoftware from main and pnpm-release-management from its lock systemfsoftware main 497dd37 fetches each workspace tarball as its own fixed-output derivation, so the tarballs match across linux and darwin; the pre-squash #606 commit hashed pnpm's whole store, which differs on darwin. pnpm-release-management now follows systemfsoftware's lock (5eb4c5d). The sandbox proofs take iplConfigHook and the whole nix tree, as prm's own flake does; the lockfile records the new tarballs' integrity * build(repo): build the sandbox's pnpm store with pnpm-release-management's consumer store pnpm 12 checks TLS with the platform verifier, which on macOS refuses mitm-cache's per-build certificate (UnknownIssuer on every registry tarball); prm's mkPnpmConsumerStore replays the fetches over plain HTTP for pnpm 12, still checking each tarball's lockfile integrity. The starter's own store derivation goes * ci(ci): run the release gate only on pushes to main starter-verify F10 and F13 (Kiro ruling, cycle 35): drop workflow_dispatch so mutation runs on push to main only; the README no longer claims Cell workflows, Cell.provide, tenant-bound store ports or compile-time phase markers, and no longer tells readers to run pnpm mutation locally * chore(repo): refuse to run the journeys when port 1337 is already taken starter-verify F6 (Kiro ruling, cycle 35): a stale server on :1337 would answer the journeys instead of this run's pnpm dev. Proof: with a python http.server on 127.0.0.1:1337, bin/journeys prints the refusal and exits 1; with the port free, pnpm journeys passes 2 of 2 * test(repo): drop the plain-fetch tests from the journeys suite starter-verify F9 (Kiro ruling, cycle 35): home.integration.test.ts fetched / without a browser, and request-timeout.integration.test.ts only tested the fetch fixture's own deadline. The strict-CSP browser journey already asserts / answers 200, and #62 adds the health journey. Nothing else imports site.fixture.ts, so it goes too. pnpm journeys: 1 of 1 passed; e2e typecheck and lint clean * chore(repo): restore commitlint.config.ts as main has it starter-verify F4 (Kiro ruling, cycle 35): commitlint.config.ts is a read-only evaluator surface, so #52 returns it byte-for-byte to main's version. No gate needs the edit: from a linked worktree, the sandboxed commit-msg hook still sees the staged files and refuses a fix(...) commit of this tooling-only diff (type-matches-diff-shape) * ci(ci): stop running the journeys against deployed sites starter-verify F11 (Kiro ruling, cycle 35): deployed-site journeys are not in the spec, so they come out: bin/journeys-deployed, the journeys:deployed script, the journeys and artifact-upload steps in previews.yml and release-gate.yml (the uploads only carried what those journeys wrote), the README line and the AGENTS.md clause. Deploy, preview comment and destroy are unchanged; the preview job is named for what it does now * chore(repo): fail commitlint closed when git cannot read the index Kiro ruling (cycle 35, F4 follow-up), declared per CONST-W3 in #52: commitlint.config.ts is an evaluator surface, and this restores #52's tightening of it that F4 reverted. stagedFiles no longer catches git's failure and returns no files; it lets the error through, so an unreadable index fails the commit instead of passing type-matches-diff-shape as no staged files. The sandbox proof sandbox-proofs/git-hooks.test.ts:135 depends on it; with main's version it fails with 'git failed silently' * ci(ci): let the macos chromium download follow the chrome for testing redirect Kiro ruling (cycle 35), declared per CONST-W3 in #52: ci.yml's macOS Playwright install step gains exactly --allow-host storage.googleapis.com. cdn.playwright.dev answers Chrome for Testing builds with a 307 to storage.googleapis.com/chrome-for-testing-public/..., which the sandbox refused, so the download failed with 403 (run 37566060960, job 112614085077) after the macOS dev shell started building * build(repo): drop darwin from the flake and the toolchain Ryan's standing rule via Kiro (cycle 52): no macOS anywhere. The flake builds x86_64-linux and aarch64-linux only; dprint pins only Linux archives; the comment-checker sandbox loses its sandbox-exec branch; AGENTS.md says CI is Linux only * ci(ci): run the journeys on linux only Ryan's standing rule via Kiro (cycle 52): no macOS legs. The journeys job runs on ubuntu-latest alone * ci(ci): drop the macos chromium install step Ryan's standing rule via Kiro (cycle 52): journeys run on Linux only, where Chromium comes from the dev shell * build(repo): drop darwin from the sandbox patch and supported architectures Ryan's standing rule via Kiro (cycle 52): the linked-worktree sandbox patch keeps only its bubblewrap hunks (the seatbelt profile hunks go), and pnpm resolves optional packages for linux only. The macOS Playwright install step, with the storage.googleapis.com allowance, left with the merge of #49 * chore(repo): drop the journeys busy-port guard Conductor ruling, cycle 61, F4: inside the sandbox's network namespace the guard can never fire, and that isolation already keeps a stale host server from answering the journeys * build(repo): show which tests ran in the turbo test logs Conductor ruling, cycle 61, F6: the test task's outputLogs is new-only, so CI logs show each executed test * ci(ci): install for the changeset check through the dev shell's bootstrap Conductor ruling, cycle 61, F2: the plain install outside Nix cannot read the .sfs-deps tarballs (ERR_PNPM_TARBALL_READ_LOCAL_TARBALL). devshell: true makes prm's shared workflow run the starter's own bootstrap script, which installs through sandbox. Evaluator-surface edit at the conductor's direction (CONST-W3) * build(repo): drop the root mutation script no workflow calls Conductor ruling, cycle 61, F5: the release gate runs turbo run mutation per package directly; nothing calls the root pnpm mutation * deps(deps): move @effect/tsgo to 0.50.0 Conductor ruling, cycle 62: the same bump pnpm-release-management#31 took. effect and every @effect/* stay at 4.0.1; none resolves lower. tsgo 0.50.0 reports nothing new: typecheck and lint are clean uncached. @systemfsoftware/oxlint-config-recommended 4.0.0 still pulls its own @effect/tsgo 0.45.0 * build(repo): take systemfsoftware main 8a4b543 Conductor ruling, cycle 62: systemfsoftware input moves from 497dd37 to main 8a4b543 (#659, #660, the macOS drops). pnpm-release-management still follows the rev systemfsoftware main pins (5eb4c5d). The published tarballs are byte-identical: the sandboxed bootstrap installs against the unchanged lockfile * build(repo): name the changesets versioning strategy, move @effect/tsgo to 0.51.0 Conductor ruling, cycle 67: prm main (#9, ca932ae) now requires versioning.strategy. The starter versions each workspace package from .changeset intents, so it takes changesets. Followed prm README: "`changesets` versioning drives the changesets libraries per package: the assembled release plan decides each member's bump, workspace dependents move with it, and the consumed intents are removed". No other field is required under that strategy. @effect/tsgo 0.50.0 -> 0.51.0 (npm latest, 2026-10-07T09:37Z); effect stays 4.0.1. Uncached check:ci and journeys pass with no new diagnostics * build(repo): give the dev shell release-tools from the pnpm-release-management input Conductor ruling, cycle 71: since pnpm-release-management#11 (aa712d9), devshell: true runs nix develop --command sandbox -- changeset-management check with the release-tools in the caller's dev shell. The pinned input (5eb4c5d, followed from systemfsoftware main) already exports packages.<system>.release-tools, so the lock does not move * build(repo): take the sandbox from pnpm-release-management unpatched Conductor rulings, cycles 74 and 77: pnpm-release-management main 537d17c carries #14, #20 (the consumer store) and #24 (the linked-worktree change), so the starter's own copy of that change (nix/patches/sandbox-linked-worktree-git.patch) and its applyPatches wiring go. pnpm-release-management becomes a direct input on main, locked to 537d17c; systemfsoftware follows it, and its nixpkgs, comment-checker and importPnpmLock follow the starter's * test(repo): drop every sandbox launcher from PATH in the hooks proof starter-verify c73: withoutSandboxOnPath removed only the first directory that held sandbox, so with two launchers on PATH the hooks still found one and the refusal step failed falsely. It now drops every PATH entry that provides sandbox. Two launchers on PATH: the old helper fails the step, the new one passes; with the hooks' sandbox prefix stripped, the step fails * build(repo): take the stryker packages from the stryker-js-effect flake Conductor ruling, cycle 83: stryker-js-effect main f06f244 (#196) publishes its 17 members as tarballs with an index.json. The flake adds it as an input (locked to f06f244, following the starter's nixpkgs, comment-checker, importPnpmLock, pnpm-release-management and systemfsoftware), and .sfs-deps now holds both flakes' tarballs with one merged index.json. Every @systemfsoftware/stryker-* member is a file:.sfs-deps catalog entry with an override, the same mechanism as the systemfsoftware packages, plus one for the stryker-js-vm-runner npm alias that stryker-js 17.0.2 declares, so check:sfs-sources passes with no npm-sourced @systemfsoftware/* package * revert(repo): take lake1/deploy's merge back off lake1/nix-sandbox 7857171 (lake1/nix-sandbox merged into lake1/deploy) was pushed to lake1/nix-sandbox by mistake during the cycle 83 merge-up. This restores the layer's tree to fb4f8a1 exactly; lake1/deploy reverts this revert so it keeps its own content
kiro-systemf Bot
pushed a commit
that referenced
this pull request
Oct 7, 2026
* ci(ci): move mutation to a release gate on main check:ci no longer runs stryker. The release gate on push to main plans one shard per workspace package that declares a mutation script, refuses an empty set, and runs each shard at break 100 on the fleet with its incremental report cached. The checker drops prioritizePerformanceOverAccuracy, which stryker-js 15 removes. Operator approval: Kiro, 2026-10-05 (GATE1) * ci(ci): run the release gate on github-hosted runners The plan and per-package mutation jobs move from the self-hosted fleet, which admits only private repositories, to ubuntu-latest. Mutation stays one parallel job per package. Operator approval: Kiro, 2026-10-05 (GATE1) * fix(ci): refuse a mutation shard whose mutate globs match no files A package with a mutation script whose mutate globs match nothing reports zero mutants and passes a break-100 threshold vacuously (review finding #9). Each package now declares its globs once, as stryker.mutate in package.json; its Stryker config reads them and the release gate's shard planner expands them. The planner fails red at plan time naming the package, its directory and its globs when they match no file, including a package with no declared globs. Its Deno tests are ordinary tests: a turbo root task, test:scripts, runs them through the dev shell's deno, and both pnpm test and check:ci run it, so a local check:ci covers the planner exactly as CI does * ci(ci): pass the release gate with a notice when there are no decisions to mutate A decision is a *.workflow.ts file. When no workspace package has one, the planner emits an empty shard list and a 'No decisions to mutate' notice, and the mutation job is skipped. When decisions exist and no package declares a mutation script, it still refuses the empty set, naming how many decisions went unmutated. Kiro ruling, 2026-10-06 * deps(deps): move to effect 4 stable at exact pins Every catalog entry is an exact version: effect 4.0.1, the systemfsoftware toolchain on its Effect 4 stable majors (recommended preset 4.0.0, stryker-js 15.0.1, vitest 2.0.0, gherkin-spec 7.0.1) and current tooling. minimumReleaseAge 1440 is explicit, so the policy is strict; the exclude list holds the effect name patterns and one exact entry per resolved systemfsoftware package. oxlint-tsgolint stays 7.0.2001, the exact peer the presets require. turbo 2.11 writes an agent guidance block into AGENTS.md unless agentGuidance is false, so turbo.json opts out. Grader package majors move here because they require Effect 4 stable (CONST-W3 declared) * build(repo): move lint, test and mutation settings to the root oxlint.shared.ts, vitest.shared.ts and stryker.shared.ts hold the house settings; each package config extends them and declares only its own mutate set and aliases. Both lint overrides are gone: preset 4.0.0 already accepts Gherkin step bodies and build-config imports. The root lint file is named oxlint.shared.ts so lint-staged's nearest-config walk never treats the repository root as a package. Turbo inputs track the shared files. Operator approval: Kiro, 2026-10-05 (GATE1) * build(repo): declare the effect/http unstable-api opt-in once at the root Root tsconfig.base.json extends the bundler/dom preset and allows exactly effect/http through the effect language service. effect 4.0.1 ships HTTP only as unstable and global-fetch-in-effect bans the fetch alternative. Operator approval: Kiro, 2026-10-05 (GATE1) * feat(repo): serve the starter site from one worker One Cloudflare Worker serves the TanStack Start site, defined with Alchemy and run locally by pnpm dev through alchemy dev. The e2e journeys run against that local app * ci(ci): run the e2e journeys against pnpm dev on linux and macos bin/journeys starts the app with pnpm dev, runs the journeys against it and stops it with one SIGINT to its process group, so alchemy's local sidecar is not orphaned. The dev port is strict, so a stale server fails the run instead of moving it to another port * feat(repo): serve an effect httpapi and its openapi document from the site worker The site Worker routes /api/ to an Effect HttpApi: GET /api/health answers {"status":"ok"}, and /api/openapi.json serves the OpenAPI 3.1 document HttpApiBuilder generates. Every other path still goes to TanStack Start under the strict CSP. The root unstable-API opt-in gains exactly effect/http-api, where Effect 4.0.1 ships HttpApi (Kiro ruling, 2026-10-06). A journey reads the API description and calls the health check against pnpm dev * feat(repo): preview every pull request and deploy production after the release gate In an adopter's copy, never the template, a same-repo pull request deploys as its pr-<N> stage, gets its URL in one PR comment, runs the e2e journeys against it and is destroyed when it closes. Main deploys production once every mutation shard passes, to SITE_DOMAIN when that repository variable is set. The deployed journeys run through pnpm journeys:deployed, whose sandbox reaches only the site and gets no Cloudflare credentials * feat(repo): call the site worker through effect rpc instead of httpapi Ryan ruling via Kiro 2026-10-07: one RpcGroup served by RpcServer over HTTP at /api/rpc, a typed RpcClient in the page; a Health procedure replaces /api/health and the OpenAPI document goes. The home page shows the Worker's health and a browser journey reads it. The unstable-API opt-in lists effect/rpc instead of effect/http-api * refactor(repo): delete the hello seed package packages/starter and every trace of it leave in one commit: the README sections that describe it, the trusted-publisher note in .changeset, and the catalog entries no remaining package uses (rimraf, tsdown). npm publishing goes with it. The release job keeps capture, release-note assertion, tagging and GitHub Releases and drops build, OIDC and pnpm publish. A version in a manifest with no matching <name>@v<version> tag is owed; no script queries registry.npmjs.org. Operator approval: Kiro, 2026-10-05 (release.yml) * feat(repo): enforce a strict nonce csp with trusted types The Worker draws a fresh nonce per request, hands it to TanStack Start for its scripts and sends a strict Content-Security-Policy with Trusted Types. A browser journey in Chromium loads the home page under that policy with no violations, and removing the Trusted Types directive fails it * build(repo): run all dependency code in the sandbox, systemfsoftware packages from nix Every script that runs dependency code (install, build, test, dev, the journeys and the git hooks) goes through prm's sandbox launcher, systemfsoftware packages come from the systemfsoftware flake as tarballs, and the pnpm store is built from per-tarball fetches. check:sfs-sources stays red on the two Stryker packages, which still resolve from npm until systemfsoftware/stryker-js-effect ships them as flake outputs * feat(repo): deploy the site from one command pnpm run deploy deploys the Worker with Alchemy to the adopter's own Cloudflare account through bin/cloud, the one place Cloudflare credentials enter the sandbox; cloud stages keep their state in Cloudflare. A production domain is adopter configuration: SITE_DOMAIN, read by alchemy.run.ts on the prod stage. The template holds no credentials * ci(ci): deploy production after a passing or skipped mutation job With no decisions to mutate the release gate skips the mutation job, which used to skip the deploy that needs it. Production now deploys when the plan passed and the mutation job passed or was skipped, and never when either failed or the run was cancelled * refactor(repo): name the site's procedure health * ci(ci): pin the shared release tooling to main, as main does * build(repo): take systemfsoftware from main and pnpm-release-management from its lock systemfsoftware main 497dd37 fetches each workspace tarball as its own fixed-output derivation, so the tarballs match across linux and darwin; the pre-squash #606 commit hashed pnpm's whole store, which differs on darwin. pnpm-release-management now follows systemfsoftware's lock (5eb4c5d). The sandbox proofs take iplConfigHook and the whole nix tree, as prm's own flake does; the lockfile records the new tarballs' integrity * build(repo): build the sandbox's pnpm store with pnpm-release-management's consumer store pnpm 12 checks TLS with the platform verifier, which on macOS refuses mitm-cache's per-build certificate (UnknownIssuer on every registry tarball); prm's mkPnpmConsumerStore replays the fetches over plain HTTP for pnpm 12, still checking each tarball's lockfile integrity. The starter's own store derivation goes * ci(ci): run the release gate only on pushes to main starter-verify F10 and F13 (Kiro ruling, cycle 35): drop workflow_dispatch so mutation runs on push to main only; the README no longer claims Cell workflows, Cell.provide, tenant-bound store ports or compile-time phase markers, and no longer tells readers to run pnpm mutation locally * chore(repo): refuse to run the journeys when port 1337 is already taken starter-verify F6 (Kiro ruling, cycle 35): a stale server on :1337 would answer the journeys instead of this run's pnpm dev. Proof: with a python http.server on 127.0.0.1:1337, bin/journeys prints the refusal and exits 1; with the port free, pnpm journeys passes 2 of 2 * test(repo): drop the plain-fetch tests from the journeys suite starter-verify F9 (Kiro ruling, cycle 35): home.integration.test.ts fetched / without a browser, and request-timeout.integration.test.ts only tested the fetch fixture's own deadline. The strict-CSP browser journey already asserts / answers 200, and #62 adds the health journey. Nothing else imports site.fixture.ts, so it goes too. pnpm journeys: 1 of 1 passed; e2e typecheck and lint clean * chore(repo): restore commitlint.config.ts as main has it starter-verify F4 (Kiro ruling, cycle 35): commitlint.config.ts is a read-only evaluator surface, so #52 returns it byte-for-byte to main's version. No gate needs the edit: from a linked worktree, the sandboxed commit-msg hook still sees the staged files and refuses a fix(...) commit of this tooling-only diff (type-matches-diff-shape) * ci(ci): stop running the journeys against deployed sites starter-verify F11 (Kiro ruling, cycle 35): deployed-site journeys are not in the spec, so they come out: bin/journeys-deployed, the journeys:deployed script, the journeys and artifact-upload steps in previews.yml and release-gate.yml (the uploads only carried what those journeys wrote), the README line and the AGENTS.md clause. Deploy, preview comment and destroy are unchanged; the preview job is named for what it does now * fix(repo): send every rpc call to the served path in one round trip starter-verify F12 (Kiro ruling, cycle 35): RpcClient.layerProtocolHttp prepends its url to the protocol's empty request path, and joining '/api/rpc' with '' gives '/api/rpc/'. The Worker serves '/api/rpc' exactly, so every call took a 307 to the served path. The client now builds the protocol with makeProtocolHttp over a client whose requests are set to SITE_RPC_PATH. Probe (Chromium against pnpm dev, every /api/rpc* request and response on a home-page load): before, POST /api/rpc/ -> 307 -> POST /api/rpc -> 200 per call; after, POST /api/rpc -> 200. Site typecheck and lint clean; pnpm journeys green * chore(repo): fail commitlint closed when git cannot read the index Kiro ruling (cycle 35, F4 follow-up), declared per CONST-W3 in #52: commitlint.config.ts is an evaluator surface, and this restores #52's tightening of it that F4 reverted. stagedFiles no longer catches git's failure and returns no files; it lets the error through, so an unreadable index fails the commit instead of passing type-matches-diff-shape as no staged files. The sandbox proof sandbox-proofs/git-hooks.test.ts:135 depends on it; with main's version it fails with 'git failed silently' * ci(ci): let the macos chromium download follow the chrome for testing redirect Kiro ruling (cycle 35), declared per CONST-W3 in #52: ci.yml's macOS Playwright install step gains exactly --allow-host storage.googleapis.com. cdn.playwright.dev answers Chrome for Testing builds with a 307 to storage.googleapis.com/chrome-for-testing-public/..., which the sandbox refused, so the download failed with 403 (run 37566060960, job 112614085077) after the macOS dev shell started building * build(repo): drop darwin from the flake and the toolchain Ryan's standing rule via Kiro (cycle 52): no macOS anywhere. The flake builds x86_64-linux and aarch64-linux only; dprint pins only Linux archives; the comment-checker sandbox loses its sandbox-exec branch; AGENTS.md says CI is Linux only * ci(ci): run the journeys on linux only Ryan's standing rule via Kiro (cycle 52): no macOS legs. The journeys job runs on ubuntu-latest alone * ci(ci): drop the macos chromium install step Ryan's standing rule via Kiro (cycle 52): journeys run on Linux only, where Chromium comes from the dev shell * build(repo): drop darwin from the sandbox patch and supported architectures Ryan's standing rule via Kiro (cycle 52): the linked-worktree sandbox patch keeps only its bubblewrap hunks (the seatbelt profile hunks go), and pnpm resolves optional packages for linux only. The macOS Playwright install step, with the storage.googleapis.com allowance, left with the merge of #49 * chore(repo): drop the journeys busy-port guard Conductor ruling, cycle 61, F4: inside the sandbox's network namespace the guard can never fire, and that isolation already keeps a stale host server from answering the journeys * build(repo): show which tests ran in the turbo test logs Conductor ruling, cycle 61, F6: the test task's outputLogs is new-only, so CI logs show each executed test * ci(ci): install for the changeset check through the dev shell's bootstrap Conductor ruling, cycle 61, F2: the plain install outside Nix cannot read the .sfs-deps tarballs (ERR_PNPM_TARBALL_READ_LOCAL_TARBALL). devshell: true makes prm's shared workflow run the starter's own bootstrap script, which installs through sandbox. Evaluator-surface edit at the conductor's direction (CONST-W3) * build(repo): drop the root mutation script no workflow calls Conductor ruling, cycle 61, F5: the release gate runs turbo run mutation per package directly; nothing calls the root pnpm mutation * deps(deps): move @effect/tsgo to 0.50.0 Conductor ruling, cycle 62: the same bump pnpm-release-management#31 took. effect and every @effect/* stay at 4.0.1; none resolves lower. tsgo 0.50.0 reports nothing new: typecheck and lint are clean uncached. @systemfsoftware/oxlint-config-recommended 4.0.0 still pulls its own @effect/tsgo 0.45.0 * build(repo): take systemfsoftware main 8a4b543 Conductor ruling, cycle 62: systemfsoftware input moves from 497dd37 to main 8a4b543 (#659, #660, the macOS drops). pnpm-release-management still follows the rev systemfsoftware main pins (5eb4c5d). The published tarballs are byte-identical: the sandboxed bootstrap installs against the unchanged lockfile * build(repo): name the changesets versioning strategy, move @effect/tsgo to 0.51.0 Conductor ruling, cycle 67: prm main (#9, ca932ae) now requires versioning.strategy. The starter versions each workspace package from .changeset intents, so it takes changesets. Followed prm README: "`changesets` versioning drives the changesets libraries per package: the assembled release plan decides each member's bump, workspace dependents move with it, and the consumed intents are removed". No other field is required under that strategy. @effect/tsgo 0.50.0 -> 0.51.0 (npm latest, 2026-10-07T09:37Z); effect stays 4.0.1. Uncached check:ci and journeys pass with no new diagnostics * build(repo): give the dev shell release-tools from the pnpm-release-management input Conductor ruling, cycle 71: since pnpm-release-management#11 (aa712d9), devshell: true runs nix develop --command sandbox -- changeset-management check with the release-tools in the caller's dev shell. The pinned input (5eb4c5d, followed from systemfsoftware main) already exports packages.<system>.release-tools, so the lock does not move * build(repo): take the sandbox from pnpm-release-management unpatched Conductor rulings, cycles 74 and 77: pnpm-release-management main 537d17c carries #14, #20 (the consumer store) and #24 (the linked-worktree change), so the starter's own copy of that change (nix/patches/sandbox-linked-worktree-git.patch) and its applyPatches wiring go. pnpm-release-management becomes a direct input on main, locked to 537d17c; systemfsoftware follows it, and its nixpkgs, comment-checker and importPnpmLock follow the starter's * test(repo): drop every sandbox launcher from PATH in the hooks proof starter-verify c73: withoutSandboxOnPath removed only the first directory that held sandbox, so with two launchers on PATH the hooks still found one and the refusal step failed falsely. It now drops every PATH entry that provides sandbox. Two launchers on PATH: the old helper fails the step, the new one passes; with the hooks' sandbox prefix stripped, the step fails * build(repo): take the stryker packages from the stryker-js-effect flake Conductor ruling, cycle 83: stryker-js-effect main f06f244 (#196) publishes its 17 members as tarballs with an index.json. The flake adds it as an input (locked to f06f244, following the starter's nixpkgs, comment-checker, importPnpmLock, pnpm-release-management and systemfsoftware), and .sfs-deps now holds both flakes' tarballs with one merged index.json. Every @systemfsoftware/stryker-* member is a file:.sfs-deps catalog entry with an override, the same mechanism as the systemfsoftware packages, plus one for the stryker-js-vm-runner npm alias that stryker-js 17.0.2 declares, so check:sfs-sources passes with no npm-sourced @systemfsoftware/* package * revert(repo): take lake1/deploy's merge back off lake1/nix-sandbox 7857171 (lake1/nix-sandbox merged into lake1/deploy) was pushed to lake1/nix-sandbox by mistake during the cycle 83 merge-up. This restores the layer's tree to fb4f8a1 exactly; lake1/deploy reverts this revert so it keeps its own content
kiro-systemf Bot
pushed a commit
that referenced
this pull request
Oct 7, 2026
…der pnpm dev (#63) * ci(ci): move mutation to a release gate on main check:ci no longer runs stryker. The release gate on push to main plans one shard per workspace package that declares a mutation script, refuses an empty set, and runs each shard at break 100 on the fleet with its incremental report cached. The checker drops prioritizePerformanceOverAccuracy, which stryker-js 15 removes. Operator approval: Kiro, 2026-10-05 (GATE1) * ci(ci): run the release gate on github-hosted runners The plan and per-package mutation jobs move from the self-hosted fleet, which admits only private repositories, to ubuntu-latest. Mutation stays one parallel job per package. Operator approval: Kiro, 2026-10-05 (GATE1) * fix(ci): refuse a mutation shard whose mutate globs match no files A package with a mutation script whose mutate globs match nothing reports zero mutants and passes a break-100 threshold vacuously (review finding #9). Each package now declares its globs once, as stryker.mutate in package.json; its Stryker config reads them and the release gate's shard planner expands them. The planner fails red at plan time naming the package, its directory and its globs when they match no file, including a package with no declared globs. Its Deno tests are ordinary tests: a turbo root task, test:scripts, runs them through the dev shell's deno, and both pnpm test and check:ci run it, so a local check:ci covers the planner exactly as CI does * ci(ci): pass the release gate with a notice when there are no decisions to mutate A decision is a *.workflow.ts file. When no workspace package has one, the planner emits an empty shard list and a 'No decisions to mutate' notice, and the mutation job is skipped. When decisions exist and no package declares a mutation script, it still refuses the empty set, naming how many decisions went unmutated. Kiro ruling, 2026-10-06 * deps(deps): move to effect 4 stable at exact pins Every catalog entry is an exact version: effect 4.0.1, the systemfsoftware toolchain on its Effect 4 stable majors (recommended preset 4.0.0, stryker-js 15.0.1, vitest 2.0.0, gherkin-spec 7.0.1) and current tooling. minimumReleaseAge 1440 is explicit, so the policy is strict; the exclude list holds the effect name patterns and one exact entry per resolved systemfsoftware package. oxlint-tsgolint stays 7.0.2001, the exact peer the presets require. turbo 2.11 writes an agent guidance block into AGENTS.md unless agentGuidance is false, so turbo.json opts out. Grader package majors move here because they require Effect 4 stable (CONST-W3 declared) * build(repo): move lint, test and mutation settings to the root oxlint.shared.ts, vitest.shared.ts and stryker.shared.ts hold the house settings; each package config extends them and declares only its own mutate set and aliases. Both lint overrides are gone: preset 4.0.0 already accepts Gherkin step bodies and build-config imports. The root lint file is named oxlint.shared.ts so lint-staged's nearest-config walk never treats the repository root as a package. Turbo inputs track the shared files. Operator approval: Kiro, 2026-10-05 (GATE1) * build(repo): declare the effect/http unstable-api opt-in once at the root Root tsconfig.base.json extends the bundler/dom preset and allows exactly effect/http through the effect language service. effect 4.0.1 ships HTTP only as unstable and global-fetch-in-effect bans the fetch alternative. Operator approval: Kiro, 2026-10-05 (GATE1) * feat(repo): serve the starter site from one worker One Cloudflare Worker serves the TanStack Start site, defined with Alchemy and run locally by pnpm dev through alchemy dev. The e2e journeys run against that local app * ci(ci): run the e2e journeys against pnpm dev on linux and macos bin/journeys starts the app with pnpm dev, runs the journeys against it and stops it with one SIGINT to its process group, so alchemy's local sidecar is not orphaned. The dev port is strict, so a stale server fails the run instead of moving it to another port * feat(repo): serve an effect httpapi and its openapi document from the site worker The site Worker routes /api/ to an Effect HttpApi: GET /api/health answers {"status":"ok"}, and /api/openapi.json serves the OpenAPI 3.1 document HttpApiBuilder generates. Every other path still goes to TanStack Start under the strict CSP. The root unstable-API opt-in gains exactly effect/http-api, where Effect 4.0.1 ships HttpApi (Kiro ruling, 2026-10-06). A journey reads the API description and calls the health check against pnpm dev * feat(repo): give the site worker one d1 database, emulated locally under pnpm dev alchemy.run.ts declares a D1 database and binds it to the Worker as DB. alchemy dev creates and serves it locally with no cloud account, and each cloud stage gets its own. The Worker reads the binding from cloudflare:workers, typed through Alchemy's InferEnv of the Site, so a binding renamed on one side fails the typecheck. The Health procedure now answers ok only when the database answers SELECT 1, and the home page shows unreachable when it does not. Queries go through the D1 binding itself, with no effect/sql or @effect/sql-d1 * feat(repo): preview every pull request and deploy production after the release gate In an adopter's copy, never the template, a same-repo pull request deploys as its pr-<N> stage, gets its URL in one PR comment, runs the e2e journeys against it and is destroyed when it closes. Main deploys production once every mutation shard passes, to SITE_DOMAIN when that repository variable is set. The deployed journeys run through pnpm journeys:deployed, whose sandbox reaches only the site and gets no Cloudflare credentials * feat(repo): call the site worker through effect rpc instead of httpapi Ryan ruling via Kiro 2026-10-07: one RpcGroup served by RpcServer over HTTP at /api/rpc, a typed RpcClient in the page; a Health procedure replaces /api/health and the OpenAPI document goes. The home page shows the Worker's health and a browser journey reads it. The unstable-API opt-in lists effect/rpc instead of effect/http-api * docs(repo): name the token rights the d1 deploy needs * refactor(repo): delete the hello seed package packages/starter and every trace of it leave in one commit: the README sections that describe it, the trusted-publisher note in .changeset, and the catalog entries no remaining package uses (rimraf, tsdown). npm publishing goes with it. The release job keeps capture, release-note assertion, tagging and GitHub Releases and drops build, OIDC and pnpm publish. A version in a manifest with no matching <name>@v<version> tag is owed; no script queries registry.npmjs.org. Operator approval: Kiro, 2026-10-05 (release.yml) * feat(repo): enforce a strict nonce csp with trusted types The Worker draws a fresh nonce per request, hands it to TanStack Start for its scripts and sends a strict Content-Security-Policy with Trusted Types. A browser journey in Chromium loads the home page under that policy with no violations, and removing the Trusted Types directive fails it * build(repo): run all dependency code in the sandbox, systemfsoftware packages from nix Every script that runs dependency code (install, build, test, dev, the journeys and the git hooks) goes through prm's sandbox launcher, systemfsoftware packages come from the systemfsoftware flake as tarballs, and the pnpm store is built from per-tarball fetches. check:sfs-sources stays red on the two Stryker packages, which still resolve from npm until systemfsoftware/stryker-js-effect ships them as flake outputs * feat(repo): deploy the site from one command pnpm run deploy deploys the Worker with Alchemy to the adopter's own Cloudflare account through bin/cloud, the one place Cloudflare credentials enter the sandbox; cloud stages keep their state in Cloudflare. A production domain is adopter configuration: SITE_DOMAIN, read by alchemy.run.ts on the prod stage. The template holds no credentials * ci(ci): deploy production after a passing or skipped mutation job With no decisions to mutate the release gate skips the mutation job, which used to skip the deploy that needs it. Production now deploys when the plan passed and the mutation job passed or was skipped, and never when either failed or the run was cancelled * refactor(repo): name the site's procedure health * ci(ci): pin the shared release tooling to main, as main does * build(repo): take systemfsoftware from main and pnpm-release-management from its lock systemfsoftware main 497dd37 fetches each workspace tarball as its own fixed-output derivation, so the tarballs match across linux and darwin; the pre-squash #606 commit hashed pnpm's whole store, which differs on darwin. pnpm-release-management now follows systemfsoftware's lock (5eb4c5d). The sandbox proofs take iplConfigHook and the whole nix tree, as prm's own flake does; the lockfile records the new tarballs' integrity * build(repo): build the sandbox's pnpm store with pnpm-release-management's consumer store pnpm 12 checks TLS with the platform verifier, which on macOS refuses mitm-cache's per-build certificate (UnknownIssuer on every registry tarball); prm's mkPnpmConsumerStore replays the fetches over plain HTTP for pnpm 12, still checking each tarball's lockfile integrity. The starter's own store derivation goes * ci(ci): run the release gate only on pushes to main starter-verify F10 and F13 (Kiro ruling, cycle 35): drop workflow_dispatch so mutation runs on push to main only; the README no longer claims Cell workflows, Cell.provide, tenant-bound store ports or compile-time phase markers, and no longer tells readers to run pnpm mutation locally * chore(repo): refuse to run the journeys when port 1337 is already taken starter-verify F6 (Kiro ruling, cycle 35): a stale server on :1337 would answer the journeys instead of this run's pnpm dev. Proof: with a python http.server on 127.0.0.1:1337, bin/journeys prints the refusal and exits 1; with the port free, pnpm journeys passes 2 of 2 * test(repo): drop the plain-fetch tests from the journeys suite starter-verify F9 (Kiro ruling, cycle 35): home.integration.test.ts fetched / without a browser, and request-timeout.integration.test.ts only tested the fetch fixture's own deadline. The strict-CSP browser journey already asserts / answers 200, and #62 adds the health journey. Nothing else imports site.fixture.ts, so it goes too. pnpm journeys: 1 of 1 passed; e2e typecheck and lint clean * chore(repo): restore commitlint.config.ts as main has it starter-verify F4 (Kiro ruling, cycle 35): commitlint.config.ts is a read-only evaluator surface, so #52 returns it byte-for-byte to main's version. No gate needs the edit: from a linked worktree, the sandboxed commit-msg hook still sees the staged files and refuses a fix(...) commit of this tooling-only diff (type-matches-diff-shape) * ci(ci): stop running the journeys against deployed sites starter-verify F11 (Kiro ruling, cycle 35): deployed-site journeys are not in the spec, so they come out: bin/journeys-deployed, the journeys:deployed script, the journeys and artifact-upload steps in previews.yml and release-gate.yml (the uploads only carried what those journeys wrote), the README line and the AGENTS.md clause. Deploy, preview comment and destroy are unchanged; the preview job is named for what it does now * fix(repo): send every rpc call to the served path in one round trip starter-verify F12 (Kiro ruling, cycle 35): RpcClient.layerProtocolHttp prepends its url to the protocol's empty request path, and joining '/api/rpc' with '' gives '/api/rpc/'. The Worker serves '/api/rpc' exactly, so every call took a 307 to the served path. The client now builds the protocol with makeProtocolHttp over a client whose requests are set to SITE_RPC_PATH. Probe (Chromium against pnpm dev, every /api/rpc* request and response on a home-page load): before, POST /api/rpc/ -> 307 -> POST /api/rpc -> 200 per call; after, POST /api/rpc -> 200. Site typecheck and lint clean; pnpm journeys green * feat(repo): decide the worker's health in a pure workflow the site tests Kiro ruling (cycle 35, F5 follow-up): the site needs a test of its own that survives the guestbook's removal. check-health.workflow.ts is a Workflow.make decision from the D1 probe outcome to Healthy or the DatabaseUnreachable refusal, which becomes the health procedure's typed error; the handler only runs the probe and calls it. __tests__/check-health.workflow.property.test.ts states the law: healthy exactly when the probe answered. Sabotage: flipping the decision fails it (shrunk to ProbeUnanswered). The site's test and mutation toolchain (vitest, the Stryker set, effect-cell-types, configs) moves here from the guestbook layer with the first workflow, and the release-gate planner now plans @endgame/site * chore(repo): fail commitlint closed when git cannot read the index Kiro ruling (cycle 35, F4 follow-up), declared per CONST-W3 in #52: commitlint.config.ts is an evaluator surface, and this restores #52's tightening of it that F4 reverted. stagedFiles no longer catches git's failure and returns no files; it lets the error through, so an unreadable index fails the commit instead of passing type-matches-diff-shape as no staged files. The sandbox proof sandbox-proofs/git-hooks.test.ts:135 depends on it; with main's version it fails with 'git failed silently' * ci(ci): let the macos chromium download follow the chrome for testing redirect Kiro ruling (cycle 35), declared per CONST-W3 in #52: ci.yml's macOS Playwright install step gains exactly --allow-host storage.googleapis.com. cdn.playwright.dev answers Chrome for Testing builds with a 307 to storage.googleapis.com/chrome-for-testing-public/..., which the sandbox refused, so the download failed with 403 (run 37566060960, job 112614085077) after the macOS dev shell started building * build(repo): drop darwin from the flake and the toolchain Ryan's standing rule via Kiro (cycle 52): no macOS anywhere. The flake builds x86_64-linux and aarch64-linux only; dprint pins only Linux archives; the comment-checker sandbox loses its sandbox-exec branch; AGENTS.md says CI is Linux only * ci(ci): run the journeys on linux only Ryan's standing rule via Kiro (cycle 52): no macOS legs. The journeys job runs on ubuntu-latest alone * ci(ci): drop the macos chromium install step Ryan's standing rule via Kiro (cycle 52): journeys run on Linux only, where Chromium comes from the dev shell * build(repo): drop darwin from the sandbox patch and supported architectures Ryan's standing rule via Kiro (cycle 52): the linked-worktree sandbox patch keeps only its bubblewrap hunks (the seatbelt profile hunks go), and pnpm resolves optional packages for linux only. The macOS Playwright install step, with the storage.googleapis.com allowance, left with the merge of #49 * chore(repo): drop the journeys busy-port guard Conductor ruling, cycle 61, F4: inside the sandbox's network namespace the guard can never fire, and that isolation already keeps a stale host server from answering the journeys * build(repo): show which tests ran in the turbo test logs Conductor ruling, cycle 61, F6: the test task's outputLogs is new-only, so CI logs show each executed test * ci(ci): install for the changeset check through the dev shell's bootstrap Conductor ruling, cycle 61, F2: the plain install outside Nix cannot read the .sfs-deps tarballs (ERR_PNPM_TARBALL_READ_LOCAL_TARBALL). devshell: true makes prm's shared workflow run the starter's own bootstrap script, which installs through sandbox. Evaluator-surface edit at the conductor's direction (CONST-W3) * build(repo): drop the root mutation script no workflow calls Conductor ruling, cycle 61, F5: the release gate runs turbo run mutation per package directly; nothing calls the root pnpm mutation * deps(deps): move @effect/tsgo to 0.50.0 Conductor ruling, cycle 62: the same bump pnpm-release-management#31 took. effect and every @effect/* stay at 4.0.1; none resolves lower. tsgo 0.50.0 reports nothing new: typecheck and lint are clean uncached. @systemfsoftware/oxlint-config-recommended 4.0.0 still pulls its own @effect/tsgo 0.45.0 * build(repo): take systemfsoftware main 8a4b543 Conductor ruling, cycle 62: systemfsoftware input moves from 497dd37 to main 8a4b543 (#659, #660, the macOS drops). pnpm-release-management still follows the rev systemfsoftware main pins (5eb4c5d). The published tarballs are byte-identical: the sandboxed bootstrap installs against the unchanged lockfile * build(repo): name the changesets versioning strategy, move @effect/tsgo to 0.51.0 Conductor ruling, cycle 67: prm main (#9, ca932ae) now requires versioning.strategy. The starter versions each workspace package from .changeset intents, so it takes changesets. Followed prm README: "`changesets` versioning drives the changesets libraries per package: the assembled release plan decides each member's bump, workspace dependents move with it, and the consumed intents are removed". No other field is required under that strategy. @effect/tsgo 0.50.0 -> 0.51.0 (npm latest, 2026-10-07T09:37Z); effect stays 4.0.1. Uncached check:ci and journeys pass with no new diagnostics * build(repo): give the dev shell release-tools from the pnpm-release-management input Conductor ruling, cycle 71: since pnpm-release-management#11 (aa712d9), devshell: true runs nix develop --command sandbox -- changeset-management check with the release-tools in the caller's dev shell. The pinned input (5eb4c5d, followed from systemfsoftware main) already exports packages.<system>.release-tools, so the lock does not move * build(repo): take the sandbox from pnpm-release-management unpatched Conductor rulings, cycles 74 and 77: pnpm-release-management main 537d17c carries #14, #20 (the consumer store) and #24 (the linked-worktree change), so the starter's own copy of that change (nix/patches/sandbox-linked-worktree-git.patch) and its applyPatches wiring go. pnpm-release-management becomes a direct input on main, locked to 537d17c; systemfsoftware follows it, and its nixpkgs, comment-checker and importPnpmLock follow the starter's * test(repo): drop every sandbox launcher from PATH in the hooks proof starter-verify c73: withoutSandboxOnPath removed only the first directory that held sandbox, so with two launchers on PATH the hooks still found one and the refusal step failed falsely. It now drops every PATH entry that provides sandbox. Two launchers on PATH: the old helper fails the step, the new one passes; with the hooks' sandbox prefix stripped, the step fails * build(repo): take the stryker packages from the stryker-js-effect flake Conductor ruling, cycle 83: stryker-js-effect main f06f244 (#196) publishes its 17 members as tarballs with an index.json. The flake adds it as an input (locked to f06f244, following the starter's nixpkgs, comment-checker, importPnpmLock, pnpm-release-management and systemfsoftware), and .sfs-deps now holds both flakes' tarballs with one merged index.json. Every @systemfsoftware/stryker-* member is a file:.sfs-deps catalog entry with an override, the same mechanism as the systemfsoftware packages, plus one for the stryker-js-vm-runner npm alias that stryker-js 17.0.2 declares, so check:sfs-sources passes with no npm-sourced @systemfsoftware/* package * revert(repo): take lake1/deploy's merge back off lake1/nix-sandbox 7857171 (lake1/nix-sandbox merged into lake1/deploy) was pushed to lake1/nix-sandbox by mistake during the cycle 83 merge-up. This restores the layer's tree to fb4f8a1 exactly; lake1/deploy reverts this revert so it keeps its own content
kiro-systemf Bot
pushed a commit
that referenced
this pull request
Oct 7, 2026
* ci(ci): move mutation to a release gate on main check:ci no longer runs stryker. The release gate on push to main plans one shard per workspace package that declares a mutation script, refuses an empty set, and runs each shard at break 100 on the fleet with its incremental report cached. The checker drops prioritizePerformanceOverAccuracy, which stryker-js 15 removes. Operator approval: Kiro, 2026-10-05 (GATE1) * ci(ci): run the release gate on github-hosted runners The plan and per-package mutation jobs move from the self-hosted fleet, which admits only private repositories, to ubuntu-latest. Mutation stays one parallel job per package. Operator approval: Kiro, 2026-10-05 (GATE1) * fix(ci): refuse a mutation shard whose mutate globs match no files A package with a mutation script whose mutate globs match nothing reports zero mutants and passes a break-100 threshold vacuously (review finding #9). Each package now declares its globs once, as stryker.mutate in package.json; its Stryker config reads them and the release gate's shard planner expands them. The planner fails red at plan time naming the package, its directory and its globs when they match no file, including a package with no declared globs. Its Deno tests are ordinary tests: a turbo root task, test:scripts, runs them through the dev shell's deno, and both pnpm test and check:ci run it, so a local check:ci covers the planner exactly as CI does * ci(ci): pass the release gate with a notice when there are no decisions to mutate A decision is a *.workflow.ts file. When no workspace package has one, the planner emits an empty shard list and a 'No decisions to mutate' notice, and the mutation job is skipped. When decisions exist and no package declares a mutation script, it still refuses the empty set, naming how many decisions went unmutated. Kiro ruling, 2026-10-06 * deps(deps): move to effect 4 stable at exact pins Every catalog entry is an exact version: effect 4.0.1, the systemfsoftware toolchain on its Effect 4 stable majors (recommended preset 4.0.0, stryker-js 15.0.1, vitest 2.0.0, gherkin-spec 7.0.1) and current tooling. minimumReleaseAge 1440 is explicit, so the policy is strict; the exclude list holds the effect name patterns and one exact entry per resolved systemfsoftware package. oxlint-tsgolint stays 7.0.2001, the exact peer the presets require. turbo 2.11 writes an agent guidance block into AGENTS.md unless agentGuidance is false, so turbo.json opts out. Grader package majors move here because they require Effect 4 stable (CONST-W3 declared) * build(repo): move lint, test and mutation settings to the root oxlint.shared.ts, vitest.shared.ts and stryker.shared.ts hold the house settings; each package config extends them and declares only its own mutate set and aliases. Both lint overrides are gone: preset 4.0.0 already accepts Gherkin step bodies and build-config imports. The root lint file is named oxlint.shared.ts so lint-staged's nearest-config walk never treats the repository root as a package. Turbo inputs track the shared files. Operator approval: Kiro, 2026-10-05 (GATE1) * build(repo): declare the effect/http unstable-api opt-in once at the root Root tsconfig.base.json extends the bundler/dom preset and allows exactly effect/http through the effect language service. effect 4.0.1 ships HTTP only as unstable and global-fetch-in-effect bans the fetch alternative. Operator approval: Kiro, 2026-10-05 (GATE1) * feat(repo): serve the starter site from one worker One Cloudflare Worker serves the TanStack Start site, defined with Alchemy and run locally by pnpm dev through alchemy dev. The e2e journeys run against that local app * ci(ci): run the e2e journeys against pnpm dev on linux and macos bin/journeys starts the app with pnpm dev, runs the journeys against it and stops it with one SIGINT to its process group, so alchemy's local sidecar is not orphaned. The dev port is strict, so a stale server fails the run instead of moving it to another port * feat(repo): serve an effect httpapi and its openapi document from the site worker The site Worker routes /api/ to an Effect HttpApi: GET /api/health answers {"status":"ok"}, and /api/openapi.json serves the OpenAPI 3.1 document HttpApiBuilder generates. Every other path still goes to TanStack Start under the strict CSP. The root unstable-API opt-in gains exactly effect/http-api, where Effect 4.0.1 ships HttpApi (Kiro ruling, 2026-10-06). A journey reads the API description and calls the health check against pnpm dev * feat(repo): give the site worker one d1 database, emulated locally under pnpm dev alchemy.run.ts declares a D1 database and binds it to the Worker as DB. alchemy dev creates and serves it locally with no cloud account, and each cloud stage gets its own. The Worker reads the binding from cloudflare:workers, typed through Alchemy's InferEnv of the Site, so a binding renamed on one side fails the typecheck. The Health procedure now answers ok only when the database answers SELECT 1, and the home page shows unreachable when it does not. Queries go through the D1 binding itself, with no effect/sql or @effect/sql-d1 * feat(repo): preview every pull request and deploy production after the release gate In an adopter's copy, never the template, a same-repo pull request deploys as its pr-<N> stage, gets its URL in one PR comment, runs the e2e journeys against it and is destroyed when it closes. Main deploys production once every mutation shard passes, to SITE_DOMAIN when that repository variable is set. The deployed journeys run through pnpm journeys:deployed, whose sandbox reaches only the site and gets no Cloudflare credentials * feat(repo): call the site worker through effect rpc instead of httpapi Ryan ruling via Kiro 2026-10-07: one RpcGroup served by RpcServer over HTTP at /api/rpc, a typed RpcClient in the page; a Health procedure replaces /api/health and the OpenAPI document goes. The home page shows the Worker's health and a browser journey reads it. The unstable-API opt-in lists effect/rpc instead of effect/http-api * docs(repo): name the token rights the d1 deploy needs * feat(repo): add a guestbook example feature over rpc and d1, removable in one step A pure Workflow.make decision trims and refuses a guestbook entry with typed errors; two RPC procedures sign and list entries through a small Effect service over the D1 binding; the page calls them through the site's typed RpcClient and shows the typed refusal. Its laws run under vitest, its journeys in a real browser against pnpm dev. Everything lives under the two guestbook folders; the README names the four registration points and how to undo them * refactor(repo): delete the hello seed package packages/starter and every trace of it leave in one commit: the README sections that describe it, the trusted-publisher note in .changeset, and the catalog entries no remaining package uses (rimraf, tsdown). npm publishing goes with it. The release job keeps capture, release-note assertion, tagging and GitHub Releases and drops build, OIDC and pnpm publish. A version in a manifest with no matching <name>@v<version> tag is owed; no script queries registry.npmjs.org. Operator approval: Kiro, 2026-10-05 (release.yml) * feat(repo): enforce a strict nonce csp with trusted types The Worker draws a fresh nonce per request, hands it to TanStack Start for its scripts and sends a strict Content-Security-Policy with Trusted Types. A browser journey in Chromium loads the home page under that policy with no violations, and removing the Trusted Types directive fails it * build(repo): run all dependency code in the sandbox, systemfsoftware packages from nix Every script that runs dependency code (install, build, test, dev, the journeys and the git hooks) goes through prm's sandbox launcher, systemfsoftware packages come from the systemfsoftware flake as tarballs, and the pnpm store is built from per-tarball fetches. check:sfs-sources stays red on the two Stryker packages, which still resolve from npm until systemfsoftware/stryker-js-effect ships them as flake outputs * feat(repo): deploy the site from one command pnpm run deploy deploys the Worker with Alchemy to the adopter's own Cloudflare account through bin/cloud, the one place Cloudflare credentials enter the sandbox; cloud stages keep their state in Cloudflare. A production domain is adopter configuration: SITE_DOMAIN, read by alchemy.run.ts on the prod stage. The template holds no credentials * ci(ci): deploy production after a passing or skipped mutation job With no decisions to mutate the release gate skips the mutation job, which used to skip the deploy that needs it. Production now deploys when the plan passed and the mutation job passed or was skipped, and never when either failed or the run was cancelled * refactor(repo): name the site's procedure health * test(repo): split the guestbook journeys into sign, see the entry and empty refused Each journey opens its own browser context, so its own cookie jar; seeing the entry reopens the guestbook in a second context after signing * refactor(repo): name the guestbook procedures sign and list * ci(ci): pin the shared release tooling to main, as main does * build(repo): take systemfsoftware from main and pnpm-release-management from its lock systemfsoftware main 497dd37 fetches each workspace tarball as its own fixed-output derivation, so the tarballs match across linux and darwin; the pre-squash #606 commit hashed pnpm's whole store, which differs on darwin. pnpm-release-management now follows systemfsoftware's lock (5eb4c5d). The sandbox proofs take iplConfigHook and the whole nix tree, as prm's own flake does; the lockfile records the new tarballs' integrity * build(repo): build the sandbox's pnpm store with pnpm-release-management's consumer store pnpm 12 checks TLS with the platform verifier, which on macOS refuses mitm-cache's per-build certificate (UnknownIssuer on every registry tarball); prm's mkPnpmConsumerStore replays the fetches over plain HTTP for pnpm 12, still checking each tarball's lockfile integrity. The starter's own store derivation goes * ci(ci): run the release gate only on pushes to main starter-verify F10 and F13 (Kiro ruling, cycle 35): drop workflow_dispatch so mutation runs on push to main only; the README no longer claims Cell workflows, Cell.provide, tenant-bound store ports or compile-time phase markers, and no longer tells readers to run pnpm mutation locally * chore(repo): refuse to run the journeys when port 1337 is already taken starter-verify F6 (Kiro ruling, cycle 35): a stale server on :1337 would answer the journeys instead of this run's pnpm dev. Proof: with a python http.server on 127.0.0.1:1337, bin/journeys prints the refusal and exits 1; with the port free, pnpm journeys passes 2 of 2 * test(repo): drop the plain-fetch tests from the journeys suite starter-verify F9 (Kiro ruling, cycle 35): home.integration.test.ts fetched / without a browser, and request-timeout.integration.test.ts only tested the fetch fixture's own deadline. The strict-CSP browser journey already asserts / answers 200, and #62 adds the health journey. Nothing else imports site.fixture.ts, so it goes too. pnpm journeys: 1 of 1 passed; e2e typecheck and lint clean * chore(repo): restore commitlint.config.ts as main has it starter-verify F4 (Kiro ruling, cycle 35): commitlint.config.ts is a read-only evaluator surface, so #52 returns it byte-for-byte to main's version. No gate needs the edit: from a linked worktree, the sandboxed commit-msg hook still sees the staged files and refuses a fix(...) commit of this tooling-only diff (type-matches-diff-shape) * ci(ci): stop running the journeys against deployed sites starter-verify F11 (Kiro ruling, cycle 35): deployed-site journeys are not in the spec, so they come out: bin/journeys-deployed, the journeys:deployed script, the journeys and artifact-upload steps in previews.yml and release-gate.yml (the uploads only carried what those journeys wrote), the README line and the AGENTS.md clause. Deploy, preview comment and destroy are unchanged; the preview job is named for what it does now * fix(repo): send every rpc call to the served path in one round trip starter-verify F12 (Kiro ruling, cycle 35): RpcClient.layerProtocolHttp prepends its url to the protocol's empty request path, and joining '/api/rpc' with '' gives '/api/rpc/'. The Worker serves '/api/rpc' exactly, so every call took a 307 to the served path. The client now builds the protocol with makeProtocolHttp over a client whose requests are set to SITE_RPC_PATH. Probe (Chromium against pnpm dev, every /api/rpc* request and response on a home-page load): before, POST /api/rpc/ -> 307 -> POST /api/rpc -> 200 per call; after, POST /api/rpc -> 200. Site typecheck and lint clean; pnpm journeys green * chore(repo): fail the site test run when it finds no test files starter-verify F5 (Kiro ruling, cycle 35): the site has tests, so an empty run must fail. Without --passWithNoTests, pnpm --filter @endgame/site test runs the 5 guestbook properties (exit 0), and vitest run over a directory with no test files exits 1 (No test files found) * docs(repo): say what removing the guestbook leaves in a deployed d1 starter-verify F8 (Kiro ruling, cycle 35): removal leaves the guestbook_entries table and its 0001_create_guestbook_entries.sql row in __alchemy_migrations (alchemy's default migrations table) in a deployed D1; the README says so and gives the two statements that drop them. Both names as found in the local D1 pnpm dev created * feat(repo): decide the worker's health in a pure workflow the site tests Kiro ruling (cycle 35, F5 follow-up): the site needs a test of its own that survives the guestbook's removal. check-health.workflow.ts is a Workflow.make decision from the D1 probe outcome to Healthy or the DatabaseUnreachable refusal, which becomes the health procedure's typed error; the handler only runs the probe and calls it. __tests__/check-health.workflow.property.test.ts states the law: healthy exactly when the probe answered. Sabotage: flipping the decision fails it (shrunk to ProbeUnanswered). The site's test and mutation toolchain (vitest, the Stryker set, effect-cell-types, configs) moves here from the guestbook layer with the first workflow, and the release-gate planner now plans @endgame/site * chore(repo): fail commitlint closed when git cannot read the index Kiro ruling (cycle 35, F4 follow-up), declared per CONST-W3 in #52: commitlint.config.ts is an evaluator surface, and this restores #52's tightening of it that F4 reverted. stagedFiles no longer catches git's failure and returns no files; it lets the error through, so an unreadable index fails the commit instead of passing type-matches-diff-shape as no staged files. The sandbox proof sandbox-proofs/git-hooks.test.ts:135 depends on it; with main's version it fails with 'git failed silently' * ci(ci): let the macos chromium download follow the chrome for testing redirect Kiro ruling (cycle 35), declared per CONST-W3 in #52: ci.yml's macOS Playwright install step gains exactly --allow-host storage.googleapis.com. cdn.playwright.dev answers Chrome for Testing builds with a 307 to storage.googleapis.com/chrome-for-testing-public/..., which the sandbox refused, so the download failed with 403 (run 37566060960, job 112614085077) after the macOS dev shell started building * build(repo): drop darwin from the flake and the toolchain Ryan's standing rule via Kiro (cycle 52): no macOS anywhere. The flake builds x86_64-linux and aarch64-linux only; dprint pins only Linux archives; the comment-checker sandbox loses its sandbox-exec branch; AGENTS.md says CI is Linux only * ci(ci): run the journeys on linux only Ryan's standing rule via Kiro (cycle 52): no macOS legs. The journeys job runs on ubuntu-latest alone * ci(ci): drop the macos chromium install step Ryan's standing rule via Kiro (cycle 52): journeys run on Linux only, where Chromium comes from the dev shell * build(repo): drop darwin from the sandbox patch and supported architectures Ryan's standing rule via Kiro (cycle 52): the linked-worktree sandbox patch keeps only its bubblewrap hunks (the seatbelt profile hunks go), and pnpm resolves optional packages for linux only. The macOS Playwright install step, with the storage.googleapis.com allowance, left with the merge of #49 * chore(repo): drop the journeys busy-port guard Conductor ruling, cycle 61, F4: inside the sandbox's network namespace the guard can never fire, and that isolation already keeps a stale host server from answering the journeys * build(repo): show which tests ran in the turbo test logs Conductor ruling, cycle 61, F6: the test task's outputLogs is new-only, so CI logs show each executed test * ci(ci): install for the changeset check through the dev shell's bootstrap Conductor ruling, cycle 61, F2: the plain install outside Nix cannot read the .sfs-deps tarballs (ERR_PNPM_TARBALL_READ_LOCAL_TARBALL). devshell: true makes prm's shared workflow run the starter's own bootstrap script, which installs through sandbox. Evaluator-surface edit at the conductor's direction (CONST-W3) * build(repo): drop the root mutation script no workflow calls Conductor ruling, cycle 61, F5: the release gate runs turbo run mutation per package directly; nothing calls the root pnpm mutation * docs(repo): restore the guestbook section and its removal steps Conductor ruling, cycle 61, F1: merge 57a661c took d1's README and dropped the guestbook paragraph, the removal list and the D1 note. Restored from 8f8de78, updated to the six steps that remove the feature today * deps(deps): move @effect/tsgo to 0.50.0 Conductor ruling, cycle 62: the same bump pnpm-release-management#31 took. effect and every @effect/* stay at 4.0.1; none resolves lower. tsgo 0.50.0 reports nothing new: typecheck and lint are clean uncached. @systemfsoftware/oxlint-config-recommended 4.0.0 still pulls its own @effect/tsgo 0.45.0 * build(repo): take systemfsoftware main 8a4b543 Conductor ruling, cycle 62: systemfsoftware input moves from 497dd37 to main 8a4b543 (#659, #660, the macOS drops). pnpm-release-management still follows the rev systemfsoftware main pins (5eb4c5d). The published tarballs are byte-identical: the sandboxed bootstrap installs against the unchanged lockfile * build(repo): name the changesets versioning strategy, move @effect/tsgo to 0.51.0 Conductor ruling, cycle 67: prm main (#9, ca932ae) now requires versioning.strategy. The starter versions each workspace package from .changeset intents, so it takes changesets. Followed prm README: "`changesets` versioning drives the changesets libraries per package: the assembled release plan decides each member's bump, workspace dependents move with it, and the consumed intents are removed". No other field is required under that strategy. @effect/tsgo 0.50.0 -> 0.51.0 (npm latest, 2026-10-07T09:37Z); effect stays 4.0.1. Uncached check:ci and journeys pass with no new diagnostics * build(repo): give the dev shell release-tools from the pnpm-release-management input Conductor ruling, cycle 71: since pnpm-release-management#11 (aa712d9), devshell: true runs nix develop --command sandbox -- changeset-management check with the release-tools in the caller's dev shell. The pinned input (5eb4c5d, followed from systemfsoftware main) already exports packages.<system>.release-tools, so the lock does not move * build(repo): take the sandbox from pnpm-release-management unpatched Conductor rulings, cycles 74 and 77: pnpm-release-management main 537d17c carries #14, #20 (the consumer store) and #24 (the linked-worktree change), so the starter's own copy of that change (nix/patches/sandbox-linked-worktree-git.patch) and its applyPatches wiring go. pnpm-release-management becomes a direct input on main, locked to 537d17c; systemfsoftware follows it, and its nixpkgs, comment-checker and importPnpmLock follow the starter's * test(repo): drop every sandbox launcher from PATH in the hooks proof starter-verify c73: withoutSandboxOnPath removed only the first directory that held sandbox, so with two launchers on PATH the hooks still found one and the refusal step failed falsely. It now drops every PATH entry that provides sandbox. Two launchers on PATH: the old helper fails the step, the new one passes; with the hooks' sandbox prefix stripped, the step fails * build(repo): take the stryker packages from the stryker-js-effect flake Conductor ruling, cycle 83: stryker-js-effect main f06f244 (#196) publishes its 17 members as tarballs with an index.json. The flake adds it as an input (locked to f06f244, following the starter's nixpkgs, comment-checker, importPnpmLock, pnpm-release-management and systemfsoftware), and .sfs-deps now holds both flakes' tarballs with one merged index.json. Every @systemfsoftware/stryker-* member is a file:.sfs-deps catalog entry with an override, the same mechanism as the systemfsoftware packages, plus one for the stryker-js-vm-runner npm alias that stryker-js 17.0.2 declares, so check:sfs-sources passes with no npm-sourced @systemfsoftware/* package * revert(repo): take lake1/deploy's merge back off lake1/nix-sandbox 7857171 (lake1/nix-sandbox merged into lake1/deploy) was pushed to lake1/nix-sandbox by mistake during the cycle 83 merge-up. This restores the layer's tree to fb4f8a1 exactly; lake1/deploy reverts this revert so it keeps its own content * chore(repo): name both flakes in the sfs-sources message Conductor ruling, cycle 87: the stryker packages come from the stryker-js-effect flake since #52, so the message names both flakes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every entry point that runs dependency code now runs inside prm's deny-by-default launcher (
sandbox), and every@systemfsoftware/*package the systemfsoftware monorepo publishes comes from its flake, not from npm (Kiro rulings, 2026-10-06). The layer sits onmain, below #50, and stays a draft whilecheck:sfs-sourcesis red (below).What changes
main(locked at8a4b543) and pnpm-release-management (prm) atmain(locked at537d17c, which carries prm chore(deps): bump the other-minor-patch group across 1 directory with 5 updates #14, chore(deps): bump the other-minor-patch group across 1 directory with 5 updates #20 and chore(deps): bump turbo from 2.10.12 to 2.11.4 in the turborepo-minor-patch group across 1 directory #24) as inputs; systemfsoftware follows the starter's prm. The 18 packages the starter uses from the systemfsoftware monorepo arefile:.sfs-deps/*.tgzcatalog entries with overrides, and their release-age exclusions are gone.lib.mkPnpmConsumerStore, given the lockfile,pnpm-workspace.yaml, everypackage.jsonand the.sfs-depstarballs. Every tarball is its own fixed-output fetch keyed by its lockfile integrity, and the starter carries no store hash, so a lockfile change, a Dependabot bump included, needs no hash edit.pnpm devandpnpm journeysincluded), the git hooks (from a linked worktree too), dprint's plugins, and every CI job: install, gates, journeys, commitlint, changeset check, mutation, and release. CI jobs install through.github/actions/dev-shell, offline from the Nix store.ignoreScriptsstops a hostpnpm installfrom running lifecycle scripts, andverifyDepsBeforeRun: warnstops pnpm installing on the host before a script.supportedArchitecturesresolves optional packages forlinuxonly. The launcher is prm's, unpatched: prm chore(deps): bump turbo from 2.10.12 to 2.11.4 in the turborepo-minor-patch group across 1 directory #24 carries the linked-worktree change the starter used to patch in.check:sfs-sourcesfails while any@systemfsoftware/*lockfile entry resolves from the registry. It is red by design:@systemfsoftware/stryker-js15.0.1 and@systemfsoftware/stryker-js-vitest-runner8.0.3 resolve from npm until systemfsoftware/stryker-js-effect ships them as flake outputs (stryker-js-effect#196).Declared under CONST-W3
commitlint.config.tsis an evaluator surface (AGENTS.md), and this layer changes it:stagedFilesno longer turns a failedgit diff --cachedinto an empty list. Onmain, an index git cannot read passes as "no staged files"; here git's error reaches stderr and commitlint fails. The sandbox proof atsandbox-proofs/git-hooks.test.ts:138("commitlint fails with git's error when git cannot read the index") needs it, and withmain's version that step fails. Kiro authorized the change (cycle 35, F4 follow-up, restored inb9854fe) and owns the instrument.Proofs
@types/nodewent from 24.19.1 to 25.9.9 (catalog and lockfile only), the store built, and on a fresh tree the sandboxedpnpm bootstrapinstalled from the launcher's store view: 660 reused, 0 downloaded. On prm's consumer store: feat(repo): give the site worker one d1 database, emulated locally under pnpm dev #63 adds the site's test toolchain (374 lockfile lines), andpnpm bootstrapbuilds the store and installs with no hash edit.770abef: one digest changed inerror-stack-parser-es's integrity giveshash mismatch in fixed-output derivation '…error-stack-parser-es-1.0.5.tgz.drv', specifiedsha512-6qu…, gotsha512-5qu…, and the store does not build./root/.local/share/pnpm/store/v11) ahead of the bootstrap script. After: a warning, then the install inside the sandbox.bin/cloudreachedapi.cloudflare.com(400) and a preview'sworkers.devhost (200), and the launcher refusedexample.com. OfGH_TOKEN,SECRET_THINGand the Cloudflare variables, only the declared ones crossed.Gate
Gate at
e4614c4, clean worktree, Linux, sandboxed:On a local commit with the same tree,
nix buildof.#pnpm-store,.#sandboxand.#sandbox-proofsexits 0, the dev shell starts, andsandbox -- changeset-management check <merge-base>exits 0 ("no publishable package changed").CI run 37667193245 on
e4614c4: 6 of 7 jobs pass (format, lint, typecheck, test, dist, journeys);check (sfs-sources)fails with the message above. Commitlint (37667193141) and Changeset Check (37667194022) pass.Found while landing it
CHANGELOG.mdthe sandboxed build does not see. CI runs sandboxed, so the lockfile records the sandboxed bytes. A host with the sandbox off has to build.#sfs-depswith--option sandbox true.supportedArchitecturesto Linux left the count at 1243.Cycles 61 and 62 (conductor rulings)
fbcd048): the Changeset Check caller passesdevshell: true, so prm's shared workflow installs through the starter's ownbootstrapscript instead of a plainpnpm installthat cannot read the.sfs-depstarballs (ERR_PNPM_TARBALL_READ_LOCAL_TARBALL). Declared under CONST-W3:.github/workflows/is an evaluator surface, edited at the conductor's direction. Changeset Check is green on this layer (37648415465) and on feat(repo): add a guestbook example feature over rpc and d1 #65 (37648460198); both job logs shownix develop --command pnpm run bootstraprunning$ sandbox -- pnpm install && sandbox -- pnpm rebuild --config.ignore-scripts=false && sandbox -- pnpm run prepare.ffd6832): the rootmutationscript is gone. No workflow called it: the release gate runsturbo run mutation --filter=<package>per package.b3eef8f, cycle 62): systemfsoftware moves from497dd37to main8a4b543(#659 and #660, the macOS drops). pnpm-release-management followed the rev systemfsoftware main pins (5eb4c5d) until cycle 77 (below). The published tarballs did not change: the sandboxedpnpm bootstrapinstalls against the unchanged lockfile.Cycles 74 to 77 (conductor rulings)
main, unpatched (2542024): prmmain537d17ccarries chore(deps): bump the other-minor-patch group across 1 directory with 5 updates #14, chore(deps): bump the other-minor-patch group across 1 directory with 5 updates #20 (the consumer store) and chore(deps): bump turbo from 2.10.12 to 2.11.4 in the turborepo-minor-patch group across 1 directory #24 (the linked-worktree change), so the starter's own copy of that change (nix/patches/sandbox-linked-worktree-git.patch) and itsapplyPatcheswiring are deleted, andsandboxandsandbox-proofscome from prm'snix/sandbox/as published. prm is a direct input atmain, locked to537d17c; systemfsoftware follows it, and prm's nixpkgs, comment-checker and importPnpmLock follow the starter's. Before chore(deps): bump turbo from 2.10.12 to 2.11.4 in the turborepo-minor-patch group across 1 directory #24 merged, the same change passed against chore(deps): bump turbo from 2.10.12 to 2.11.4 in the turborepo-minor-patch group across 1 directory #24's head (afb2f08): store, sandbox, sandbox proofs, dev shell, bootstrap and changeset check.e4614c4, starter-verify c73):withoutSandboxOnPathinsandbox-proofs/git-hooks.test.tsremoved only the first PATH directory holdingsandbox, so with a second launcher on PATH the hooks still found one and "the hooks refuse to run when the sandbox is unavailable" failed falsely. It now removes every PATH entry that providessandbox. In scratch worktrees with a second launcher first on PATH, the old helper fails that step and the new one passes; with thesandbox … --prefix stripped from.husky/commit-msgand.husky/pre-commit, the step fails.