Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6662a62
docs(agents): drop no-ops from AGENTS.md and start CODING_STANDARDS.md.
m-murphy Oct 6, 2026
3efb494
docs: move test and database rules into CODING_STANDARDS.md.
m-murphy Oct 6, 2026
bf1cbda
docs(agents): turn AGENTS.md into a router and fold docs/agents into it.
m-murphy Oct 6, 2026
d4a8e9a
docs: hold the ADR-derived rules back for their own review.
m-murphy Oct 6, 2026
dd642a4
docs: cite ADRs as "ADR 0006" in the files this PR touches.
m-murphy Oct 6, 2026
bfa8d40
docs: write the coding standards as second-person imperatives.
m-murphy Oct 6, 2026
bf9746b
docs: allow read-only tools that must not migrate to skip openOperati…
m-murphy Oct 6, 2026
d2797d8
docs: say when the hand-rolled session helpers are acceptable.
m-murphy Oct 6, 2026
08e081c
docs: put the reason for withWriteTransaction back next to the rule.
m-murphy Oct 6, 2026
85cb068
docs: restore the test rules the README rewrite dropped.
m-murphy Oct 6, 2026
acb19ec
docs: keep the whole-*Api override rule in CODING_STANDARDS.md only.
m-murphy Oct 6, 2026
8e96e89
docs: describe the pre-commit hook and ci:verify in README.md only.
m-murphy Oct 6, 2026
be5a547
docs: name the five triage labels in AGENTS.md.
m-murphy Oct 6, 2026
35f4f1d
docs: rename the AGENTS.md "Checks" heading so it isn't confused with…
m-murphy Oct 6, 2026
c0f3e55
docs: say what CODING_STANDARDS.md holds in the AGENTS.md pointer.
m-murphy Oct 6, 2026
063c188
docs: give AGENTS.md an H1 heading, as CODING_STANDARDS.md has.
m-murphy Oct 6, 2026
af31451
docs: make the root README the one place for coverage targets.
m-murphy Oct 6, 2026
d40df05
docs: restore the CURRENT_SCHEMA_VERSION bullet in the db README.
m-murphy Oct 6, 2026
1a1ea59
Merge branch 'refactor/specimen-registration' into docs/agents-md-res…
m-murphy Oct 7, 2026
f51b352
docs(standards): say which code may run DDL, and scope the web test r…
m-murphy Oct 30, 2026
c23c9f3
docs(agents): give the API and contract test commands, setup:hooks, a…
m-murphy Oct 30, 2026
2d7ce70
docs(tests): make the README test snippets compile and follow the que…
m-murphy Oct 30, 2026
c355602
Merge branch 'refactor/specimen-registration' into docs/agents-md-res…
m-murphy Oct 30, 2026
46d22de
Merge branch 'refactor/specimen-registration' into docs/agents-md-res…
m-murphy Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 17 additions & 46 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,23 @@
## Agent skills

### Issue tracker

GitHub Issues on eppicenter/sampledb3 (via `gh` CLI). See `docs/agents/issue-tracker.md`.

### Triage labels

Canonical triage roles mapped to GitHub labels; see `docs/agents/triage-labels.md`.

### Domain docs

`CONTEXT.md` (glossary) and `docs/adr/` (decisions) at repo root. See `docs/agents/domain.md`.

### Package docs
# Agent guide

- Writing an API test (test database, factories, authenticated routes): `packages/api/src/__tests__/README.md`
- Writing a web test (render helper, API mocks, React Query hooks): `packages/web/src/__tests__/README.md`
- Changing the schema, a migration, or a transaction: `packages/api/src/db/README.md`
- Finding the code behind a UI label: `packages/web/src/components/Sidebar.tsx` (label → path) → `packages/web/src/App.tsx` (path → page) → the page's hooks and `lib/` → `packages/api/src/routes` → `packages/api/src/lib`

### Tests

| Package | Command | Runner |
|---------|---------|--------|
| `@sampledb/web` | `cd packages/web && bun run test -- <files>` | **Vitest** (jsdom, `vi.mock`) |
| `@sampledb/api` | `cd packages/api && bun test src/...` | **Bun** (needs `bun:sqlite`) |
| repo root | `bun run test` | both, via package scripts |

**Run web tests with `bun run test` (Vitest).** It runs `vitest run`, which exits on its own, so piping to `tail` or `grep` is safe. Bun's own runner (`bun test`) hangs on React component tests.

Example (completes in ~1s):

```bash
cd packages/web && bun run test -- src/components/__tests__/BulkImportFlow.test.tsx
```

### Pre-commit hooks and CI
## Agent skills

