A rebuildable local mirror of the reconciled backlog — an index over gitea's
durable truth, never the source of truth (D4). This lands the core seam:
- `cache/cache-v0.ts`: the 5-table schema (issues/labels/milestones/comments/
issue_events), a `CacheDriver` injected-IO interface (exec/run/get/all), and
pure row<->domain mappers. `upsertIssue` mirrors one issue (+ its milestone),
upsert-by-number so a re-reconcile updates in place; `readIssue` re-derives
`facts` from the stored label set via extractLabelFacts, so the mirror can't
drift from the label semantics. Facts are never stored.
- Resolves#3's open scope: pure mappers + SQL in core (driver-agnostic), native
driver in main. Tests bind node:sqlite (present in the Node 24 test runtime) to
the same CacheDriver seam better-sqlite3 will fill in main.
Acceptance met: upsert one reconciled issue, read back, assert
estimateDays/priority/hardDeadline (+ upsert-in-place, no-milestone, and miss
cases). Core suite green; typecheck clean.
Follow-up (noted in the plan): the main-process better-sqlite3 adapter +
snapshot-store migration — packaging-sensitive (native module), kept out of this
slice so the shippable .dmg stays verified.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>