Repository navigation
feat(repo): call the site worker through effect rpc - #62
Merged
Merged
Conversation
systemfsoftware-maker
added this pull request to stack #61
October 6, 2026 23:00
systemfsoftware-maker
force-pushed
the
lake1/previews
branch
from
October 6, 2026 23:46
bcd99ef to
5139972
Compare
systemfsoftware-maker
force-pushed
the
lake1/http-api
branch
from
October 6, 2026 23:46
16525e5 to
4e8103c
Compare
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)
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)
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
…ns 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
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)
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)
…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)
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
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
… 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
…e 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
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
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)
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
…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
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
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
systemfsoftware-maker
force-pushed
the
lake1/previews
branch
from
October 7, 2026 01:01
5139972 to
d7244df
Compare
systemfsoftware-maker
force-pushed
the
lake1/http-api
branch
from
October 7, 2026 01:01
1f24957 to
907bdac
Compare
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
kiro-systemf Bot
pushed a commit
that referenced
this pull request
Oct 7, 2026
…packages from Nix (#52) * 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
* 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
Contributor
There was a problem hiding this comment.
Verified: all checks green on 4b4daf9, 0 threads, hunt clean.
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 7, 2026
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.
Lake 1, new layer 1 (Ryan ruling via Kiro: effect/rpc, not HttpApi), stacked on #51.
History: the branch first added HttpApi + OpenAPI; later commits replace that with RPC and name the procedure
health. The rebase onto main force-pushed this branch on 2026-10-07 00:41Z; since then it moves only by plain fast-forward pushes.apps/site/src/api/site-rpcs.ts: oneRpcGroupwith ahealthprocedure (success schema{ status: 'ok' }), served at/api/rpc.apps/site/src/api/site-rpc-server.ts:RpcServer.layerHttp({ protocol: 'http' })with JSON serialization;src/worker.tshandsPOST /api/rpcto it and everything else to TanStack Start.apps/site/src/api/site-rpc-client.ts: the typedRpcClientbuilt from the same group, posting every call to/api/rpcin one round trip. The home page callshealthand showsWorker health: ok(orunreachable)./api/health,/api/openapi.json, everyeffect/http-apiimport, and the HTTP-level API journey with its JSON fetch helper.tsconfig.base.json: the unstable-API opt-in listseffect/httpandeffect/rpc(waseffect/http-api).apps/site-e2e/tests/health.integration.test.ts: a real Chromium opens the home page and reads the health the page got over RPC.Gate at
d7daabb(clean worktree, sandboxed)Sabotage: pointing the client at
/api/rpcxmakes the health journey fail (Worker health: unreachable), and the strict-CSP journey fails with it.Cycle 35 (Kiro rulings on starter-verify's review)
e6b6e5e). Cause:RpcClient.layerProtocolHttpprepends itsurlto the protocol's empty request path, andHttpClientRequestjoins'/api/rpc'and''as'/api/rpc/'. The Worker serves/api/rpcexactly, so every call took a 307 to the served path. The client now builds the protocol withRpcClient.makeProtocolHttpover a client whose requests are set toSITE_RPC_PATH. Probe (Chromium againstpnpm 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.listfiring twice (measured on feat(repo): add a guestbook example feature over rpc and d1 #65; no code change). The site has no client entry of its own, so TanStack Start's default entry hydrates under<StrictMode>, and React's development build mounts each effect twice. On/guestbook: underpnpm dev, twolistcalls; under the production build (vite build, thenvite previewin workerd), one. Each is one round trip.Gate after the cycle 52 restack (Linux only)
Gate at
df02b35, clean worktree, Linux, sandboxed:CI run 37573595122 on
df02b35: 6 of 7 jobs pass (format, lint, typecheck, test, dist, journeys);check (sfs-sources)fails, the same red as the local gate above.Gate after cycle 83 (every
@systemfsoftware/*package from our flakes, #52)Gate at
a9eac20, clean worktree, Linux, sandboxed:CI run 37675962244 on
a9eac20: all 7 jobs pass,check (sfs-sources)included. Theexit 1gates above are from before the stryker packages came from the stryker-js-effect flake.