Skip to content

Writing Workspaces and Reference Implementations

A playable case ships two projects for each engine it supports. The seeded workspace is the project a run starts from, and the reference implementation is the case’s own conformant build of that project. The rules on this page apply to every end-to-end and full-stack case on every engine, including no engine.

The two projects are one project at two stages. A reference implementation is the seeded workspace with the build’s source, its tests, its showcase, and a committed lockfile added, so the two share a toolchain and the configuration that toolchain runs under. The manifest reference states which key names each directory; this page states what goes in them.

A workspace holds the project a model opens and nothing that belongs to a finished build. Under an engine it also carries the entry stub the engine’s module contract requires and the src/constants.ts the specification’s figures are seeded in.

FileHolds
package.jsonThe build interface, the toolchain scripts, and the project’s dependencies
tsconfig.jsonThe TypeScript options the produced code compiles under
vite.config.tsThe bundler configuration, with base: './' so the built site loads from a page-relative URL
vitest.config.tsThe reporter and coverage settings a run reads its results from
eslint.config.jsThe lint rules the lint command applies
.prettierrc.jsonThe formatting options the format command checks against
.prettierignoreWhat the format command leaves alone
.gitignoreThe build artifacts kept out of the published per-run repository
index.htmlThe document the built site loads

Hidden entries are skipped at seed time apart from an allowlist, so the two prettier files and .gitignore reach a run while a hidden lint configuration would not. ESLint’s flat eslint.config.js seeds as an ordinary file.

Engineless workspaces ship configuration only

Section titled “Engineless workspaces ship configuration only”

A case’s none workspace supplies configuration alone: a package.json, tool configuration, and an index.html, with no source code. The model owns as much of the code as possible, which is the point of the engineless configuration.

Use <link rel="icon" href="data:," /> in seeded index.html files. Omitting this results in 404 errors getting reported in the console, which validators may detect and fail on.

A model must not learn that it is being evaluated, and the workspace carries that rule with the specs. Its index.html, its package.json, and its tool configuration name the build’s own files and its entry point, so what the model opens describes the game and nothing else. See Keeping evaluation out of the seeded set.

A reference implementation is the case’s authored answer: a complete, conformant build of one variant on one engine, built with the case’s own [build] commands and never seeded into a run. A case supporting three engines carries three of them per variant, because the build a reference demonstrates differs under each runtime.

The reference is what tcab capture-baselines drives, what tcab publish-reference deploys, and the tree a case’s validators are developed against. A validator asserts the specification rather than the reference, as Validators assert the specification, not the reference covers.

A reference begins as a copy of the workspace for its engine and adds what a finished build holds: the source under src/, the build’s own tests, the showcase, and a committed package-lock.json. It keeps the seeded tool configuration, so the rules a run is held to are the rules the reference is held to.

A reference may extend that configuration where it owns something a run’s tree does not. The case’s validator projects are developed inside the reference, so its eslint.config.js carries the gates that keep a suite from reaching into the build’s src/ for a figure it should transcribe.

A reference is installed from its own directory rather than as a member of the repository’s npm workspace, and an engine-backed one resolves its engine through a relative file: dependency. The repository root must be installed and built first, as Reference implementations covers.

It carries the least its showcase validator needs

Section titled “It carries the least its showcase validator needs”

A case requiring showcase media validates only that the media exists. The reference therefore carries the description, the carousel manifest, and one small file the carousel names. The media a case presents is captured once into the variant’s showcase/<variant>/ directory, which is the copy every surface renders.

A workspace’s package.json declares the four scripts the case’s [toolchain] table names, and a reference declares the same four. The table’s sample values are the commands themselves:

[toolchain]
typecheck = "npx tsc --noEmit"
lint = "npx eslint . --max-warnings 0"
format = "npx prettier --check ."
test = "npx vitest run --coverage"

A warning is a failure: lint runs ESLint with --max-warnings 0, and format exits non-zero on any file Prettier would change. A run’s copies of these commands are recorded on the run record, with typecheck gating the run’s rating. See The TypeScript toolchain.

