Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Do not add new `TODO.md`, `NOTES.md`, or extra plan files. Extra design docs are
- Follow `docs/DESIGN.md` (taste v1 adapted for this console)
- Keep iterating Qoder login, usage, and account routing. Borrow scheduling ideas from [sub2api](https://github.com/Wei-Shaw/sub2api), not its commercial gateway
- Multi-account = one worker process per Qoder HOME; do not share WASM context
- Schema changes go in a new numbered SQLite migration. Never rewrite a shipped file

## Don't

Expand All @@ -36,3 +37,4 @@ Do not add new `TODO.md`, `NOTES.md`, or extra plan files. Extra design docs are
- Copy sub2api billing, Redis slots, multi-tenant API keys, or session-hash-for-profit
- Add a new component library, purple AI chrome, centered generic login cards, or emoji in UI copy
- Start Cursor / Anthropic until the current Qoder milestone in `docs/PLAN.md` is done. Qoder CN is that milestone (`provider=qoder` + `region=cn`); do not spawn a full `qoderclicn` per request
- Change the SQL bytes of a shipped SQLite migration in `internal/accounts/migrations.go`. Tabs, spaces, and comments inside the raw string count. `gofmt` on the Go around it is fine; indenting the SQL is not. Existing databases panic on boot with `checksum mismatch`
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ from `docs/DEVELOPMENT.md`; do not create version tags by hand.
- Preserve the proven WASM encode and HTTP/SSE request path
- Use HeroUI for console components
- Add tests for account, routing, API, or translation behavior changes
- Treat shipped SQLite migration SQL as immutable. Add a new numbered file instead of editing an applied one; pin the canonical checksum in tests. See `docs/DESIGN.md`
- Do not commit `.env`, `.qoder`, auth blobs, tokens, raw captures, host IPs, or
`docs/PRIVATE_DEPLOYMENT.md`

Expand Down
25 changes: 24 additions & 1 deletion docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,29 @@ on state change (`power2.out`, 180–220ms); they do not loop.
Distinguish this account-level quota from the request-level `insufficient_quota` error kind:
that error means a per-request token/model limit, not a zero account balance.

## SQLite migrations

Go embeds numbered SQL in `internal/accounts/migrations.go`. On open, each filename is recorded in `schema_migrations` with the SHA-256 of the **raw Go string**, including the leading newline and every tab or space. Applied files are never re-run. A checksum mismatch panics in `api.New` and the process exits, so Docker `restart: unless-stopped` loops and `/health` never comes up.

This is the rule that `v0.2.19` broke: it retabbed `006_request_log_provider.sql` while adding `007`. Databases that had already applied 006 on `v0.2.18` refused to boot. The host updater rolled us1 back because health never reported `v0.2.19`.

Hard constraints:

- Never edit a shipped migration's SQL bytes. Whitespace, comments, quote style, and statement order all change the checksum.
- `gofmt` / indent of the surrounding Go is fine. Indenting the text inside the raw string is not.
- New columns, indexes, or tables go in the next numbered file (`008_…`). Do not append to an applied file.
- Do not `UPDATE schema_migrations` on a live database to make a rewritten file pass. That hides the mismatch and can leave hosts on different schemas.
- Do not delete, rename, or reorder applied filenames.

If a bad rewrite already shipped (as with 006 in `v0.2.19`):

1. Restore the original SQL bytes so new databases record the first checksum.
2. Put the mistaken checksum on that file's `legacyChecksums` so databases that recorded the rewrite can still open.
3. Pin both checksums in `internal/accounts` tests. `TestRequestLogProviderMigrationKeepsV0218Bytes` is the pattern.
4. Ship that as a new patch. Do not ask operators to patch SQLite by hand.

`legacyChecksums` is only for a checksum that already landed in published images. It is not a way to keep editing old SQL.

## Console IA

Keep the menu short. Login is a gate, not a nav item.
Expand Down Expand Up @@ -484,7 +507,7 @@ Useful ideas borrowed from sub2api:
- system operations are serialized and expose durable status;
- release checks use a fixed trusted repository and stable releases only;
- host-only privileged work crosses a narrow authenticated boundary;
- applied database migrations are immutable and checksum-verified.
- applied database migrations are immutable and checksum-verified. Editing the SQL bytes of a shipped file, including whitespace, panics existing databases on boot; add a new numbered file instead. `legacyChecksums` is only for a checksum that already shipped by mistake.
- release artifacts are built per OS/architecture and verified by checksum.

Ideas intentionally not copied:
Expand Down
3 changes: 2 additions & 1 deletion docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Development

last-updated: 2026-08-28
last-updated: 2026-08-29

Repository rules, layering constraints, and validation checklists live in
[CONTRIBUTING.md](../CONTRIBUTING.md). This page covers the local build loop
Expand Down Expand Up @@ -53,6 +53,7 @@ Do not create or push version tags by hand. The tag, GitHub Release, updater ass
1. `main` is the commit you want to ship. The workflow waits for CI on that exact SHA.
2. `CHANGELOG.md` `## Unreleased` has matching `### English` and `### 中文` bullets for every user-facing change on `main` since the last published tag. The workflow copies that section into the GitHub Release and the console System page. `validate` allows an empty Unreleased section; `extract-for-release` fails if Unreleased is empty and the new version heading does not already exist.
3. Do not freeze Unreleased yourself before the run. The workflow reads Unreleased first; freeze only after the tag is public.
4. Do not ship a change that edits the SQL bytes of an already-applied SQLite migration. Existing databases panic on boot with `checksum mismatch`, and the host updater rolls back. Schema changes go in a new numbered file; details are in `docs/DESIGN.md`.

```bash
gh workflow run release.yml --ref main
Expand Down
Loading