How the data flows¶
Every reader and every writer of the board goes through the same layers, whatever keeps the board. This page shows them, what each one owns, and the path a write and a read take. Storage gives the git datastore's format; ADR 0002 gives why the board is cut at the pack.
Watch it move: the packages as the binary links
them, and a write, a list, a change event and a forge sync played through
them, for each datastore and forge. The page is drawn from the code by
tools/flowgen; its stories are flows.json, and each step
is checked against the code (make flow, and go test while it is
stale).
The layers¶
v0.12 (ADR 0021, the design): each
package imports only the ones below it, as layers_test.go's table says.
flowchart TB
subgraph clients["Who reads and writes"]
web["Web app<br/>(web/, Vue)"]
cli["git board …<br/>(cmd/git-board, surface/cli)"]
tui["TUI<br/>(surface/tui)"]
end
http["surface/http<br/>the HTTP API, change events"]
app["app<br/>opens a board, wires the domains:<br/>the one API every surface calls"]
tools["tools/…<br/>ship (the release runner), ghproject,<br/>branches, code, insights, uploads"]
subgraph domains["domain: the kinds and their rules"]
issues["domain/issues<br/>fields, filters, the inbox, numbers"]
people["domain/people<br/>persons, accounts, me"]
releases["domain/releases · releases/proof<br/>records, versions, overlays,<br/>signed check results"]
cycle["domain/cycle · cycle/work<br/>an issue's step; the Work page"]
end
conf["conf<br/>.git-board.toml, table by table"]
sync["sync<br/>the engine: records, journal, lock;<br/>mappers per kind"]
forge["forge · forge/github · forge/gitlab"]
view["view<br/>rows, terms, texts, heads"]
kernel["kernel · kernel/pack<br/>entities, operations, packs:<br/>encode, hash, order; Do"]
ds{{"store.Datastore<br/>Heads · Load · Add · Folder"}}
subgraph stores["Where the board is kept: board.datastore"]
git[("git<br/>refs/board/*<br/>store/git")]
pg[("PostgreSQL<br/>git_board.*<br/>store/pg")]
md[("Markdown files<br/><dir>/.board/*.jsonl<br/>store/md")]
copy[("the forges alone<br/>.git/board/forge-copy.git<br/>store/forgecopy")]
end
forges(["GitHub · GitLab"])
folder[/"the machine's folder<br/>.git/board: v2/view.db, sync.lock,<br/>caches"/]
code[("the code repository<br/>branches, diffs, tags,<br/>check notes")]
web --> http
http --> app
cli --> app
tui --> app
app --> tools
app --> domains
app --> view
app --> ds
tools --> domains
tools --> code
cycle --> issues
cycle --> releases
releases --> people
issues --> people
domains --> conf
domains --> kernel
domains --> sync
sync --> forge
sync --> kernel
forge <--> forges
view --> kernel
kernel --> ds
ds --> git
ds --> pg
ds --> md
ds --> copy
app -. "renders NNN-slug.md, reads hand edits (domain/issues/mdfiles)" .-> md
view -. "sqlite, for git, Markdown and forge only" .-> folder
view -. "git_board_view_v1, on PostgreSQL" .-> pg
sync -. "sync.lock, journal" .-> folder
releases -. "check notes, refs/notes/ci/*" .-> code
| Layer | Owns | Knows nothing of |
|---|---|---|
surface/cli, surface/http, surface/tui |
the commands, the API and its change events, the terminal UI: adapters over app |
how the board is stored, the rules of a kind |
app |
a board opened: its datastore, its view, its config, the domains wired; the one API every surface calls, its errors and exit codes; what only git does (pull, push, start over), refused elsewhere | the format of a pack |
tools/… |
apps over app: the release runner (ship), the GitHub Project bridge (ghproject), branches, the code repository, insights, uploads |
the board's storage |
domain/issues, domain/people |
the issue and person kinds: their state folded from their operations, fields (ADR 0007), filters, the inbox, numbers |
where the operations are kept |
domain/releases, releases/proof |
a release's record and its rules (one per commit), versions and tags, overlays; check results as signed lines in git notes, and the gate | registries, config repos and the forge, which tools/ship drives |
domain/cycle, cycle/work |
an issue's step from intent to verified, never stored; the Work page's rows | git and the forges, read in app; the experimental switch (ADR 0018) |
conf |
.git-board.toml: tables each domain registers, read once, written table by table, its schema version |
what a table means |
sync |
the sync engine, generic over kinds: plan, pull, push; its records (links, last seen, posted) are operations in sync entities; the journal and the lock |
git: a forge sync runs the same on every datastore |
forge, forge/github, forge/gitlab |
the forges' adapters, and their conformance suite (forge/forgetest) |
the board |
view |
lists, search and lookups: the rows, terms and texts of every kind, brought up to the datastore's heads before each query (ADR 0009) | the truth: it is never the source |
kernel, kernel/pack |
entities and operations; packs: their bytes, their id (the git blob id of the bytes), their order (clock, then id); Do, a write with its compare-and-swap |
where the packs are kept |
store |
the Datastore interface, its capabilities and errors: ErrNotFound, ErrMoved |
the packs' content |
store/git, store/pg, store/md, store/forgecopy |
the bytes, a version per entity, the compare-and-swap | operations |
A write¶
sequenceDiagram
participant P as Web app
participant S as surface/http
participant A as app
participant I as domain/issues
participant K as kernel
participant D as Datastore
P->>S: POST /api/issues/{id}/comments
S->>A: Comment
A->>I: Comment(id, body)
I->>K: Do(id, decide)
K->>D: Load(issue, id) → packs, version v
K->>I: decide(the issue at v) → operations
K->>K: pack {clock: highest + 1, ops}
K->>D: Add(issue, id, pack, prev = v)
alt the version moved meanwhile
D-->>K: ErrMoved
K->>D: Load again, decide again, Add on the new version (5 tries)
end
D-->>K: new version
K-->>A: the issue; the view's row replaced (write-through)
A-->>S: the issue
S-->>P: 200, then an event to every page
opt the forges alone
A->>A: the issue queued for the forge, sent at once, again later while it fails
end
- One write is one pack, added only if the entity is still at the version it was read at. Two writers never lose a write: the second reads again, decides again, and writes on top.
- A pack's id is computed by the kernel, not by the datastore, so an
operation's id (
<pack id>:<index>) is the same in every datastore, and so is every reference to it: an edit's target, a sync record, a marker on a forge.
A read¶
- A list or a search asks the view. The view first reads the
datastore's
Heads(one version per entity), compares them with the heads it holds, loads again only the entities whose version moved, and then answers from its tables (the read path). - One issue is its row and its state at the current version; with no state stored, its packs are read from the datastore, sorted and folded, and the state kept.
- The pages' change events compare the datastore's heads every second: a write from another clone, another machine or the command line reaches every open page.
A forge sync¶
Sync reads and writes operations through the same datastore as everything
else: the issues it pulls into, and the sync entities that keep its
links and last seen values. It doesn't know where they are kept, so a
board in PostgreSQL syncs with GitHub exactly as one in git does. Its lock
and journal are in the machine's folder: two runs on one machine never
overlap. On PostgreSQL a run also holds the board's lock in the database
(an advisory lock, store.Locker), so two machines on one board
never sync at once; the server lets it go when a killed run's session
ends.
What lives where¶
| What | git | PostgreSQL | Markdown files | the forges alone |
|---|---|---|---|---|
| issues, people, sync and release records | refs/board/<kind>/<id> |
git_board.packs, git_board.entities |
<dir>/.board/<kind>/<id>.jsonl, one pack a line, and <dir>/NNN-slug.md rendered |
a git copy in .git/board/forge-copy.git, filled by sync |
| project numbers | refs/board/seq |
git_board.counter |
the forges' own numbers only | the forges' own numbers only |
| the view | .git/board/v2/view.db (sqlite) |
git_board_view_v1.*, shared |
.git/board/v2/view.db |
.git/board/v2/view.db |
| who you write as | git config board.me |
git config board.me |
git config board.me |
git config board.me |
| sync lock and journal, caches | .git/board/ |
.git/board/, and the board's sync lock in the database |
.git/board/ |
.git/board/ |
| the files' lock (read-in, write, render) | <dir>/.board/board.lock |
|||
| branches, diffs, tags, check notes | the code repository | the code repository | the code repository | the code repository |
.git-board.toml |
the code checkout | the code checkout | the code checkout | the code checkout |