Repository toolchain
September 20, 2026 · View on GitHub
Use Vite+ for repository commands. Bun is the package manager and script runtime.
Framework packages live in packages/*; runnable examples live in examples/*.
Versions {#source-of-truth-versions}
The root package.json owns shared dependency versions.
Workspace manifests use catalog: for those dependencies and workspace:* for internal packages.
The travel planner is a release consumer: its Effect Agent dependencies and compatible
effect-cf version pin exact npm versions and advance together after publication.
bunfig.toml disables implicit workspace linking, so only explicit workspace: dependencies
use local source; registry dependencies, including transitive ones, stay on published packages.
Commit the Bun lockfile; CI installs with --frozen-lockfile.
| Tool | Repository version |
|---|---|
| Bun | 1.4.2 |
| Vite+ | 0.3.2 |
| Alchemy and its Cloudflare runtime | 2.0.0-beta.77 with upstream compatibility patches |
| Effect and its provider/platform/SQL/Atom/test packages | 4.0.0-rc.116 |
effect-cf | 0.44.1 |
| TypeScript | 7.0.2 |
@effect/tsgo | 0.45.0 |
| Node.js | 22.18+ or 24.11+ |
Public packages require effect@^4.0.0-rc.116 as a peer. The exact catalog pin supplies the
development version. Raise the peer minimum when code needs a newer API.
Private examples declare Effect as a regular dependency. Adapters depend on the platform and
SQL implementations they use.
platform-cloudflare requires effect-cf@^0.44.1 and effect@^4.0.0-rc.116 as host peers
and uses the exact catalog versions for development. Supply Effect SQL packages compatible with
rc.116 for effect-cf. Consumers provide the shared runtime.
Root overrides keep Effect, its Node/browser platforms, shared SQL adapters, and test packages
on the catalog versions, including dependencies of published consumers.
The root also installs Alchemy's optional @effect/platform-bun peer at the shared Effect
version so its Bun entry points remain available.
Vite+ supplies Vitest except in the two Cloudflare packages, whose Workers pool requires a
direct catalog-pinned Vitest dependency and a Vite task. Run those tasks through vp run.
Operational harnesses under tooling/* also use Vite tasks for Miniflare tests.
VitePress uses its own Vite dependency. Keep the root Vite+ core alias required by Vite+; do not add a global Vite override.
Alchemy and its Cloudflare runtime advance together. Their published beta.77 packages and
Distilled rc.9 clients still use Effect APIs renamed in rc.113. The version-specific Bun
patches backport Alchemy's compatibility fix
and Distilled's matching fix, including the
published JavaScript entry points. The root declares mime because the Cloudflare runtime
imports it without declaring the dependency. Keep these corrections until a published upgrade
includes them; verify that upgrade with a frozen install and vp run check:deploy.
Current workspace
See the package map for public packages and capabilities.
| Directory | Purpose |
|---|---|
packages/* | Framework and private PR-review integration packages |
examples/travel-planner | Canonical Cloudflare application, deployed with Alchemy |
tooling/runtime-benchmark | Deterministic runtime comparisons |
tooling/context-continuity-eval | Release continuity gates and deployed performance |
tooling/cloudflare-memory | Thread-to-Memory latency and heap measurements |
tooling/browser-run-worker-proof | Opt-in hosted Browser Run verification |
tooling/pr-review-eval | Opt-in live review evaluation |
tooling/semantic-memory-eval | Semantic-memory quality evaluation |
action/ | PR-review Action contract and ignored build output |
Framework code stays in packages/*. The canonical app and operational harnesses are leaf workspaces.
Provider integrations come from upstream Effect AI Layers, including @effect/ai-typesafe.
ai-decision owns thread model selection and consumes Effect's native Decision and DecisionModel.
effect-agent <- storage adapters
effect-agent <- workflow
effect-agent + selected adapters <- platform packages
effect-agent <- sandbox-local
effect-agent <- testing
effect-agent <- pr-review
Within packages/effect-agent/src, dependencies point inward:
core <- engine <- capabilities <- durable and core <- sandbox <- capabilities.
Public module paths address these implementations directly; source directories are not separate
packages. Keep core and sandbox contracts platform-neutral. The export check enforces these
internal boundaries as well as package imports.
Arrows point toward dependencies. An inward package must not import an outward one.
The testing package consumes ai-decision as a devDependency for model integration fixtures.
Shared compiler settings live in tsconfig.base.json.
Commands
Run vp help or vp <command> --help for options.
| Command | Use |
|---|---|
vp install | Install dependencies and hooks |
vp check | Format, lint, and type checks |
vp fmt / vp fmt --check | Format files / check formatting |
vp lint / vp lint --fix | Lint / apply fixes |
vp test | Root test runner |
vp run check | All static checks, package types, scripts, and purity |
vp run test | All workspace suites, including Cloudflare |
vp run build | Package, docs, and Action builds |
vp run ready | Full handoff gate: check, test, build |
vp run docs:dev | Docs development server |
vp run docs:build | Build docs and check links |
vp run docs:preview | Preview built docs |
vp run docs:deploy --yes | Deploy docs to the existing production stack |
vp run check:deploy | Load both deployment CLIs without deploying |
vp run -F @effect-agent/example-travel-planner dev | Cloudflare travel planner |
vp env doctor | Diagnose toolchain setup |
Use vp run <task> for other scripts. Do not use bun run, npm run, pnpm run,
yarn run, or invoke the wrapped compiler, formatter, linter, or test runner directly.
Include vp env doctor output when asking for toolchain help.
Local tests run with at most four workspace tasks at once and without dependency ordering. CI gives the travel planner, context-continuity evaluation, runtime benchmark, Node platform, testing package, and both Cloudflare packages separate runners. The remaining workspace suites share one runner and run sequentially. Each suite keeps its own Vitest/workerd worker limits; running more heavy suites on one runner can starve ownership-lease renewals in crash tests. Builds follow dependency order. Process-kill, soak, and adapter contract suites are part of the ordinary test command.
Vite Task caches successful results against their inputs. Vitest's mutable result cache is
disabled so it does not invalidate task caching. CI transfers node_modules/.vite/task-cache.
Direct Vitest tasks, Node platform and Cloudflare-memory tests, and travel-planner tests and builds
exclude generated Vite files and dependency directory listings, but track imported dependency files
and the lockfile. Wrangler dry-run builds exclude their temporary .wrangler bundles; the Action
build excludes its generated bundle from inputs and restores action/dist/index.mjs on a cache hit.
Failed tasks are never cached. Vite Task fingerprints whole files, including package manifests: a version-only change
can invalidate tests even when their source is unchanged. Keep manifests tracked because exports,
module type, and dependency declarations also affect execution.
Use vp run -v test for cache decisions, vp run --last-details for the previous run,
or vp run --no-cache test to rerun every suite.
Storage certification reports
The repository runs the same certification against memory, SQLite, and Cloudflare adapters. Set
EFFECT_AGENT_CERTIFICATION_OUT in a Node certification test to write a local schema-encoded
report. Set PRINT_REPORT in the Cloudflare test file to print its workerd report.
Documentation examples
Lead implementation guides with a short, concrete code example after at most one introductory sentence. Explain behavior after the code it describes. Keep complete setup available and type-check examples through their runnable entry points; move advanced contracts and recovery details into linked reference pages instead of front-loading them in walkthroughs.
The homepage imports docs/snippets/travel-planner/*.ts.
Edit those files to change its examples. A twoslash fence enables type hovers and compiler
validation during vp run docs:build. Relative imports resolve from that snippet directory.
Twoslash uses the pinned typescript-twoslash JavaScript compiler API; repository checks use
TypeScript 7. Production builds reuse Twoslash compiler and filesystem caches within the process;
the dev server disables both so imported snippet edits remain visible.
Keep compiler validation enabled. Do not suppress errors with noErrors or
noErrorValidation.
Keep yield* inside a generator, such as Effect.gen(function* () { ... }).
Outside a generator, the formatter parses * as multiplication and inserts spaces.
Link previews
The docs config adds Open Graph and Twitter metadata to the built HTML. Each page uses its
resolved title and description, a canonical URL on https://effect-agent.com, and its own
1200 × 630 PNG. docs/.vitepress/social-images.ts renders the page title, description, and URL
using the installed IBM Plex fonts and docs/public/mark.svg. The build writes images under
docs/.vitepress/dist/social/; no browser, remote font request, or manual screenshot is needed.
Preview crawlers read the metadata and images without JavaScript.
Edit the renderer to change the artwork, or the page's frontmatter to change its title and
description. The build fails if text cannot fit at the minimum font size. Section fragments such
as #workflow are not sent to the server, so they share their parent page's preview. A section
needs its own page URL to have a distinct preview.
Link to guide/workflows for the Effect Workflows preview. The old platforms/node#workflow
anchor points readers to that guide.
Run vp run docs:build and inspect the generated HTML for the homepage, a guide, and a directory
index such as platforms/index.html. Open their generated PNGs to check the layout. Image URLs
must be absolute, and canonical URLs must match the site's clean routes. Existing messages may
retain a cached preview after a deployment.
Releasing to npm
All eleven public packages share one Changesets fixed group and publish to beta
as X.Y.Z-beta.N. Keep the group in .changeset/config.json aligned with public workspaces.
The travel planner is a private application with no package version. It does not receive
changesets, version bumps, changelogs, package tags, or npm releases. Private-package versioning
and tagging remain disabled in the Changesets configuration.
Changesets updates internal dependency ranges only when they use workspace:. Exact registry
pins, including the travel planner's published Effect Agent dependencies, stay unchanged during
versioning. Upgrade those consumers and their import paths separately after publication; otherwise the version task's
install would request packages that have not been published yet.
The project is in prerelease mode. Leaving it requires an explicit release decision and
vp run changeset pre exit.
Use vp run changeset to describe a consumer-visible change.
On each main push, .github/workflows/release.yml maintains the version PR without waiting for CI.
After that PR merges and its exact main revision passes CI, the workflow publishes through npm
trusted publishing with provenance. PR updates and publication use separate queues.
Publication first runs release:checked-publish, which checks npm for unpublished public versions.
If all versions already exist, it skips publication and the paid evaluations. Registry failures
stop the attempt before inference. A pending release requires fresh, uncached continuity and
hosted checkout checks on the exact clean candidate checkout. Missing credentials, incomplete runs,
model failures, or failed assertions stop publication.
For the context continuity evaluation, configure
OPENAI_API_KEY as a repository secret and optionally CONTEXT_EVAL_MODEL
as a repository variable; the workflow explicitly selects gpt-6-astra by default. Each suite has a
conservative $10 spending limit. Nightly and release jobs select one existing explicit-rollover
profile; manual dispatch may select one bounded SQLite pressure/restart profile. Cloudflare and
full-capacity coverage require separate explicit preparation. PR checks are deterministic and
never call a model.
Each attempt preserves its own evidence artifact, including failures. This gate proves the
documented continuity scenario; it does not certify large-history startup or Cloudflare host
performance.
The hosted checkout gate runs 12 automated
cases with gpt-5.6-luna, four concurrently, before npm publication. Checkout or cleanup failure
blocks the release. Configure the CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN and narrow
BROWSER_RENDERING_API_TOKEN repository secrets and the CLOUDFLARE_WORKERS_SUBDOMAIN variable;
it reuses OPENAI_API_KEY. Its report is retained for 30 days and recorded cleanup is retried after
failure or cancellation. Manual hosted checkout also runs on demand for a selected revision.
Neither changeset additions nor ordinary PRs trigger this paid matrix, and CI never waits for human
verification.
The release PR always runs candidate builds and the required ready gate. It can reuse
proven ordinary source checks through the release metadata proof.
The workflow uses the existing Effect Agent GitHub App to create and update it, so those pushes trigger
PR CI. Keep the App's contents and pull-request write permissions enabled and configure
the EFFECT_AGENT_APP_ID and EFFECT_AGENT_APP_PRIVATE_KEY repository secrets.
The checkout disables persisted credentials so Changesets uses the App token.
After merge, Changesets handles registry version checks, publishing, package tags, and GitHub
releases. CI transfers the exact validated package build to publication; manual releases build locally.
The publisher temporarily prepares npm-ready manifests:
source exports point at built files, workspace:* dependencies use the current workspace
versions, and catalog: dependencies use the root catalog. All source manifests are restored
on success, failure, or interruption. npm publishes through OIDC with provenance; each package
must list release.yml in danieljvdm/effect-agent as its trusted publisher.
Changesets defaults packages with no stable release to latest. The adapter temporarily marks
the prerelease state as exiting while running changeset publish --tag beta, then restores it.
It never runs versioning in that state, so versions and the ongoing beta release train stay intact.
The release workflow does not require strict up-to-date branch rules or post a separate
ready check. Changesets refreshes the version PR as changes land on main.
For an authenticated manual release:
- Run
vp run changeset. - Run
vp run changeset:version, thenvp install. - Run
vp run ready. - Supply the continuity and checkout environment
for the exact clean candidate. Use two checkout repetitions and
CHECKOUT_HUMAN=false. - Run
vp run release:publish --dry-run, thenEFFECT_AGENT_LIVE=1 vp run --no-cache release:checked-publish. Add--otp <code>if npm requests it. - Run
git push --follow-tagsto push the tags created by Changesets.
Use release:publish so npm receives built exports and resolved dependencies.
Its dry run builds and inspects packages without publishing or creating tags.
All public packages use the MIT license.
Script runners
Package scripts use Bun through vp run.
Scripts that import @effect-agent/storage-sqlite continue to use
node --experimental-transform-types to exercise the Node host runtime.
Strip-only execution cannot handle the framework's runtime namespaces.
This includes admin:durable and the Node crash/soak workers.
Post-install setup
vp install runs vp config --no-agent --hooks-dir .vite-hooks through prepare.
Apply the compiler patch separately with vp run patch:tsgo.
The pinned upstream command selects a replacement for the installed TypeScript version and
fails if that replacement is unavailable. Dependency versions live in the root catalog.
CI suppresses lifecycle scripts, then explicitly patches the compiler in jobs that check, test,
or build TypeScript. Read installed Effect sources in node_modules/effect.
preferTypedSchemaDecoder is enabled as a warning in tsconfig.base.json and fails
typechecks when an unknown-input decoder discards a known encoded type. Use the typed
decoder when the input matches the Schema's Encoded type; keep unknown-input decoders
at untyped boundaries. Both forms perform the same runtime validation.
The config also enforces direct Effect combinators for selective error recovery, collection traversal, conditional validation, Option conversion, single-service provision, timeout recovery, and Exit runners. Prefer these built-in operations when they preserve the existing error channels, concurrency, interruption, and resource lifetime. Other style suggestions remain advisory; synchronous Schema codecs are not prohibited globally.
To upgrade Effect:
- Update all Effect-family catalog entries together.
- Run
vp install. - Run
vp run ready, including canonical application compilation. - Run applicable opt-in provider checks with host credentials.
- Commit the catalog and lockfile together.
Contributor agent skills
.agents/skills contains repo-owned Dev Kit skills, tracked by individual
.dev-kit-origin.json receipts and linked from .claude/skills.
- Check updates with
bunx @danieljvdm/dev-kit@latest skills status. - Update an unmodified skill with
bunx @danieljvdm/dev-kit@latest skills update <name>. - Add an approved skill with
bunx @danieljvdm/dev-kit@latest skills add <name>.
The CLI leaves locally edited skills for an agent merge. Use the dev-kit skill before
changing these files. Contributor skills are tooling; framework packages must not import them.
Adding a package
Get owner agreement before adding a new framework concern.
- Add
packages/<name>/package.json,src/index.ts, andtsconfig.json. - Match sibling manifests: MIT license, public publishing, and source-first exports.
- Add the package to the Changesets fixed group and configure its npm trusted publisher.
- Use
catalog:andworkspace:*dependencies with the required inward direction. - Extend
tsconfig.base.jsonand add a roottsconfig.jsonproject reference. - Add applicable
check,test, andbuildtasks and update the guides. - Run
vp run ready.
vp run build dispatches vp pack. A package-level Vite config overrides zero-config pack
defaults, so declare dts and sourcemap there when needed.
Exports and entry points
Follow the pinned Effect package's module layout. Package roots and public groups use namespace
exports such as export * as Agent from "./Agent.ts"; explicit named conveniences are also
allowed, as Effect does for pipe and flow. Public namespaces and source filenames use
PascalCase; public import subpaths use kebab-case. import { Agent } from "effect-agent" and
import * as Agent from "effect-agent/agent" select the same module.
- Lead documentation examples with named namespace imports from package roots, such as
import { NodeDurableHost } from "@effect-agent/platform-node". Use kebab-case subpaths for individual declarations such as services, schemas, or types; direct module and lazy-loading examples; and specialized adapters or runtime-specific helpers. In particular, Node-safe Cloudflare helpers must use their dedicated subpaths rather than the Workers package root. - Keep implementations in named modules. A public module exposes every declaration it exports; move sibling-only helpers into private files. A small, explicit public selector may expose supported declarations from a private implementation without exposing its helpers.
- Import sibling implementations relatively and directly. Do not route them through the package's
own public entry points or index barrels. Re-export-only files under
internal/add no public boundary and should be removed. - Import other framework packages through their declared public entry points. Direct modules, roots, and namespace groups are allowed; declare the dependency and respect package direction.
- Keep one implementation owner for each API. Deliberate public modules may forward another
package's bindings, including with
export *, as Effect's platform packages do. A namespace group such as/testingis also a valid public boundary. Review additions for consumer value; avoid accumulating overlapping aliases without a reason. - Keep test-only groups and modules under
/testingor/testing/module, excluded from production entry points. Optional browser adapters and fixtures may remain direct-only imports. - Keep
package.jsonexports explicit, with matching pack entries. Exports may address nested source modules directly, such as./agentmapping to./src/core/Agent.ts; no forwarding file is needed. Pack preserves paths relative tosrc, and the publisher maps them todist. Do not publish wildcard,internal, orindexsubpaths.
See the package map for API ownership and import changes.
Oxlint enforces export-only root and group indexes, prevents self-barrel imports, and rejects
re-export-only internal files during vp lint, vp check, and the pre-commit hook. Indexes may
contain namespace and explicit named re-exports, but not bare wildcard exports or implementation
code. Public forwarding modules need no umbrella-specific exception or file allowlist.
The export check in vp run check verifies manifest paths, exact filename casing, namespace
targets, pack entries, declared workspace dependencies, and the inward-only source layers within
effect-agent. The purity check uses declared testing
targets as well as known test-module paths to prevent production entry points from reaching
test-only code. Choosing supported APIs and useful public groups still requires review.
Audited packages declare "sideEffects": [], as Effect does, so bundlers can discard unused
modules. This describes import-time behavior, not whether exported operations perform effects.
Keep I/O and registration inside Effects and Layers. Recheck the declaration when adding an
import-time initializer or upgrading a dependency with startup behavior. The Cloudflare platform
package remains unmarked because its optional Puppeteer adapters patch globals on import.
Bundle size comparisons
Pull requests run the Bundle size workflow against the exact base and head commits.
Like Effect's bundle check,
it bundles small consumer fixtures against built packages. Each checkout installs its own
lockfile. The comparison uses the PR's esbuild version and the same fixture source for both sides.
Disposable comparison manifests alias historical PascalCase subpaths to their kebab-case names;
staged modules also expose the former Ephemeral assembly as InMemory. The published packages
retain only their canonical exports. Renamed modules remain comparable.
The fixtures in scripts/bundle cover agent construction, importing the runtime's run function,
the in-memory assembly, and loading the runtime on demand, through both root and direct module imports. The
analyzer stages copies of dist and uses the release publisher's manifest conversion. It does
not bundle workspace source or externalize Effect. It uses minified ESM, a browser target,
es2022, production mode, and gzip level 9 per emitted chunk.
Initial counts the entry and every statically reachable shared chunk. Deferred counts
the remaining output, and total counts each chunk once. These are byte measurements, not
startup timing or a promise about another bundler. Newly introduced modules show n/a
for the base. Build failures fail the report; size increases are informational.
Use direct module paths at lazy-loading boundaries. With the measured esbuild configuration,
statically importing Agent from the root and dynamically importing AgentRuntime from the same
root pulls the runtime into the initial chunk. Direct effect-agent/agent and
effect-agent/agent-runtime imports preserve a deferred runtime chunk; shared Effect dependencies
still count toward the initial load.
The comparison also bundles and executes runtime-smoke.ts against the PR's staged packages.
This Node check verifies root/direct bindings and a deterministic agent run after minification
and tree shaking. It is excluded from browser byte totals, and failure prevents a success report.
To reproduce locally, install dependencies and run vp run -F './packages/*' build in each
checkout, then run from the PR checkout:
vp run bundle:compare -- --base-dir /path/to/base-checkout
.bundle-report/report.md contains the table; report.json contains exact raw/gzip bytes,
revisions, Effect versions, and chunk membership. Each fixture also gets emitted .mjs files,
an esbuild meta.json, and modules.txt with module contributions. The metafile can be opened
in esbuild's analyzer. CI attaches these as bundle-stats
and bundle-analysis artifacts and updates one PR comment through a separate trusted workflow.
The comment workflow becomes active after it is merged into the default branch.
Runtime performance comparisons
Pull requests run the Runtime performance workflow against the exact base and head commits.
The scripted benchmark runs identical
fixture bytes against production builds and each revision's own lockfile on the same Node runtime.
Run vp run perf:compare --help for local reproduction. Timing tasks bypass the task cache; keep
other builds, tests, and benchmarks idle during measurement.
The PR profile covers small runs and streams, fixed-size responses with increasing fragmentation, growing prompts, parallel tools and repeated rounds, file-backed SQLite history, checkpoint recovery, and settled Submission ledgers. Fresh durable startup includes reopening the host and admission; recovery of an existing Run is a separate case. First-model timing ends in the actual provider callback. A separate subprocess measurement includes startup and fixture imports. Retain raw samples, failures, environment metadata, fixture and artifact hashes, and exact SHAs. The trusted comment workflow becomes active after it reaches the default branch.
Latency reports are informational until repeated CI runs establish variance and useful absolute and relative thresholds. Deterministic call, concurrency, ownership, tracing, and history-work budgets remain correctness gates. The fresh-submission history guard permits two linear scans plus fixed work for histories without compaction. Compacted and checkpoint-seeded views keep their existing validation passes. Checkpoint recovery bounds do not establish constant-time fresh admission. Fairness, lock contention, optional memory/MCP publication, and large settled-ledger indexing require their own controlled evidence before changing those paths.
The separate manual Cloudflare evaluation
deploys real model-and-tool workloads through the public HTTP/DO host. Use vp run perf:cloudflare --dry-run to inspect the deployment plan without credentials. The workflow accepts exact commits
and runs only on explicit dispatch with its dedicated environment credentials. Its bounded fresh,
warm, and recovery cases assert consumed tool results, validated output, canonical settlement,
same-thread continuity, and cleanup. Preserve failure artifacts and retry any recorded cleanup
before closing an attempt. Offline workerd tests validate the harness, not deployed latency.
Compare matched workloads and clock domains. Local scripted timings do not measure provider latency, Cloudflare CPU billing, or provider cache effects. A live report identifies what it can observe and must accompany claims about the exact candidate and configuration it measured.
CI and hooks
PR CI runs static checks, tests, and builds, then reports the required ready result.
Static checks include check:deploy, which invokes both deployment entry points with --help
and imports both stack files using a temporary Alchemy profile. This catches missing dependencies
and incompatible Effect APIs without credentials or infrastructure changes; it does not verify
Cloudflare credentials or remote deployment. Deployment tasks disable Vite Task caching so every
invocation runs and receives its deployment environment.
Cloudflare storage, Cloudflare platform, Node platform, and testing have dedicated test runners.
The remaining-workspace job includes every other package and runs one package task at a time.
The generated Changesets PR uses the release metadata proof below, with ordinary CI as its fallback.
Explicit @effect-agent review comments still request review.
PR Review uses pull_request_target and runs only trusted default-branch code.
It publishes the shared Effect Agent review check on the inspected PR head using the workflow
token's checks: write permission. Automatic and manual reviews use the same check name;
manual retries show progress in the PR checks panel. Published findings and incomplete coverage
fail that check, while setup, execution, and check publication failures also fail the workflow job.
See the Action check configuration for consumer setup.
Fork reviews wait for approval before checkout, token creation, or model execution.
Open the PR Review run from the PR's checks, select Review deployments, select
pr-review-forks, then Approve and deploy. GitHub uses deployment wording for
this approval gate, but the job does not deploy anything or create deployment records.
Approving an ordinary fork workflow does not grant it repository secrets.
Before enabling this workflow, configure Settings → Environments → pr-review-forks
with repository maintainers as required reviewers. The current reviewer is danieljvdm;
update this list when maintainers change. Allow self-review so a maintainer can approve
their own fork PR. Keep this environment and its required-reviewer rule in place;
a missing environment is automatically created without protection by GitHub.
The separate pr-review environment has no approval requirement and is used for
same-repository PRs and authorized review comments. Both environments use
deployment: false to avoid adding review runs to deployment history.
Each fork PR update requires approval. The Action's expected-head check skips an approved run if its PR head has since changed. Comment-triggered reviews retain their existing maintainer authorization and do not require a second approval. Never check out, install dependencies from, or execute the PR head in this secret-bearing workflow; the reviewer reads untrusted source through GitHub's API instead.
Each test-matrix job has its own task-cache key. The three suites split from the workspace job
also fall back to its earlier cache, so splitting the matrix does not discard reusable results.
Static checks, tests, and builds save successful task results even when another task fails.
Ordinary main pushes run static checks, tests, and builds to populate shared caches
and validate Action releases. Proven version merges reuse their source checks and exact PR build. The ready fan-in runs only on PRs. Main runs are not cancelled
by newer pushes. GitHub scopes PR caches to each PR's merge ref, so another PR cannot reuse them.
A new release PR can restore the latest main results only after those jobs finish saving their
caches. Waiting for those caches alone does not prevent version fields from invalidating whole-file
task fingerprints. Ordinary task results are reused only when task inputs match.
Release metadata CI {#release-metadata-ci}
scripts/release-ci.ts reuses static checks and all eight test-matrix gates from ordinary
CI on the exact source base, both on the version PR and after its merge. The verifier and its
dependencies run from that base, with read-only contents, Actions and pull-request permissions.
Candidate files are read as Git objects; the proof does not execute candidate code.
main push -> ordinary source CI
-> version PR: proof or ordinary CI + build + package checks
successful source + PR CI -> version merge: proof + restore PR build + package checks
successful main CI -> publication: restore main build + package checks + live gate -> npm
The supported delta is deliberately narrow: every public package in the single fixed group
advances by one beta number, changelogs prepend the corresponding entry without rewriting history,
and bun.lock changes only the matching workspace version fields. Manifests and the lockfile
must otherwise remain byte-identical. Prerelease state must record all existing changeset IDs and
initialize each newly included public package at its base version; mode, tag and existing initial
versions stay fixed. Changeset files themselves, dependencies, exports, scripts, module type,
source, tests, configuration and workflow policy cannot change. Stable releases, other prerelease
transitions and unfamiliar layouts run ordinary CI.
The proof checks the current PR head and base, current main, the synthetic merge commit's exact
two parents, and equality of the merge and head trees. It queries the latest base push run of
the identified CI workflow, requiring a completed successful run and successful ordinary
command steps for static checks, every test suite and the build. Evidence is bound to its run
ID and attempt, then rechecked along with the PR revisions. A skipped command, missing job,
partial API page, changing attempt, policy change, API failure or 45-second proof timeout selects ordinary
CI. Setup failures also fall back. The summary records the immutable revisions and evidence run.
Only main-push source validation can authorize reuse; fast-path PR results never authorize another
fast path. No manifest or lockfile is globally excluded from Vite Task inputs.
The version PR still receives a frozen install, all package/example/docs/Action builds, formatting,
export and purity checks, and ci:release-packages. Main repeats the frozen install, formatting,
export, purity and package checks after restoring that exact build. Package inspection temporarily prepares the
same npm-ready manifests used by publication and checks npm pack --dry-run --ignore-scripts
for the actual version and every exported JavaScript and declaration file. Source manifests and
prerelease state are restored. A failed retained check fails ready. This path neither publishes
nor calls paid models; the separate paid gates in release:checked-publish remain intact.
Release PR generation runs on push alongside main CI and skips superseded main revisions.
If ordinary source CI is still running when the PR proof checks it, the PR runs ordinary CI.
Publication alone uses workflow_run: completed after successful main CI and skips commits with
pending changesets. Because Changesets uses github.sha internally, publication requires that
SHA to equal the completed run's head; a newer main revision waits for its own CI.
An already stale PR or changed merge tree also falls back to ordinary CI. Evidence applies only to the
recorded merge checkout, just as ordinary PR checks do; it does not validate later base movement
or replace branch protection's up-to-date requirements.
A version merge must belong to the single merged Changesets PR, have its recorded base as the
previous main revision, and have exactly the PR head's tree. Squash and two-parent merges are
supported; changed bases, merge resolutions and other topologies select ordinary CI. The proof
rechecks ordinary source CI and the latest successful version-PR CI attempt, including its actual
build, package checks and ready command. It never chains source approval through another fast path.
Each build uploads one release-build-<run>-<attempt> artifact containing package dist files and
the Action bundle, bound to its Git tree, commit and parents. Consumers check the authenticated
GitHub artifact identity and SHA-256 of the complete archive before decoding it, then check the
recorded revisions and file hashes. Only generated build paths can be restored. Main records the
restored build under its own identity after package validation. Documentation and example outputs
are not transferred, but their successful exact-tree build remains required evidence.
Publication accepts only the successful exact-main CI run selected by workflow_run, rechecks
its attempt and current main before preparation and after the live gate, and inspects the restored
npm packages again. Main rebuilds if the PR artifact cannot be restored. Publication fails on
missing, expired, corrupt or mismatched artifacts; they never authorize publication. Artifact
retention is seven days; rerun main CI to replace expired evidence.
The fresh paid gate, Changesets registry checks, npm OIDC provenance and Action tag publication
remain required. Local controlled proofs establish correctness; hosted release latency needs a
matched version-merge run.
The pre-commit hook runs vp check --fix on staged JavaScript and TypeScript.
CI runs the full gate, including package type checks and the Action build.
Action bundles use the catalog-pinned esbuild. vp run action:build writes ignored
output to action/dist/index.mjs and checks its Node.js syntax. The root build task
also builds the Action, so every PR validates bundling without committing generated
JavaScript. There is no bundle freshness check or input-hash manifest.
On successful main runs, CI publishes the exact build artifact in a child commit
of the validated source. It creates action-<source-commit-sha> and advances
action-v1 in one atomic push, without changing main. Only the publication job
has repository write permission; it installs no dependencies and runs no project
code. Superseded source commits are skipped, and a Git lease prevents competing
publishers from overwriting a newer channel. Failed publication preserves the last
release and can be retried by rerunning the failed CI job.
Consumers use danieljvdm/effect-agent/action@action-v1 or pin the distribution
commit SHA printed in the CI summary. New source commits and @main no longer
contain a runnable bundle; older SHA pins still work. Before the initial cutover, seed action-v1 with the
last validated source commit that still contains the bundle, then migrate existing
workflows. The first successful main CI run publishes the new distribution commits.
These tags are independent of npm package releases.