Changelog
Written by hand, in the shape of Keep a Changelog. Versions follow Semantic Versioning; while the major is 0, a minor may break something.
Written by hand, in the shape of Keep a Changelog. Versions follow Semantic Versioning; while the major is 0, a minor may break something.
Unreleased
Five defects a real consumer found. Foldkit's own component gallery was showcased with this tool, and each of these is something it hit on the first run.
Fixed
foldcase docswrites one file per component, on a real app. A component was the set of Showcases declaring the samemessageandmodelobjects. A Foldkit app has one Model struct and one Message union for the whole app, each component a slice of them, so the gallery's 146 Showcases across 24 components wrote a single 15 KB document, titled after an arbitrary Showcase and listing every id in one paragraph. A component is now the id namespace — everything before the last/— which is whatfoldcase_run_catalogalready filters on, so the same app writes 24 files namedbutton.md,calendar.mdand the rest. Asking the app to declare a narrower per-component Schema was not the fix: that is a second description of a component, which ADR-0001 forbids. Each table is read from the first Showcase in id order that declares it, and a component that declares neither Schema now gets no file rather than a page saying so.An argument the verb does not take stops the run.
foldcase test a.showcase.ts b.showcase.tsused to run the first path, exit 0 and say nothing about the second — and the one it dropped may be the catalog that will not load, so a CI job written that way passed forever over half its request.testtakes one target,docsa target and an out-dir,mcpnone; anything more prints the argument it did not understand, then the usage banner, and exits 1. Flags still sit on either side of the target.A bin without its optional platform peer says which one to install. Both
@effect/platform-*packages are optional peers, so a consumer who installs only one used to meetERR_MODULE_NOT_FOUNDand a resolver stack trace before the CLI ran at all. Each shell now reaches for its package at runtime and, when it is missing, prints the package, the install command and the other bin, then exits 1. Any other resolution failure — a consumer's own missing module — is still reported as data.A failed load names Node's type-stripping when that is the cause. Node strips types but cannot tell a type-only import from a value import, so a showcase reaching a module that writes
import { Document } from 'foldkit/html'failed with a bareSyntaxError. The loader now names the cause and the two ways out, and the README states the rule. One change in the single loader, sotest,docsandmcpall say it.
Changed
foldcase docsreads a real Model. A union of tagged structs documents as its tags rather than asobject, and an anonymous struct as its field list rather thanobject. Both are recognised by shape, not by annotation, so an Effect beta cannot move them. An inline struct stops at one level of nesting and five fields, because the cell is one line of a table.The stack gate reads every manifest, not just the root one. The banned-package and bundler checks looked at
package.jsonalone, so a nested manifest could have declared React or Vite unseen. They now walk every manifest in the repository. This tightened the fence in the same change that opened its one hole, below.
Added
A documentation site, in this repository, at
docs/site/. It is a foldocs application — Foldkit and Effect, the same line this tool is written for — and every page of it is derived fromREADME.md,CHANGELOG.mdanddocs/adr/on each build. The generated pages are gitignored, so the prose has one home and cannot fork.mise run docs:devserves it,mise run docs:buildwritesdocs/site/dist/, andmise run docs:deployputs it on Cloudflare through alchemy. None of this is in the seven CI checks, and nothing of it reaches the npm tarball. foldocs builds with Vite, which ADR-0002 had fenced out entirely; ADR-0003 records why the site is here rather than in a second repository — a second one would either copy the prose or derive it across a pin that lags, and this tool exists to say that one definition should have many surfaces, not many copies.
0.1.0 — 2026-08-04
The first release of the core line, published under the alpha dist-tag: install it as
foldcase@alpha. It is a new codebase. The Openstory fork it grew out of — the React
shell, the CSF-3 catalog, the framework adapters, the pnpm/turbo/vite toolchain — stays on
the foldkit branch and is not a dependency of this line. What carried forward is named
in NOTICE: the play contract and the serialized-error shape.
Added
The
Showcaserecord, insrc/runner.ts. One component in one state, with aplaythat throws on a failed assertion. It is the only description of a component in the codebase, and every surface below is a projection of it (ADR-0001).foldcase test [dir-or-file]— find every*.showcase.tsunder a file or directory, run each Showcase headlessly, print a report and exit non-zero if one failed. A failing Showcase is data, not a crash, so one bad entry cannot decide the fate of the rest. Reports are EffectSchemavalues and survive a JSON round-trip.foldcase docs [dir] [out-dir]— one Markdown document per component, its Model and Message tables read out of the Schemas the Showcase declares. Nothing parses your source.foldcase mcp— the catalog as an MCP server over stdio, with six read-only tools: list the Showcases, read a Showcase's Message schema or Model schema as JSON Schema, run one Showcase, run the whole catalog or one id prefix, and re-read the catalog from disk or point it at another directory. The catalog is loaded before the transport reads stdin, so the firsttools/listanswers in full.--jsonontestanddocs. The run as one document — the report Schemas encoded, not a hand-built object — with coverage inside it when--coverageasks for coverage. stdout carries the document and nothing else; logs go to stderr; the exit codes do not move.Every report names the file it came from. An id does not say what to open, and the loader holds that fact, so it travels in the report rather than being declared on the
Showcase. The MCP run verbs report it too.foldcase test --coverage— line and function coverage of the code the plays really executed, tallied from V8 precise coverage. Bun exposes no programmatic precise coverage, so the measurement runs in a Node subprocess; the report names any file it could not measure rather than quietly scoring it zero.Two bins over one core. The whole CLI is a runtime-agnostic Effect that returns an exit code, and each runtime gets one thin shell:
foldcaseon Node andfoldcase-bunon Bun. Both load a consumer's TypeScript without a build step (ADR-0002 › Amendment 1).A published
dist/built bytsc, with no bundler. One.jsand one.d.tsper source file, so Node, Vite and Bun all resolve it as ordinary ESM. The library entry points ship theShowcasetype and the runner;./cli,./mcp,./mcp/catalogand./mcp/toolsare public subpaths. Effect is a peer dependency, and both@effect/platform-*packages are optional peers, so a consumer installs only the one their runtime needs.examples/counter— a real Foldkit app with its ownpackage.json, showcased by the built CLI under both runtimes on every push. Its committed documents are diffed against freshly generated ones, so the example cannot drift from what the tool writes.The two ADRs, as gates.
test/stack.test.tsandtest/surface-derivation.test.tsfail on a second Showcase shape, a second catalog loader, a surface that parses source, a rival lockfile or toolchain file, a banned framework, a bundler, and adist/packed stale.
Requirements
Node 22.18.0 or later, or Bun 1.3.14 or later.
effect4.0.0-beta.90 or later, as a peer dependency.
Last updated Aug 4, 2026