Repository navigation
feat(ci): alchemy CI and deploy shape from the starter, effect 4.1.0 (SYS-900) - #14
Merged
Merged
Conversation
…(SYS-900) CI is two workflows, check.yml and deploy.yml, both copied byte for byte from the starter's #104. deploy.yml is dormant until the repository variable ALCHEMY_DEPLOY is true. Credentials are code: apps/site/stacks/github.ts mints a scoped Cloudflare token into repository secrets, and the app stack posts the preview URL as a comment. - effect and every published @effect/* package at 4.1.0; alchemy and its cloudflare and github packages pinned to the starter's pkg.alchemy.run tarballs; @alchemy.run/frontend-frameworks dropped (unused) - turbo.json, bin/cloud, package.json scripts check-env/deploy/destroy and the commitlint comments as in the starter - the required status context is now `check` alone; the release gate, changesets, release.jsonc, the mutation shard planner and both composite actions are removed; STRATEGY.md and AGENTS.md job-shape lines updated - stacks/github.ts lowercases GITHUB_REPOSITORY before naming the stack, so Owner/Repo and owner/repo are one stack effect 4.1.0 fallout fixed here: - KeyValueStore.makeStringOnly requires `keys`; the three test stores in Atom.integration.test.ts gain it - concurrent Atom.pull requests could finish out of order and leave the atom on the shorter batch; an accumulating pull now keeps the longest batch - @effect/platform-node-shared 4.1.0 reads process.platform at module load and the browser test projects import it, so those two projects define it
The queue conditions still named the ten contexts the old workflows reported (check (format), journeys, rules, lint PR commits, changeset ...). After the two-workflow cutover none of them reports, so Mergify would never queue a PR. Both queue_conditions and merge_conditions now require check-success = check, the one context the ruleset requires; the other conditions are unchanged.
…-900) frontend publishes @systemfsoftware/effect-atom, effect-atom-react, storybook-gherkin and oxlint-config-frontend, so the release flow stays. - restore .changeset/ (README, ledger and the seven pending intents), release.jsonc and commitlint.config.ts exactly as on main - restore release.yml with one line changed: ci-workflow is now check.yml, because ci.yml is gone and the release flow dispatches that workflow on the release PR branch (check.yml has workflow_dispatch) - fold the change-intent gate into check.yml as a PR step that runs the devShell's changeset-management; a reusable workflow cannot be a step, and the release flow only needs the gate on pull requests. It skips the release PR branch, as the reusable workflow did, so changeset-check.yml stays deleted and the required context stays `check` alone - restore the Changesets row in the README toolchain table - AGENTS.md CI line now names three workflows
… (SYS-900) The 4.1.0 bump changes what the four published packages resolve against, so they get one change intent at patch, the bump the policy gives a dependency floor raise. The published peer range on effect stays ^4, and the intent says so; effect-atom's entry names the pull-atoms-keep-the-longest-batch fix. effect-4-0-5-peer-floor.md's opening line claimed Effect now resolves to 4.0.5, which this bump makes false; it now states it in the past tense. Its intent is untouched.
…(SYS-900) CONST-W3 declaration. This PR replaces the grading instrument it is graded by: the gates ruleset's required status checks go from the ten contexts of the old six-workflow tree (check (format), check (lint), check (typecheck), check (test), check (dist), check (sfs-sources), journeys, rules, lint PR commits, changeset · shared tooling) to the single context `check` of the new two-workflow tree. The authority is the owner's SYS-900 order, relayed by the lead on 2026-10-30 03:41Z, which orders the starter's shape onto this repo; the owner's review rules re-affirm it. The ruleset change on GitHub is applied by the lead, not by this branch: PUT .github/rulesets/gates.json to the installed `gates` ruleset (id 25612455) so it requires only `check`, before merging. - turbo typecheck now depends on test:types, so the owner's single command keeps running the type-level tests; lint already ran lint:tsgo and that wiring is unchanged - release.yml gains a types job running pnpm attw (the published type shape of all four packages) before the shared release job, which now needs it; attw guards the publish, not the pull request, so it is not in check - README section 5 documents updating an installed ruleset by PUT to its id and pnpm check:branch-rules, instead of only creating one
…sgo (SYS-900) - tsgo-0-63-brands.md: the field renames are breaking, so the intent is now major for the three packages it names - check.yml intent gate: a fork branch named changeset-release/main no longer skips the gate (the skip also requires the PR to come from this repository); the step still runs changeset-management check <base-sha>, which enforces the same rule the deleted changeset-check.yml did on main - turbo lint now depends on lint:tsgo, so the owner's single command runs it; the PR body previously claimed it was already wired, which was wrong
The gate's skip ANDed the repository guard onto the whole condition, so every fork PR skipped the intent gate. The skip is now the negation of the pair: a branch named changeset-release/main from this repository skips, and a fork PR - even from a branch with that name - runs the gate.
There was a problem hiding this comment.
Conductor verdict: approved at f354a83. The review (rv-frontend-14-f354a83-1793378611) is complete, and CI check is green.
- C1 (P0): the CONST-W3 declaration now names the intent-gate condition and both turbo edges, with their authorities.
- B1 (P3): the lint -> lint:tsgo edge is shown by dry-run output.
- The four P2s went to SYS-916.
- Two wording corrections go into the PR body before the merge: the edits were made at the conductor's ruling, not the driver's, and the clause 're-affirmed by the owner's review rulings on this PR' comes out.
- Merge needs the lead's PUT of the gates ruleset to require only the 'check' context (operator step 3). The check job's context name is 'check'.
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.
Summary
Ports the starter's alchemy CI and deploy shape (systemfsoftware/effect-endgame-starter-kit#104, merged as d65a7a754) onto frontend:
check.ymlanddeploy.ymlfrom the starter, credentials as code, effect 4.1.0. SYS-900 roll-out #1. Light lane: the starter PR is the plan, and review is the driver's.frontend publishes, so the release workflow stays.
.github/workflowsholdscheck.yml,deploy.ymlandrelease.yml.Packages that keep publishing
@systemfsoftware/effect-atom6.0.0@systemfsoftware/effect-atom-react2.0.0@systemfsoftware/storybook-gherkin5.0.0@systemfsoftware/oxlint-config-frontend0.1.0The 7 pending changesets are kept, plus one added for the effect 4.1.0 bump (8 in all).
.changeset/,release.jsoncandcommitlint.config.tsmatchmainexcept for the two intended changesets edits (the addedeffect-4-1-0-resolved.md, and one sentence ineffect-4-0-5-peer-floor.mdput in the past tense).release.ymldiffers frommainby one line, below.Proof
deploy.ymlis byte-identical to the starter's (SYS-900 Done-when)Empty output.
Workflows
check.yml+deploy.ymlare 138 lines (under 300).actionlint .github/workflows/*.ymlexits 0.ci.yml,release-gate.yml,changeset-check.yml,commitlint.yml,previews.ymland.github/actions/are deleted.Release wiring: the two differences from
mainand from the starterrelease.yml, one line:ci-workflow: ci.ymlis nowci-workflow: check.yml. The shared release workflow runsgh workflow run <ci-workflow>on the release PR branch so the release PR gets its CI.ci.ymlno longer exists, andcheck.ymlhasworkflow_dispatch. Without this the release PR would never get acheckstatus.check.yml, one added step (the only difference from the starter'scheck.yml): the change-intent gate, folded in fromchangeset-check.yml.Why a step and not a separate workflow:
changeset-check.ymlwas a thin caller of the shared reusable workflow, whose only job runschangeset-management check <base>. The release flow (release.yml) never calls it; it is only a pull-request gate. A reusable workflow cannot be a step, but the devShell already shipschangeset-management, so the step runs the same command, against the same base ref. The only skip is a same-repo PR fromchangeset-release/main— that PR consumes the intents it names. A fork PR runs the gate even from a branch carrying that name: the skip requires both the branch name andgithub.event.pull_request.head.repo.full_name == github.repository. The required context stayscheckalone and.mergify.ymlrequires onlycheck.commitlint.ymlstays deleted; commitlint runs in the commit-msg hook and in thecheck.ymlstep from the starter.Proof that the step's script enforces the rule (replay of the step's command,
changeset-management check <base-sha>, against a fixture checkout of this tree):packages/atom/effect-atom/package.json(version bump) with no intent naming it:@systemfsoftware/effect-atompresent:changeset gate: no publishable package changed (6 member(s)), exit 0.Run locally on this branch:
changeset-management check origin/mainexits 0 ("4 publishable package(s) changed, each named by an intent"). It would fail without the restored intents, because the effect bump touches all four packages.pnpm why effect --depth 0pnpm test:sandboxandpnpm check:sfs-sources(run on the first commit's tree; no later commit touches what they exercise)Also run locally, all exit 0:
pnpm format:check,pnpm typecheck,pnpm lint,pnpm test(unit and browser projects), and the CI commandpnpm exec turbo run format:check lint typecheck test --affected.CONST-W3 declaration
This PR replaces its own grading instrument, and two follow-up edits (made in commits pushed after the first complete review, at the conductor's ruling) edit that instrument further. Declared per CONST-W3:
check (format),check (lint),check (typecheck),check (test),check (dist),check (sfs-sources),journeys,rules,lint PR commits,changeset · shared tooling) to the single contextcheckof the new tree. The change on GitHub is applied by the lead (operator step 3 below, a PUT by ruleset id), never by this branch.check.ymlintent-gate condition (commitf354a83): the skip is now the negated pair!(github.head_ref == 'changeset-release/main' && head.repo.full_name == github.repository), so only a same-repo release PR skips the gate and every fork PR runs it.turbo.jsongate edges (commits5608b77,77bfd52):typechecknowdependsOntest:types, andlintnowdependsOnlint:tsgo, keeping those checks under the owner's single command after the old per-part CI jobs were deleted.Authorities for all three: the owner's SYS-900 order, relayed by the lead on 2026-10-30 03:41Z, ordering the starter's CI/deploy shape onto this repo; conductor rulings ruling-fe14d (B3) and ruling-fe14e (B2, receipt#5, pre#0) under SYS-900, which ordered these exact edits.
Checks that stay out of CI
attwguards the published type shape, so it runs in the release workflow's newtypesjob (pnpm attw, all four packages) before the shared release job, not incheck. Verified locally:pnpm attwexits 0.Dropped from CI on purpose, per the owner's ruling; each still runs locally through
pnpm check:ci:test:browser— needs Storybook and Playwright against a built bundle; the conformance suite inpnpm teststill covers the spec engine.check:sfs-sources— proved at install time: CI's offline install resolves every@systemfsoftware/*from the Nix-built store, which is the property it checks.test:sandbox— the sandbox-proof suite exercises the Nix sandbox itself, not the workspace; it runs on the host locally.journeys— end-to-end Playwright against a live dev server; e2e runs locally/CI-on-demand, not per-PR (OP28).Kept under the owner's single command via turbo wiring:
test:typesis adependsOnoftypecheck, andlint:tsgois adependsOnoflint. (An earlier revision of this section wrongly saidlint:tsgowas already wired; it was not, andturbo run lintskipped it. Both are wired now.)Proof —
pnpm exec turbo run typecheck lint --dry=jsonschedules both under the matching task (6 packages each; 39 tasks total):The
lint:tsgoedge itself, frompnpm exec turbo run lint --dry=jsonpiped throughjq '[.tasks[] | select(.taskId == "@systemfsoftware/effect-atom#lint" or .taskId == "@systemfsoftware/effect-atom#lint:tsgo") | {taskId, dependencies}]':[ { "taskId": "@systemfsoftware/effect-atom#lint", "dependencies": [ "//#format:check", "//#lint:suppressions", "@systemfsoftware/effect-atom#build", "@systemfsoftware/effect-atom#generate", "@systemfsoftware/effect-atom#lint:tsgo", "@systemfsoftware/oxlint-config-frontend#build" ] }, { "taskId": "@systemfsoftware/effect-atom#lint:tsgo", "dependencies": [] } ]The tsgo-0-63-brands intent is now
majorforeffect-atom,effect-atom-reactandstorybook-gherkin: renaming the payload fields of published error classes is breaking.What differs from the starter
apps/site/stacks/github.tsis the one intended difference in the stack. It lowercasesGITHUB_REPOSITORYbefore naming the stack (process.env['GITHUB_REPOSITORY']?.toLowerCase()), soOwner/Repoandowner/repodo not make two stacks (SYS-910). Every other line is the starter's.check.ymlandrelease.yml: the two release-wiring differences above.turbo.json: the starter's edits apply as written. frontend'stypecheckalready depended onbuild, so it keeps that and gains//#typecheck:node.pnpm-workspace.yaml: the starter's lines, copied verbatim foralchemy,@alchemy.run/cloudflareand@alchemy.run/github, plusblockExoticSubdeps, theminimumReleaseAgeExcludeentries and therolldownoverride. frontend also pins@effect/sql-d1, which the starter does not; it moves to 4.1.0 with the rest.@effect/tsgostays at 0.63.0.@alchemy.run/frontend-frameworksis dropped:apps/sitedeclared it and imported nothing from it..mergify.yml: both condition lists requirecheck-success = checkinstead of the ten deleted contexts;base,-draft,-label = hold, the kiro approval and#changes-requested-reviews-by = 0are unchanged. Mergify config is not in #104; this is the owner-ordered fix for this roll-out.AGENTS.md:34describes three workflows, and the README keeps its Changesets row, because frontend still releases.commitlint.config.tskeeps its changesets comments for the same reason (the starter's edit calls releases plainmaindeploys, which is false here).bin/cloudegress for api.github.com,.github/rulesets/gates.jsonandscripts/check-branch-rules.ts(and its test) requiring onlycheck, thecheck-envscript, and the STRATEGY.md mutation line. The starter's plan document is not copied.What the effect 4.1.0 bump broke, and the fix
KeyValueStore.makeStringOnlynow requireskeys. The three test stores inpackages/atom/effect-atom/tests/Atom.integration.test.tsgainkeys: Effect.sync(() => Array.from(storage.keys())).Atom.integration.test.ts› "Two requests for the next batch at the same time both arrive once the signal comes" failed deterministically with a kernel deadlock, because under 4.1.0 two concurrentAtom.pullrequests can finish out of order and leave the atom on the shorter batch.packages/atom/effect-atom/src/atom.blueprint.ts: an accumulating pull now keeps the longest batch it has delivered and drops a late, shorter one. This is a behavior change in a published package (effect-atom);.changeset/effect-4-1-0-resolved.mdis the change intent for it and for the bump, at patch.effect-atomandeffect-atom-reactfailed to import withprocess is not defined:@effect/platform-node-shared4.1.0 readsprocess.platformat module load, and the browser projects import it through@systemfsoftware/vitest. The two browser projects' vitest configs now defineprocess.platform. The proper fix is in the@systemfsoftware/vitesttarball, which this repo cannot change without bumping its flake input; the define is the in-repo stop-gap.Baseline check: on
origin/mainthe same suites pass (350 tests ineffect-atom), so both failures are from the bump.Operator steps (the lead's; no value is printed or committed)
apps/site. The operator supplies the values from Doppler:gh secret list(names only).ALCHEMY_DEPLOY=true, andSITE_DOMAINto the same hostname.gatesruleset (id25612455) by PUT, not POST:gh api repos/systemfsoftware/frontend/rulesets/25612455 -X PUT --input .github/rulesets/gates.json, so it requires onlycheck(the installed ruleset still names the ten old contexts and would hold this PR forever). Then runpnpm check:branch-rulesto confirm the live ruleset andscripts/check-branch-rules.tsagree. Do this once this PR'scheckrun is green and before merging.maindeploys in one job.