Multi-project mode
Fairway's project boundary equals the git repo boundary. Each project has its own .fairway/state.db. Multi-project aggregation is opt-in via a registry — there is no single shared database.
Why per-project DBs
- The DB moves with the repo. Clone, archive, or
rm -rfand state stays consistent with the working tree. - Schema migrations affect one project at a time.
- A project's data is portable — copy
.fairway/to share state, or commit it for fully reproducible team setups.
Every row in every table carries a project_id column (see schema.md) — in v1 SQLite each DB has a single value, set from [fairway] project_name at DB open. This makes the schema portable to a future shared backend (Postgres) without a row-rewrite migration.
The registry
~/.fairway/registry.toml lists known projects:
[[project]]
name = "gpuaas"
path = "/path/to/GPUasService"
# db_path optional; defaults to <path>/.fairway/state.db
[[project]]
name = "fairway"
path = "/path/to/fairway"
The registry is the only file outside of a project's own directory that fairway writes. Plain TOML; editing by hand is fine.
Commands
fairway register [--name <n>]— add the current project. Name defaults to[fairway] project_nameor the repo basename. Idempotent.fairway unregister [<name>]— remove. Defaults to the current project.fairway projects— list registered projects with last-activity timestamp.
Project name
Each project carries a project_name, used to label rows in the multi-project dashboard:
[fairway]
project_name = "gpuaas" # default: basename of the repo root
Names must be unique across the registry. fairway register refuses duplicates and suggests a suffix.
Dashboard modes
fairway dashboard runs single-project mode against the current repo's DB. This is the common case.
fairway dashboard --multi runs multi-project mode:
- Opens the registry.
ATTACH DATABASEeach registered DB into a single SQLite session.- Read views
UNION ALLacross attached DBs. Each row already carriesproject_id— no need to project it from config. - The lanes strip groups lanes under project headers.
- The activity feed prefixes entries with
[project]. - Filter chips include a project filter.
Single-project and multi-project queries share the same Go view layer; the multi-project path swaps the data source.
Why per-DB instead of one shared local DB
project_id is in the schema, so a single shared local SQLite would be technically possible. We choose per-DB anyway because:
- Schema migrations stay per-project; one project's upgrade does not block another's.
- The DB moves with the repo (clone, archive,
rm -rfare all consistent). - Backup is per-project; you can hand a colleague one
.fairway/directory without leaking other projects. - Bug blast radius is bounded — a corrupt DB takes down one project, not all of them.
The shared-DB story is for a server-hosted (Postgres) future, not a shared local SQLite file.
Path to a shared server
When (if) fairway grows a Postgres adapter:
- One Postgres DB holds many
project_idvalues. - Existing local SQLite DBs are imported with
fairway export | fairway import --to postgres://...— rows carry theirproject_idintact. - The CLI's
[fairway] backend = "postgres://..."setting switches the store driver. No schema change, no row rewrite.
Performance
A registry of 20 projects with 1000 tasks each (~20MB of attached state, 20k task rows) is well within multi-project dashboard budgets. Beyond that, multi-project mode degrades to slower first-paint but stays usable. Single-project mode is unaffected by registry size.
Limits
- Multi-project mode is read-only. Mutations always target one project's DB via the CLI inside that project's directory.
- The registry does not enforce that all attached DBs share a schema version; mismatched versions are flagged in the dashboard header so the user can run
fairway db migrateper project.