Prettier and ESLint are configured in both projects

Section titled “Prettier and ESLint are configured in both projects”

Every seeded workspace and every reference implementation configures both tools: a .prettierrc.json, a .prettierignore, an eslint.config.js, prettier and eslint among the project’s development dependencies, and the format and lint scripts above. A project missing any of them leaves the format or lint command with nothing to run under, and the run’s recorded figure says nothing about the code.

Dependencies track the newest usable version

Section titled “Dependencies track the newest usable version”

Every package a workspace and its reference declare is pinned to the newest version that works with the rest of the set. A case authored against current tooling measures a model against the ecosystem it was trained to write for, and a stale pin measures it against a dialect that has moved on.

Usable is what bounds it. A package’s peer range caps the packages around it, so the newest TypeScript a case can carry is the newest one the pinned typescript-eslint accepts. A package the engines also depend on is pinned to the version the engine was built against, because a build resolving two copies of a runtime library is a defect the case would be handing the model.

Every project in a case version pins a shared package at one version, so a reference installs the tool its workspace does. Revisit the set when you add a case version, and move each pin to the newest usable release then.

Both tools ignore what the build did not write

Section titled “Both tools ignore what the build did not write”

The lint and format configuration covers the .ts and .js files a run holds: the ones the workspace seeded and the ones the build wrote. Both ignore directories holding something else, so the recorded figures describe the model’s own code:

IgnoredHolds
node_modules/Installed packages
dist/, build/, out/Build output
coverage/The report files a run reads its results and coverage from
.vendor/The vendored engine and the case’s vendored packages
assets/The files a full-stack build produced as build inputs
specs/The seeded specification

Markdown is left to its own linter, so a case’s authored prose stays as the case wrote it. npm run lint:specs covers it on the authoring side.

The specs/ directory is seeded reading material rather than the project’s source, so both .prettierignore and the ignores list in eslint.config.js name it. A tool that reached the specs would report a finding against files the build never wrote and cannot act on, and the run would record a format or lint failure that says nothing about the model.

Projectformatlint
Reference implementationPassesPasses
Seeded workspacePassesPasses, apart from what the missing code causes

A reference implementation is the case’s own answer, so both commands run clean against every one of them. A workspace’s seeded files are authored the same way and are formatted, so prettier --check passes there as well.

ESLint is the one place a seeded workspace is allowed to report. An engine workspace ships the entry stub its module contract requires, and a stub whose body the model has yet to write can leave a binding unused or a branch unreachable. A finding that traces to that missing code is the workspace working as intended, and every other finding is a defect in the seeded files and is fixed. An engine workspace is likewise not expected to type-check before the model has written the code its stub is missing.

When you finish authoring or revising a case’s workspaces and references, confirm each of the following.

  • Every seeded workspace and every reference implementation ships a .prettierrc.json, a .prettierignore, and an eslint.config.js, with prettier and eslint among its development dependencies.
  • Every one of them declares the four toolchain scripts its [toolchain] table names.
  • Both .prettierignore and eslint.config.js ignore specs/, along with the installed packages, build output, report directories, vendored code, and produced assets.
  • Every package is pinned to the newest release the rest of the set accepts, and a package the engines also depend on matches the engine’s version.
  • A shared package is pinned at one version across every project in the case version, and every reference’s package-lock.json is in sync with it.
  • prettier --check . and eslint . --max-warnings 0 both pass in every reference implementation.
  • prettier --check . passes in every seeded workspace.
  • Every finding eslint . --max-warnings 0 reports in a seeded workspace traces to code the model is expected to write.
  • Each engine’s reference starts from that engine’s workspace and keeps the seeded tool configuration, extending it only for what a run’s tree does not hold.
  • Every reference commits a package-lock.json and installs from its own directory with the repository root built first.
  • Each engineless workspace contains configuration only.
  • Every seeded index.html declares an inert icon.
  • The workspace’s index.html, package.json, and tool configuration name the build’s own files alone.
  • A reference required to carry showcase media carries the least its existence validator needs.