--- title: "Working in this repository" type: convention tags: [signpost, root, convention] proposed_by: tok_bc09b340db294c709763789154d8113d model: kmem-docs-publisher extracted_at: "2026-10-08T22:25:06.046Z" --- # Working in this repository The engineering guide for somebody with access to the kmem repository: the layout, the tests, and what a reviewable change looks like. Read [`CLAUDE.md`](./CLAUDE.md) as well; its rules bind you. ## Getting set up Node 24 and pnpm 10. Nothing else: no Cloudflare account and no API key. ```sh export PATH="$(ls -d ~/.nvm/versions/node/*/bin | tail -1):/opt/homebrew/bin:$PATH" pnpm install pnpm test:core # the pure packages, in plain Node, in seconds. If this passes you are set up ``` `pnpm test` runs everything, takes about 17 minutes, and uses every core, so run it deliberately. ## Layout [`README.md`](./README.md) has the table of every directory. Two rules about it are load-bearing. **`packages/core` has zero Cloudflare imports.** The parser, the chunker, fusion, freshness decay, near-duplicate collapse and the budget packer are pure functions over plain data. That is what makes 95% coverage cheap. Biome enforces it: an import of `cloudflare:*`, `@cloudflare/*`, `drizzle-orm` or `hono` from `packages/core/src` is a lint error with an explanation attached. When core needs a capability it cannot compute, define a narrow function type in `types.ts` and pass an implementation in from `apps/worker`. See [`docs/ADR/0004-project-context.md`](./docs/ADR/0004-project-context.md). **Every mutation goes through `CommitService.commit(ctx, change)`.** The editor, MCP, a connection document, a git pull and a folder ingest are four `Change` variants on one code path. No R2 or D1 write happens anywhere else, and `apps/worker/test/invariants/single-write-path.test.ts` proves it. A `bucket.put` or a `db.insert` outside `CommitService` is the bug. A workspace package exports its TypeScript source directly, so nothing has to be built first. ## Running things `pnpm dev` starts `wrangler dev` with every binding wired locally. `pnpm lint` and `pnpm format` are Biome. `pnpm typecheck` is `tsc --build --force` across every project, tests and configs included. `pnpm build` is the composite TypeScript build and then the web bundle. `pnpm test:core` is the pure packages in Node, `pnpm test:worker` is everything binding-shaped inside workerd, `pnpm test` is every project, `pnpm coverage` adds the coverage gate, and `pnpm e2e` is Playwright. Run the project or the files you touched rather than the whole suite: ```sh npx vitest run --project web test/shell/rail.test.tsx # from apps/web npx vitest run --project worker test/auth/config.test.ts # from apps/worker ``` `pnpm db:generate`, `pnpm db:migrate:local` and `pnpm worker:types` regenerate the migration, apply it locally, and regenerate `worker-configuration.d.ts`. `pnpm docs:links`, `pnpm docs:manifest` and `pnpm licence-check` run in CI. `pnpm release` deploys; `pnpm deploy` is a pnpm builtin and never reaches this repository's script. `package.json` is the full list. ## Tests Unit and property tests live in `packages/core/test`, in plain Node with fast-check. Integration, invariant and race tests live in `apps/worker/test`, inside workerd. The eval runner is in `eval/` and the Playwright suite in `e2e/`. **A race test gets the harness. Do not hand-roll one.** `Promise.all([a(), b()])` only ever reaches the interleaving where both calls read before either wrote, and every defect worth finding lives in a later window: between one commit's transaction and its canonical write, or between a tombstone and the R2 delete that follows it. `test/support/interleave.ts` gives you `tick`, `interleave` and `sweep`. Assert the property at **every** offset in a range: a pinned offset passes for the wrong reason as soon as the code under test grows a read. **A test that writes directly to storage imports `testDb` from `test/support/writable.ts`.** `ProjectContext` carries read-only handles: `ctx.db` has no `insert` and `ctx.bucket` has no `put`. So a fixture that seeds, poisons or empties the store says so at the call site. **Never mock a Cloudflare binding.** `@cloudflare/vitest-pool-workers` gives you real D1, R2, KV, Queues and Durable Objects inside `workerd`, locally, with no account. A mocked binding produces a green test and a broken deployment. **Never delete an invariant test.** Each invariant, when violated, produces silent data loss or silent index divergence, and neither failure announces itself. When an invariant test is in your way, the code is wrong. [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) §8 names the real failure each one prevents. **The `apps/web` project runs in plain Node against `react-dom/server`,** so there is no DOM and `useEffect` never runs. Put fetch orchestration in a plain function that takes its reader and its writer as arguments, or the code is untestable. Coverage uses the Istanbul provider rather than v8, and that is not a preference: `@vitest/coverage-v8` collects through `node:inspector`, which `workerd` does not implement, so it silently drops the whole `worker` project from the report. The gates are `packages/core` at 95% and overall at 85%, and they never go down. ## Style Biome handles formatting. Run `pnpm format` before you push. Beyond formatting, four rules. No `any`, no `@ts-expect-error`, and no `biome-ignore` used to silence a real problem: a suppression comment needs an explanation of why the rule is wrong here. Sentence case in every string a person reads — "Note not found", not "Note Not Found". No emoji, anywhere. An error states the cause and then the fix, so "Refs start with kmem://" teaches the caller something and "invalid ref" does not. An empty state is an instruction, not a mood. Write documentation, and any string a model reads, to [`docs/WRITING.md`](./docs/WRITING.md). ## Changes One concern per change: a refactor and a feature in one diff is two reviews done badly. Write the title in the imperative, such as "Add freshness decay to the ranking pass". Explain **why** in the description, because the what is in the diff. Done means `pnpm typecheck` passes, the tests you touched are green, and there is no new `any`. New behaviour needs a test that would fail without it, and a bug fix needs the test that would have caught the bug. When your change alters an interface, update the document that describes it in the same change. ## Decisions and licences Anything that will be questioned in six months goes in [`docs/ADR/`](./docs/ADR) as one short file: the decision, the alternative that was rejected, and why. One decision per file, numbered in sequence, never edited afterwards. When a decision changes, write a new ADR that supersedes the old one and say so in both. No GPL, AGPL or LGPL dependency may reach a shipped artefact, direct or transitive. `pnpm licence-check` enforces that and names the offending package. `@blocknote/*` is MPL-2.0, which is file-level copyleft, so **never fork or edit a file inside `@blocknote/*`.** Extend it through its public API instead: a custom block, the schema, a slash-menu item.