- Use root/package manifests,
turbo.json, and.github/workflows/ci.ymlfor commands; prose that conflicts with executable config is stale. CLAUDE.mdis the broad architecture index. Follow its linked present-state docs, but verify command and transport details against manifests..github/copilot-instructions.mddescribes an older repository shape and is not authoritative.- This is a pnpm 11 + Turborepo workspace (
packages/*,apps/*,benchmarks/storage), requiring Node >=20.9; CI uses Node 22.
- Root commands fan out through Turbo:
pnpm build,pnpm typecheck,pnpm test, andpnpm test:integration. pnpm lintruns Biome with--write --unsafe; it modifies files. Do not introduce ESLint or Prettier.- CI static gate order is
pnpm byline:generate:check,pnpm lint,pnpm typecheck, thenpnpm knip. - Use package filters while iterating, e.g.
pnpm --filter @byline/core testorpnpm --filter @byline/webapp typecheck. - Run one DB test from its package:
pnpm vitest run --mode=integration <test-file>. pnpm buildmay print RolldownINVALID_ANNOTATIONwarnings from Lexical dependencies; check the exit status rather than treating those warnings as failures.
- Developer documents under
docs/use YAML front matter withtitle,path, and a one-sentencesummary, followed by one H1 and aCompanions:list immediately below it. - The front matter
titleand first H1 text must match exactly. Document import and processing removes the first H1 and uses the front matter title in its place. - Write for developers evaluating or learning Byline. Introduce the main concept and vocabulary first, then move into APIs, limits, compatibility boundaries, implementation references, and tests.
- Use clear, direct, complete sentences and concrete subjects such as “Byline’s admin user interface.” Avoid vague shorthand, metaphors, slogans, compressed fragments, repeated explanations, and unstated assumptions.
- Preserve technical qualifications and verify claims against current code, manifests, and tests. Link to companion documents instead of duplicating their full explanations.
- Run
pnpm docs:checkandgit diff --checkafter creating or revising documentation. The/documentcommand in.claude/commandsand.opencode/commandscontains the full workflow.
packages/coreowns framework-independent types, lifecycle/auth/query/patch logic, configuration, and the pure@byline/core/codegenemitter.packages/clientis the in-process SDK;packages/db-postgresowns storage primitives;packages/host-tanstack-startowns TanStack server-function transport.- Keep generic React primitives in
packages/ui; CMS/editor concepts belong inpackages/admin. - There is no stable public document HTTP API. Admin document operations currently use host-adapter server functions.
- App-owned schema/config lives in
apps/webapp/byline;apps/webapp/srcowns the host/public application.
apps/webapp/byline/collections/index.tsis the single server-safe collection tuple. Import schema modules only; keep React/admin presentation imports out.apps/webapp/byline/public.tsis the blessed client-safe configuration facade for public code. Do not expose admin/server config through it.- Server client getters (
getAdminBylineClient,getPublicBylineClient,getSystemBylineClient,getViewerBylineClient,isPreviewActive) come from@byline/client/server— typed via the generatedRegistermerge, server-only (browser export condition throws). Never import it into browser code. - Server bootstrap is a side effect of
apps/webapp/src/server.tsimportingbyline/server.config.ts. - Keep both
byline/admin.config.tsregistrations:_byline/route.tsxbeforeLoadprotects child loaders, while_byline/route.lazy.tsxprotects initial hydration. An eager import leaks the admin/editor graph into public bundles.
- After changing a collection, field, or block schema, run
pnpm byline:generateand commitapps/webapp/byline/generated/collection-types.ts. - App code imports collection types from
@byline/generated-types(the generated file declaration-merges into that stub package and into@byline/client'sRegister); never import the generated file by path. pnpm byline:generate:checkis read-only and fails on missing/stale output;byline/collection-types.contract.tschecks generated types exactly against inference.- Never hand-edit generated collection types or
apps/webapp/src/routeTree.gen.ts; both are excluded from Biome intentionally. - Generated collection types are canonical unpopulated read shapes. Keep operation-specific populate overlays near the query.
@byline/core/codegenis Node-only; do not re-export it from the browser-safe@byline/coreroot.
- Document storage is typed EAV. The adapter-independent field/store map is
packages/core/src/storage/field-store-map.ts. - Array/block
_idvalues are synthetic identity metadata. Do not persist or render them as schema data. - Ordinary reads/writes should use
@byline/clientor core lifecycle services. Directdb.commands.*/db.queries.*bypass auth, hooks, normalization, and lifecycle behavior and are only for seeds, migrations, tests, and internal tooling. - Content updates create immutable versions. Status, document paths, and advertised locales use dedicated non-versioned commands.
- Canonical numeric values are integer/float =
number, decimal = precision-preservingstring; stored file sizes restore asnumber. Core lifecycle normalization enforces this before hooks/storage. - Relation
hasMany,minItems, andmaxItemsaffect both inferred/generated types and collection fingerprints.
- Local unit tests use no database.
pnpm test:integrationrequires Postgres plus package-local.env.testfiles and a one-timepnpm db:init:test. - Integration safety rejects databases whose names do not end in
_test; DB bootstrap accepts only_devor_testnames. - Client and db-postgres integration suites share one database, migrate once, truncate between files, and must remain serial (
maxWorkers: 1,isolate: false, root concurrency 1). - Drizzle schema source is
packages/db-postgres/src/database/schema/index.ts; usepnpm drizzle:generateand do not formatmigrations/metamanually. - Keep
packages/cli/src/templates/migrationssynchronized with db-postgres Drizzle migrations. packages/search-postgres/migrationsis an independent numbered migration stream; do not merge it into Drizzle migrations.
- Package node tests conventionally use
*.test.node.ts; client integration tests use*.integration.test.ts; db-postgres integration tests live undersrc/**/tests/**/*.test.ts. packages/db-postgresintentionally has no unit suite: itspnpm testis a message; use its integration mode.- Check each package's
vitest.config.tsbefore adding browser tests: plain*.test.tsxis not discovered by every package's defaultpnpm test. - There is no automated browser suite. Playwright was removed; do not add it back or treat a browser command as a gate. Verify browser behaviour by hand against a running dev server and record what you observed.
- When asked to commit, use conventional messages matching history (
feat(scope): ...,fix(scope): ...,docs: ...) and keep independently verifiable phases separate.