Runnerd State Schema
Start by saying: "I am using the runnerd-state-schema skill to change or verify state schema behavior."
Goal
Keep runnerd's database schema model-driven through GORM while preserving known older state database upgrade paths.
When To Use
- Editing
internal/state/records.go. - Editing migration logic in
internal/state/db.go. - Changing state indexes, uniqueness, required columns, defaults, or relationship constraints.
- Reviewing schema changes for sqlite/Postgres/MySQL compatibility.
- Investigating failures around
AutoMigrate, old sqlite files, orNOT NULLcolumn additions.
Rules
- Prefer GORM tags and
AutoMigratefor normal schema source of truth. Existing SQLiterunner_requestsandrunner_profilestables are additive-only: create missing model columns and indexes without generic table recreation. - Use handwritten SQL only for genuine GORM gaps.
- Keep every legacy compatibility action narrow and explicit. This includes column additions, obsolete constraint removal, and destructive reset of legacy tables that cannot represent the current scope model. Document required operator reconfiguration and user reauthentication.
- GORM foreign-key creation is disabled intentionally. Preserve the foreign-keyless schema convention unless a separately tested migration changes it.
- Do not reintroduce legacy
usersmigration behavior unless the user explicitly asks. - Avoid
default:trueon business booleans when zero-value preservation matters. - Fresh database tests are not enough for required-column changes; add or preserve old-schema upgrade coverage.
Workflow
- Inspect current state records and migration helpers:
sed -n '1,260p' internal/state/records.go
sed -n '1,220p' internal/state/db.go
- Check existing tests for old-schema fixtures and migration behavior:
rg -n "Migrate|Legacy|default_available|runner_group_name|AutoMigrate" internal/state
For schema edits, update the model tags first. Change
migrateLegacySchemaColumnsonly when old databases cannot safely migrate throughAutoMigratealone. Keep existing SQLiterunner_requestsandrunner_profileschanges additive; any non-additive change requires a narrow explicit compatibility migration. Cover column additions, constraint removal, or legacy-table reset with an old-schema fixture that proves the required cleanup and asserts preservation or intentional data loss according to the compatibility contract.Add or update tests before relying on the fix. Include old sqlite upgrade coverage for required columns, unique indexes with existing rows, and relationship changes. Assert preservation of
github_installation_id, Sandbox snapshot fields, andupdated_atfor existing runner requests; preserve runner-profile rows and existing indexes while adding every missing model field.Run:
go test ./internal/state -count=1
When a production SQLite export is available, also run:
RUNNERD_SQLITE_SNAPSHOT=/path/to/runnerd-export.db \
go test ./internal/state -run TestMigrateSQLiteRunnerRequestSnapshot -count=1 -v
When a schema, migration, or audited-mutation transaction change can affect
Postgres or MySQL, run the real-dialect gate against dedicated disposable
databases whose names end in _test:
RUNNERD_CATALOG_BACKEND_TESTS=1 \
RUNNERD_POSTGRES_TEST_DSN='<dedicated postgres test DSN>' \
RUNNERD_MYSQL_TEST_DSN='<dedicated mysql test DSN>' \
go test ./internal/state -run 'Test(ApplyMutationWithAudit|FreshSchema|ScopedRunnerCatalogFreshSchema)SQLBackends' -count=1 -v
- If callers or server behavior changed, also run:
go test ./...
task test
- Sync docs if behavior or maintenance rules changed:
README.mdandREADME.zh.mdTODO.mdAGENTS.mddocs/testing.mdanddocs/zh/testing.md.agents/rules/project-architecture.md.agents/rules/testing-and-verification.md
Output
Report:
- schema files changed;
- whether old-schema compatibility was needed;
- tests added or preserved;
- verification commands and results;
- any unverified DB backend or multi-instance boundary.