Task: Fingerprint the schema and check it at startup
Table of Contents
This page documents a task in the Refuse to start against a database built from other SQL story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
One fingerprint definition, computed by the build and by compass db recreate, compared by every service, test run and compass services start.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Refuse to start against a database built from other SQL |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-03 |
3. Acceptance
- Every service logs the expected and the actual fingerprint at startup.
- On a mismatch a service exits with a dedicated exit code and a message that names both fingerprints, the commit the database was built from, and the fix.
- A test proves the build's fingerprint and compass's fingerprint agree on the same tree.
4. Plan
(Implementation strategy. Written when work starts; key decisions
are distilled into the parent story's * Decisions at close, but the
plan itself stays — it is the historical record of what we did.)
5. Measurements
Every number this task claims about a tree, with the command that produced it
and the commit it was measured at. A number without both is not a measurement,
because a clean-up record describes a tree and trunk moves under a branch: a
consumer count, a gate's component count and a suite's size all change with no
code changing, and the reader of a bare number cannot tell which tree it came
from. Where the raw output matters, put it in an evidence/ file beside the
task and name that file in the Command column.
| What | Value | Command | Commit |
|---|---|---|---|
6. Notes
7. Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario)
that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
8. PRs
| PR | Title |
|---|---|
| #2574 | [database] Refuse to start against a database built from other SQL |
9. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
10. Result
The schema fingerprint is the first sixteen hex digits of a sha256 over the
SQL compass db recreate reads (every .sql under create/, populate/ and
instance/ and at the top of projects/ores.sql), defined once in
projects/ores.sql/schema_fingerprint.cmake and implemented again in
compass_db.schema_fingerprint; test_schema_fingerprint.py runs both on
the real tree and on a small one and requires the same answer.
compass db recreatestamps it intoores_database_info_tbl, whoseschema_versioncolumn (the CMake project version, which never tracked the schema) is replaced byschema_fingerprint. Every service user may read the table.- The build compiles it into
ores.database, regenerating the header when an SQL file changes.context_factory::make_context, which every service, the HTTP server and the test database manager call, logs the expected and the recorded fingerprint and throwsschema_mismatch_exceptionon a mismatch. Each service main prints the message and exits with the newexit_code::schema_mismatch(7), and the systemd units do not restart on it. compass services startchecks first and refuses with the same banner;compass db status,compass bearingsandcompass env statusreport in sync or out of sync instead of guessing drift from the commit date, and the environment dashboard shows both fingerprints.
Verified on 2026-10-03: build clean; database recreated; ctest 90/90; compass
tests 301 passed; dashboard tests 42 passed. With the database's fingerprint
changed to deadbeefdeadbeef, compass services start refused with both
fingerprints and the fix, and the refdata unit, restarted through systemd,
logged the FATAL banner (expected ceb50d4c0e992073, actual deadbeefdeadbeef,
the commit the database was built from, and the fix) and failed with exit
status 7 and no restarts. Recreated, the services started and logged the
check as passed.
The banner appears three times in a refusing service's log: once from the context factory, and twice from the service host's existing generic error handler, which logs every startup exception's message.