Lands #3's core seam: a rebuildable SQLite mirror of the reconciled backlog — an index over gitea's durable truth, never the source of truth (D4).
What
cache/cache-v0.ts: the 5-table schema (issues/labels/milestones/comments/issue_events); a CacheDriver injected-IO interface (exec/run/get/all); pure row↔domain mappers.
upsertIssue mirrors one issue (+ its milestone), upsert-by-number so re-reconciling updates in place, never duplicates. readIssuere-derives facts from the stored label set via extractLabelFacts — facts are never stored, so the mirror can't drift from the label semantics.
Resolves the issue'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 seam better-sqlite3 will fill.
Acceptance ✅
Upsert one reconciled issue → read back → assert estimateDays/priority/hardDeadline (plus upsert-in-place, no-milestone, and cache-miss cases). 143 core tests green; typecheck clean.
Deliberately out of scope (follow-up)
The main-process better-sqlite3 adapter + migrating the JSON snapshot-store onto it. It's packaging-sensitive (native module → electron-rebuild + asarUnpack) and would need a fresh .dmg verification; kept separate so the shipped build stays green. #30 (purity/rebuild test) depends on that integration, so it stays blocked until then.
Lands #3's core seam: a rebuildable SQLite mirror of the reconciled backlog — an index over gitea's durable truth, never the source of truth (D4).
## What
- **`cache/cache-v0.ts`**: the 5-table schema (`issues`/`labels`/`milestones`/`comments`/`issue_events`); a `CacheDriver` injected-IO interface (`exec`/`run`/`get`/`all`); pure row↔domain mappers.
- `upsertIssue` mirrors one issue (+ its milestone), **upsert-by-number** so re-reconciling updates in place, never duplicates. `readIssue` **re-derives `facts` from the stored label set** via `extractLabelFacts` — facts are never stored, so the mirror can't drift from the label semantics.
- Resolves the issue'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 seam better-sqlite3 will fill.
## Acceptance ✅
Upsert one reconciled issue → read back → assert `estimateDays`/`priority`/`hardDeadline` (plus upsert-in-place, no-milestone, and cache-miss cases). 143 core tests green; typecheck clean.
## Deliberately out of scope (follow-up)
The main-process **better-sqlite3** adapter + migrating the JSON snapshot-store onto it. It's packaging-sensitive (native module → electron-rebuild + asarUnpack) and would need a fresh .dmg verification; kept separate so the shipped build stays green. **#30 (purity/rebuild test) depends on that integration**, so it stays blocked until then.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
christian
added 1 commit 2026-07-09 19:31:01 +00:00
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>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Lands #3's core seam: a rebuildable SQLite mirror of the reconciled backlog — an index over gitea's durable truth, never the source of truth (D4).
What
cache/cache-v0.ts: the 5-table schema (issues/labels/milestones/comments/issue_events); aCacheDriverinjected-IO interface (exec/run/get/all); pure row↔domain mappers.upsertIssuemirrors one issue (+ its milestone), upsert-by-number so re-reconciling updates in place, never duplicates.readIssuere-derivesfactsfrom the stored label set viaextractLabelFacts— facts are never stored, so the mirror can't drift from the label semantics.node:sqlite(present in the Node 24 test runtime) to the same seam better-sqlite3 will fill.Acceptance ✅
Upsert one reconciled issue → read back → assert
estimateDays/priority/hardDeadline(plus upsert-in-place, no-milestone, and cache-miss cases). 143 core tests green; typecheck clean.Deliberately out of scope (follow-up)
The main-process better-sqlite3 adapter + migrating the JSON snapshot-store onto it. It's packaging-sensitive (native module → electron-rebuild + asarUnpack) and would need a fresh .dmg verification; kept separate so the shipped build stays green. #30 (purity/rebuild test) depends on that integration, so it stays blocked until then.
🤖 Generated with Claude Code