The `Checks` workflow runs `bun run ci:verify` on every PR: typecheck, lint, build, unit tests, and the docs stale-claims check. Lint is clean, so any lint error is yours.
- **Issue tracker**: GitHub Issues on `eppicenter/sampledb3`. "Publish to the issue tracker" is `gh issue create`; "fetch the relevant ticket" is `gh issue view <number> --comments`.
- **Triage labels**: each of the five triage roles is a GitHub label with the role's name: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, and `wontfix`.
- **Domain docs**: `CONTEXT.md` is the glossary, `docs/adr/` holds the decisions, and `packages/docs/` is the user guide. Before you name or change a domain concept, read its `CONTEXT.md` entry and the ADRs that touch the area.

Install repo hooks once per clone:
## Running tests and checks

```bash
bun run setup:hooks
```
- Web tests: `cd packages/web && bun run test -- <files>`. Bun's own runner (`bun test`) hangs on React component tests; Vitest exits on its own, so piping it to `tail` or `grep` is safe.
- API tests: `cd packages/api && NODE_ENV=test bun test src/...`. The package's `test` script sets `NODE_ENV=test`, and `src/db/legacy-baseline.ts` branches on it.
- Contract tests: `cd packages/contract && bun run test`. `bun run test` at the repo root runs the contract, API, and web suites.
- The `Checks` workflow lints every PR, so a lint error comes from your change.
- Run `bun run setup:hooks` once per clone to install the pre-commit hook (it typechecks and lints staged files, and refuses a source file that also has unstaged edits), and `bun run ci:verify` before pushing. The end of Build in `README.md` says what each checks.

The pre-commit hook runs `typecheck` on `@sampledb/contract`, `@sampledb/api`, and/or `@sampledb/web` when staged files touch those packages (or Docker/build config), and lints the staged API/web source files. It refuses a source file with unstaged edits, since eslint would lint the working-tree copy. To mirror CI fully before pushing:
## Where to look

```bash
bun run ci:verify
```
- Rules for naming domain concepts, contradicting an ADR, failing tests, database access, calling the API from the web, and writing API and web tests: `CODING_STANDARDS.md`. The `code-review` skill checks changes against it.
- Writing an API test: `packages/api/src/__tests__/README.md`.
- Writing a web test: `packages/web/src/__tests__/README.md`.
- Changing the schema, adding a migration, or writing in a transaction: `packages/api/src/db/README.md`.
- Tracing a UI label to code: `packages/web/src/components/Sidebar.tsx` maps the label to a path and `packages/web/src/App.tsx` maps the path to a page. From there follow the page's hooks and `lib/api/<domain>` into `packages/api/src/routes` and `packages/api/src/lib`.
43 changes: 43 additions & 0 deletions CODING_STANDARDS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Coding standards

## Domain language

- Name a domain concept with the term `CONTEXT.md` defines for it, in code, test names, issue titles, and proposals. Treat a term the glossary lists under _Avoid_ as a synonym, and replace it with the defined term.
- When a concept has no glossary entry, say so in the issue, PR, or proposal before you introduce a term for it.
- When a change contradicts an ADR in `docs/adr/`, name the ADR and give the reason to reopen it, in the PR or proposal. For example: _Contradicts ADR 0006 (unified container inbound write), but worth reopening because…_

## Tests

- Assert the expected behaviour. When a test fails, fix the code under test and keep the assertion as strict as before. You may land a new test that fails, if it states the expected behaviour.

## API

Paths are relative to `packages/api/`.

### Database

- Run a write of several statements that `await`s inside `withWriteTransaction` from `src/db/write-transaction.ts`. Drizzle's bun-sqlite `db.transaction(async () => …)` commits at the callback's first `await` instead of awaiting the returned Promise, so the rest of the write runs outside the transaction. ESLint rejects any callback passed to `.transaction(...)` that returns a Promise.
- Change the schema with a numbered migration, following the recipe in `src/db/README.md`, and refresh `initial_schema.sql` to match. Keep DDL out of the connection code (`src/db/open.ts`, `src/db/client.ts`), routes, and `src/lib/`. The schema-evolution modules in `src/db/` (`migration-runner.ts`, `migration-preflight.ts`, `legacy-baseline.ts`, `apply-initial-schema.ts`) run DDL as part of evolution, and tests may create tables inline to build a legacy or partial database, as `src/db/__tests__/client.test.ts` and `src/lib/subjects/__tests__/subject-read.test.ts` do.
- Open a database through `openOperationalDatabase`, which runs schema evolution. Open the file directly only when the code must not migrate it, and say why in a comment. For example, `src/scripts/check-integrity.ts` opens a backup with `new Database(path, { readonly: true })` so that checking it leaves it unchanged, and `src/db/__tests__/initial-schema-snapshot.test.ts` applies `initial_schema.sql` alone so that pending migrations can't hide a stale snapshot.

### API tests

