Imported from yutaasakura96/suburi (
AGENTS.md). Install upstream withnpx skills add yutaasakura96/suburi. Copyright stays with the author.
Suburi
A private, single-user, turn-based voice interview simulator for practising job interviews in Japanese and English. Rubric-scored, tracked over months.
Read @CONTEXT.md before writing any spec, ticket or code. It holds the vocabulary the docs are
written in; specs and tickets must speak it exactly. docs/00-status.md says which phase the project
is in and what is next.
Where things are
@docs/00-status.md |
The memory. Phase done, phase next. Read first. |
@CONTEXT.md |
Vocabulary, invariants, open questions. |
@docs/03-technical-design.md |
Stack, architecture, the three hard problems. |
@docs/04-database-schema.md |
Every table. §6 is what the schema must not express. |
@docs/07-api-design.md |
Every endpoint, the error envelope. §6 is the endpoints that must never exist. |
@docs/11-testing-plan.md |
§1 names the five silent failures the rest guards. |
@docs/12-deployment.md |
Environments, env vars, migrations, rollback, backups. |
@docs/06-decision-log.md |
Append-only. The answer to every "why is it like this?" |
Invariants
Never violated, in any ticket. A change that breaks one is wrong even if it was asked for — raise it rather than implement it.
- No composite score is ever computed, stored or displayed. Not "overall", not "average", not "total". No column, no view, no tooltip.
- Round-end feedback renders while the user is still at the machine. A spinner that outlives the sitting is a defect. This is why answers are scored as submitted, not batched at round end.
- First attempts are never overwritten. A retry is a new row beside the first; Progress plots only the first.
- Raw transcripts are never discarded in favour of corrected text. Both persist.
- Every scored answer carries all four stamps (CV version, rubric version, generator prompt version, scoring model), and Progress draws a boundary where any of them changed.
- All data is private to one user. No sharing surface, no leaderboard, no export-to-anyone,
despite the multi-tenant
user_idcolumns. Tenancy is not permission to build sharing. - Nothing is hard-deleted. No delete-my-round feature.
- The scoring model is an exact pinned string, never an alias. Changing it is a migration with a re-score and a boundary, not an edit.
Enforced, not merely stated, in docs/04-database-schema.md §6, docs/07-api-design.md §6 and
docs/11-testing-plan.md §3. A ticket that needs one of these relaxed is a ticket that edits those
three documents first.
Branches and databases, one-to-one
main → Vercel production → Neon main. develop → a stable Vercel URL → Neon develop, seeded
synthetic. Feature branches come off develop and share its database — no Neon branch per
feature. Nothing but main ever points at Neon main.
Three consequences that are easy to miss:
developneeds a stable domain, because Google's redirect URIs are an exact-match list. Feature branches are not deployed at all (vercel.json) — they could not sign in. Verify feature work ondevelop.- Neon
developis reset from a fresh seed, never branched frommain, or the real CV lands on a branch that unfinished code writes to. - Neon
mainis migrated beforedevelopmerges into it. Migration first, deploying push second.
Full detail: docs/12-deployment.md §1 and §4.
Commands
The commands the docs commit to; package.json wraps each as an npm run script.
tsc --noEmit typecheck
eslint lint
vitest run units
vitest run --project=integration against a real pgvector/pgvector:pg18 container
playwright test Chromium, against a production build
drizzle-kit generate write the migration; read the SQL before applying
drizzle-kit migrate apply, using DATABASE_URL_UNPOOLED
Signed-in screens, locally: with next dev up and .env.local configured for local Postgres, run npm run dev:session for browser cookie steps or .playwright/dev-session.json (storageState); see 06, 2026-09-25.
Local Postgres is Docker Compose on pgvector/pgvector:pg18. Local uses the same pinned model
strings as production — testing against a cheaper model makes local behaviour unrepresentative of
the thing being measured.
Source vs generated
design/*.dc.htmlanddesign/canvas.jsonare the source.design/suburi-directions.htmlis built — re-seed from the working files, never edit it.db/migrations/is generated bydrizzle-kit generateand reviewed before it is applied. Never hand-edit an applied migration.db/schema.tsisdocs/04-database-schema.mdin code. They change together.docs/is source for everything. Code follows it; when code has to diverge, the doc changes first.
Gotchas
- Audio goes browser → S3 directly via presigned PUT. A Vercel Function caps bodies at 4.5 MB and a four-minute take exceeds it. The client never chooses the object key.
- Migrations are manual and expand-only. No down-migrations against production, never
drizzle-kit pushagainst production, never a migration that rewritescv_versions.body(every span indexes into it), neverdeleteortruncateonanswers,scoring_attempts,scores,questions,cv_versionsorcv_claims. This is what makes Vercel's instant rollback a complete rollback story. - Never logged, traced or reported: transcript text, corrected text, CV text or claim text, company notes, prompt bodies, model response bodies, salary expectations. Logs carry ids, counts, durations and error classes. Applies to Vercel logs, Sentry and the cron status page alike.
lib/ai/score.tsis a port with one implementation. Re-scoring a held-out set with a different model is the only way to detect drift, and that is impossible if scoring is inlined at its call sites.422is the "an invariant refused this" status, and itscodenames which.403is used nowhere; another user's row is404.- Screen 7, the felt-pressure rating, is load-bearing for latency. It is where the last answer's score lands. Do not make it skippable in realistic mode.
- For Next.js APIs, read the installed version's docs in
node_modules/next/dist/docs/, not memory.agentRules: falsestopsnext devwriting them into this file. - Remote Postgres URLs carry
sslmode=verify-full. Neon hands outrequire, which pg v9 downgrades to an unchecked certificate;lib/config.tsrefuses it. - Desktop only, stated by the app rather than degraded on a phone. No breakpoints; do not infer one from the 1280px canvas.
- Every new Japanese string needs a native read. Rules earned so far:
docs/05-design-system.md§6. - A round is roughly $0.40 on
gpt-5.6-sol. Cost is not the constraint at this volume.
Workflow
- Write tests before implementation where there's a natural seam.
- Review the diff against repo standards and the original request before saying it's done.
- For work spanning sessions, write a spec and tickets first.
Agent skills
Issue tracker
GitHub Issues in yutaasakura96/suburi, via the gh CLI. See docs/agents/issue-tracker.md.
Triage labels
The five canonical roles, each label string equal to its name. See docs/agents/triage-labels.md.
Domain docs
Single-context: CONTEXT.md at the root, decisions in docs/06-decision-log.md.
See docs/agents/domain.md.
Maintaining this file
Keep this file for knowledge useful to almost every future agent session in this project. Do not repeat what the codebase already shows; point to the authoritative file or command instead. Prefer rewriting or pruning existing entries over appending new ones. When updating this file, preserve this bar for all agents and keep entries concise.
