Skip to content

ADR 0010: Go Runtime With Read-Only Index and Separate State Store

ADR 0010: Go Runtime With Read-Only Index and Separate State Store

Status: Accepted for architectural direction.

Date: 2026-05-11.

Context

The scraper pipeline publishes a validated runtime index.

The backend loads that index and serves student-facing queries.

The backend also stores anonymous student planning state.

The index must not be mutated by user requests.

Student state must survive index publication and backend restarts.

The implementation should stay simple enough for local development.

Go’s standard net/http package provides a complete HTTP server and handler model: pkg.go.dev/net/http.

SQLite can open URI filenames with query parameters such as mode=ro, and supports immutable=1 when the underlying database file is guaranteed not to change: sqlite.org/uri.html.

Decision

The version 1 backend will be a Go HTTP service.

The version 1 backend will load the published SQLite index at startup.

The version 1 backend will open the index read-only.

The version 1 backend will keep student state in a separate writable SQLite database.

The version 1 backend will not write to the published index directory.

The version 1 backend will not read raw snapshot files.

The version 1 backend will not read parsed JSONL files during normal operation.

The version 1 backend will not apply parser patches during normal operation.

The version 1 backend will validate index metadata before serving.

The version 1 backend will fail startup for unsupported index schema versions.

The version 1 backend will fail startup when the release decision is missing, rejected, or not one of approved or approved_with_warnings.

The backend may serve static frontend assets from the same process.

The backend may later move to a different storage or service layout through a new ADR.

Storage Boundary

The published index directory is read-only runtime input.

The state database is writable runtime data.

The two must not share a SQLite file.

The two must not share migration history.

The index schema version and state schema version are separate.

The index can be replaced by deployment.

The state database is preserved across deployments.

The backend should use a read-only SQLite URI for the index when supported.

The backend may use immutable=1 only when deployment guarantees that the exact file will not change while the process runs.

If deployment updates a symlink to a new index path and restarts the process, immutable=1 is acceptable.

If deployment overwrites the same SQLite file in place, immutable=1 is not acceptable.

Consequences

Local development stays simple.

The backend can run without network access to Waterloo or Kuali.

The backend has a clear startup compatibility check.

State backup can be handled independently from index publication.

Index publication cannot directly overwrite student state.

The service can be deployed as one small binary with two file inputs.

SQLite write concurrency limits are acceptable for early anonymous state usage.

If usage grows, the state store can move to a server database without changing the published index format.

Alternatives Considered

Alternative 1: Backend Reads Parsed JSONL

The backend could read parsed JSONL directly.

This would avoid SQLite schema work early.

However, it would move indexing and query optimization into backend startup.

It would blur intermediate parser output with runtime contracts.

This alternative is rejected.

Alternative 2: Backend Builds Index at Startup

The backend could parse raw or intermediate data at startup.

This would ensure the runtime always has a fresh derived model.

However, startup would become slow, fragile, and dependent on scraper internals.

It would make release gates less meaningful.

This alternative is rejected.

Alternative 3: One SQLite File for Index and State

The backend could attach writable state tables to the same SQLite file as catalog facts.

This would simplify connection management.

However, it would make the published artifact mutable.

It would complicate index replacement and backups.

It would risk accidental state loss during publication.

This alternative is rejected.

Follow-Up

Choose the SQLite driver during implementation.

Define the exact state database schema.

Define backend startup validation probes.

Define deployment conventions for index replacement.

Define backup expectations for the state database.