- Give each test its own database from `setupTestDatabase()`, and close it with `cleanupTestDatabase()` in `afterEach`.
- Seed rows with the factories in `src/__tests__/helpers/factories.ts`.
- Test a route behind session auth through `setupAuthenticatedRouteTest`. Log in by hand with `loginAndGetCookie` only when the login flow is under test, as in the auth route and middleware tests.
- For a CRUD route with `checkInUse`, write a delete test for both the in-use and the free case.
- Test a route's error cases as well as its success: validation errors, 404s, and duplicates.

## Web

Paths are relative to `packages/web/`.

- Call the API through its domain module, `src/lib/api/<domain>`. Its methods resolve to the response body, so read `res.study`. Reference-data `list()` helpers are the exception: their body is `{ data, meta? }` (ADR 0002).

### Web tests

- Mock the domain module the code imports with `createMockedDomainModule`. Make each override replace a whole `*Api` object and resolve to the same body the real module returns.
- In a new or changed test, render with `await render(...)` from `src/__tests__/helpers/render`, query elements by role or label (`getByRole`, `getByLabelText`), and drive interactions with `@testing-library/user-event`. Older tests still render with `@testing-library/react`'s `render`, click with `fireEvent`, and query by text; convert a test when you change it. Tests of a provider itself, such as `src/contexts/__tests__/ThemeContext.test.tsx`, render without the helper because it would wrap them in a second copy of the provider.
- Wait for async results with `findBy*` queries or `waitFor`.
- Test a component's loading and error states, not only its loaded state.
- In a mutation hook's test, assert which queries it invalidates.
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ bun run build
bun run start
```

Before pushing, run `bun run ci:verify` (typecheck + build) to match the Docker CI step. Install git pre-commit hooks with `bun run setup:hooks` to catch TypeScript errors on commit.
Before pushing, run `bun run ci:verify`, the same checks the `Checks` workflow runs on every PR. Install the pre-commit hook with `bun run setup:hooks`; it typechecks the packages your staged files touch and lints the staged API and web files.

## Project Structure

Expand All @@ -72,8 +72,15 @@ Put local overrides in **`.env.local`** (gitignored) at the repo root. Bun’s a

- **Unit tests (API + web)**: `bun run test`
- **End-to-end (Playwright)**: `bun run test:e2e`
- **API**: `bun --filter @sampledb/api test` (Bun). Coverage: `bun --filter @sampledb/api test:coverage` (report in `packages/api/coverage/`). Target 90% lines; see `packages/api/src/__tests__/README.md`.
- **Web**: `bun --filter @sampledb/web test` (Vitest). Coverage: `bun --filter @sampledb/web test:coverage`. Coverage excludes `src/lib/api/**` and `src/**/*.css` so the 90% target applies to testable code. Thresholds in `packages/web/vitest.config.ts` can be raised in steps toward 90%.
- **API**: `bun --filter @sampledb/api test` (Bun). Coverage: `bun --filter @sampledb/api test:coverage` (report in `packages/api/coverage/`). Test helpers: `packages/api/src/__tests__/README.md`.
- **Web**: `bun --filter @sampledb/web test` (Vitest). Coverage: `bun --filter @sampledb/web test:coverage`. Test helpers: `packages/web/src/__tests__/README.md`.

### Coverage

This is the one place coverage targets are written down; the config files point here. Both packages target 90% line coverage.

- **API**: no threshold is enforced. `coverageThreshold` in `packages/api/bunfig.toml` is commented out; set it to make `test:coverage` fail below that line coverage. The thresholds in `packages/api/vitest.config.ts` apply only to Vitest coverage runs, and Vitest cannot run the tests that touch `bun:sqlite`.
- **Web**: the thresholds in `packages/web/vitest.config.ts` are a floor that fails `test:coverage`. Raise them in steps toward 90%. Coverage excludes `src/lib/api/**` and `src/**/*.css`, so the target applies to testable code.

## Database Configuration

Expand Down
17 changes: 0 additions & 17 deletions docs/agents/domain.md

This file was deleted.

22 changes: 0 additions & 22 deletions docs/agents/issue-tracker.md

This file was deleted.

15 changes: 0 additions & 15 deletions docs/agents/triage-labels.md

This file was deleted.

4 changes: 2 additions & 2 deletions packages/api/bunfig.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[test]
# Only run tests from src directory, exclude dist (compiled files)
# Skip compiled output. The test scripts pass src.
exclude = ["dist/**", "node_modules/**"]
# Coverage: use Bun's built-in so all tests (including route tests using bun:sqlite) run
coverage = true
coverageReporter = ["text", "lcov"]
coverageDir = "coverage"
# Optional: set to enforce minimum (e.g. 50). Current baseline ~55% lines; raise toward 60% as tests are added.
# Optional: set to fail the run below this line coverage. Targets: Coverage in the root README.md.
# coverageThreshold = 50
coveragePathIgnorePatterns = ["node_modules", "src/__tests__", "src/**/*.d.ts", "src/**/*.config.*", "dist"]
Loading
Loading