Skip to content

feat(epic-store): foundation — spec + plan + parser/renderer/migrations#188

Merged
thejustinwalsh merged 10 commits into
mainfrom
feat/file-backed-epic-store
Jun 3, 2026
Merged

feat(epic-store): foundation — spec + plan + parser/renderer/migrations#188
thejustinwalsh merged 10 commits into
mainfrom
feat/file-backed-epic-store

Conversation

@thejustinwalsh

@thejustinwalsh thejustinwalsh commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

Brainstormed → spec'd → planned → implemented the foundation of the file-backed Epic store (per-repo opt-in, hybrid: file Epics + GitHub PRs).

This PR ships: the design spec, the implementation plan, the gateway-interface rename, the two schema migrations, the Epic file format with parser/renderer, and a byte-identical round-trip property test.

🚧 Draft — foundation only. The actual file gateways (fileEpicGateway/fileStateGateway/filePollGateway) and bootstrap selector are deferred to the next session pending a design call (see "Open design fork" below).

What's in this PR

Commit What
8a25b90 chore lint/format cleanup from full-suite verification
081fe7d feat Epic file renderer + byte-identical round-trip for every fixture
6b30389 feat Epic file parser (meta, sections, sub-issues, conversation)
c657e07 feat marker constants + Epic-file type model
34f9a85 feat migration 008: workflows.epic_ref with backfill
8f5dc2f feat migration 007: repo_config.epic_store + epics_dir + state_file
114f632 refactor rename GitHubGateway/StateIssueGateway/GitHubPollGatewayEpicGateway/StateGateway/PollGateway
a1cab30 docs implementation plan (27 tasks across Phase 1+2)
996011f docs design spec

All commits are back-compat — every existing repo defaults to epic_store = 'github' and behaves identically. Zero workflow body changes; zero behavior change in github mode.

Verification

  • bun run typecheck — clean
  • bun run lint — clean
  • bun run format — clean
  • bun test1090 pass, 0 fail (+16 new tests: 12 parser + 4 round-trip)
  • The byte-identical round-trip invariant holds for all four fixtures (empty Epic, codex-adapter, mid-question with open question, all-closed with provenance)

Open design fork (you call it at the start of the next session)

The plan/spec said "rename epicNumber: numberepicRef: string everywhere." Reading the actual EpicGateway surface (packages/dispatcher/src/github.ts:98), that's ~30 callsites across workflows + recommender + poller + main.ts + tests, plus the workflow input shape (ImplementationInput.epicNumber: number), plus the dispatcher's HTTP handler.

Two ways forward:

A. Full epicRef: string refactor (the spec's intent)

  • Cleanest long-term; matches the spec.
  • ~3-4 hours of mechanical changes + test updates in the next session.
  • Workflow code never sees a number again; the file mode's slug is the canonical Epic identifier.

B. Synthetic numeric ID per Epic file (pragmatic shortcut)

  • File gateway maps slug ↔ number internally (counter file .middle/epics/.ids or stable hash).
  • Every existing interface keeps taking number. Workflow code unchanged.
  • Smaller change (~1 hour); some semantic ugliness (Epic "Implement state-issue v1 types, parser, and renderer #3" doesn't refer to a GitHub issue in file mode).
  • Spec compatibility: deviates from the spec's "epicRef: string everywhere" — would need a spec amendment.

I stopped here rather than ramming through one path because it's a real architectural call, not mechanical work.

Follow-ups already filed

What's NOT yet in this PR (next-session work)

  • File gateway implementations (Tasks 8-10) — gated on the design fork above
  • Bootstrap selector + postQuestion wiring (Tasks 11-12)
  • CLI: mm init --epic-store=file, mm dispatch --epic <slug>, mm doctor mode-aware, mm resume (Tasks 13-16)
  • Skill refactor (Tasks 17-20)
  • Parity test (Task 21)
  • Phase 2: file-watcher Q&A loop (Tasks 23-26)

Spec + plan

  • Design: docs/superpowers/specs/2026-05-29-file-backed-epic-store-design.md
  • Plan: docs/superpowers/plans/2026-05-29-file-backed-epic-store.md

Read the spec first; the plan is the operational decomposition.

Summary by CodeRabbit

New Features

  • Added file-backed Epic store mode: opt-in per-repository setting to store Epics and Q&A conversation history in Markdown files alongside GitHub-native workflows.

Documentation

  • Added design specification and implementation plan for the file-backed Epic store feature.

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9ae310b6-8ddd-4916-881f-aeceedb58848

📥 Commits

Reviewing files that changed from the base of the PR and between 5d9c226 and 2db1447.

📒 Files selected for processing (41)
  • docs/superpowers/plans/2026-05-29-file-backed-epic-store.md
  • docs/superpowers/specs/2026-05-29-file-backed-epic-store-design.md
  • packages/cli/test/db-scripts.test.ts
  • packages/dashboard/src/db-deps.ts
  • packages/dispatcher/src/audit-cron.ts
  • packages/dispatcher/src/audit.ts
  • packages/dispatcher/src/build-deps.ts
  • packages/dispatcher/src/db/migrations/008_repo_config_epic_store.sql
  • packages/dispatcher/src/db/migrations/009_workflows_epic_ref.sql
  • packages/dispatcher/src/epic-store/epic-file/markers.ts
  • packages/dispatcher/src/epic-store/epic-file/parser.ts
  • packages/dispatcher/src/epic-store/epic-file/renderer.ts
  • packages/dispatcher/src/epic-store/epic-file/types.ts
  • packages/dispatcher/src/epics-cache.ts
  • packages/dispatcher/src/gates/checkbox-revert-pass.ts
  • packages/dispatcher/src/gates/gate-evidence.ts
  • packages/dispatcher/src/github.ts
  • packages/dispatcher/src/main.ts
  • packages/dispatcher/src/poller-gateway.ts
  • packages/dispatcher/src/poller.ts
  • packages/dispatcher/src/recommender-run.ts
  • packages/dispatcher/src/reconcilers/pr-divergence.ts
  • packages/dispatcher/src/staleness-cron.ts
  • packages/dispatcher/src/staleness.ts
  • packages/dispatcher/src/state-issue.ts
  • packages/dispatcher/src/workflows/recommender.ts
  • packages/dispatcher/test/backlog-audit.test.ts
  • packages/dispatcher/test/db-migrations.test.ts
  • packages/dispatcher/test/db.test.ts
  • packages/dispatcher/test/epic-store/fixtures/all-closed.md
  • packages/dispatcher/test/epic-store/fixtures/codex-adapter.md
  • packages/dispatcher/test/epic-store/fixtures/empty-epic.md
  • packages/dispatcher/test/epic-store/fixtures/mid-question.md
  • packages/dispatcher/test/epic-store/parser.test.ts
  • packages/dispatcher/test/epic-store/round-trip.test.ts
  • packages/dispatcher/test/epics-cache.test.ts
  • packages/dispatcher/test/gates/checkbox-revert-pass.test.ts
  • packages/dispatcher/test/poller.test.ts
  • packages/dispatcher/test/pr-divergence-integration.test.ts
  • packages/dispatcher/test/staleness.test.ts
  • packages/dispatcher/test/state-issue.test.ts

📝 Walkthrough

Walkthrough

This PR establishes the foundation for a file-backed Epic store mode, enabling per-repo opt-in storage of Epic data and Q&A conversations in Markdown files instead of GitHub issues. It introduces comprehensive design documentation, renames gateway interfaces for clarity (GitHubGatewayEpicGateway, StateIssueGatewayStateGateway), extends the database schema with new migration files, and implements a complete Epic markdown file format with strict parsing and byte-identical rendering.

Changes

File-backed Epic store foundation

Layer / File(s) Summary
Design specification and implementation plan
docs/superpowers/specs/2026-05-29-file-backed-epic-store-design.md, docs/superpowers/plans/2026-05-29-file-backed-epic-store.md
Comprehensive design spec and Phase 1/2 implementation plan for the file-backed Epic store feature, including architecture (parallel gateway implementations), schema migrations, CLI command semantics, Epic file format, gateway responsibilities, testing/parity strategy, and scope.
Gateway interface renames and wiring updates
packages/dispatcher/src/github.ts, packages/dispatcher/src/state-issue.ts, packages/dispatcher/src/poller.ts, packages/dispatcher/src/audit-cron.ts, packages/dispatcher/src/audit.ts, packages/dispatcher/src/build-deps.ts, packages/dispatcher/src/epics-cache.ts, packages/dispatcher/src/gates/checkbox-revert-pass.ts, packages/dispatcher/src/gates/gate-evidence.ts, packages/dispatcher/src/staleness-cron.ts, packages/dispatcher/src/staleness.ts, packages/dispatcher/src/main.ts, packages/dispatcher/src/poller-gateway.ts, packages/dispatcher/src/recommender-run.ts, packages/dispatcher/src/reconcilers/pr-divergence.ts, packages/dispatcher/src/workflows/recommender.ts, packages/dashboard/src/db-deps.ts
Systematic renaming of gateway types across dispatcher and dashboard modules: GitHubGatewayEpicGateway, StateIssueGatewayStateGateway, GitHubPollGatewayPollGateway. Updates all type imports and dependency declarations to use the renamed interfaces while preserving runtime behavior.
Database schema migrations for epic store
packages/dispatcher/src/db/migrations/008_repo_config_epic_store.sql, packages/dispatcher/src/db/migrations/009_workflows_epic_ref.sql
Migration 008 adds epic_store (non-null, default 'github'), epics_dir, and state_file columns to repo_config. Migration 009 adds epic_ref (nullable) to workflows and backfills from existing epic_number values.
Epic file format types, markers, parser, and renderer
packages/dispatcher/src/epic-store/epic-file/types.ts, packages/dispatcher/src/epic-store/epic-file/markers.ts, packages/dispatcher/src/epic-store/epic-file/parser.ts, packages/dispatcher/src/epic-store/epic-file/renderer.ts
Complete Epic markdown format implementation: EpicFile/EpicMeta types define the structure; markers define all boundary tokens; parser validates document structure, extracts metadata/sections/conversation with strict error handling; renderer outputs canonical Markdown preserving byte-for-byte round-trip invariant.
Epic file parsing and round-trip tests
packages/dispatcher/test/epic-store/parser.test.ts, packages/dispatcher/test/epic-store/round-trip.test.ts, packages/dispatcher/test/epic-store/fixtures/codex-adapter.md, packages/dispatcher/test/epic-store/fixtures/all-closed.md, packages/dispatcher/test/epic-store/fixtures/empty-epic.md, packages/dispatcher/test/epic-store/fixtures/mid-question.md
Parser test suite validates document structure, metadata key extraction, acceptance criteria/sub-issues parsing, and conversation entry handling; round-trip test ensures all fixtures parse and render identically. Four fixture files provide realistic test data covering codex adapter, closed epic, empty epic, and mid-conversation scenarios.
Test fixture updates and schema version bump
packages/cli/test/db-scripts.test.ts, packages/dispatcher/test/db.test.ts, packages/dispatcher/test/db-migrations.test.ts, packages/dispatcher/test/backlog-audit.test.ts, packages/dispatcher/test/epics-cache.test.ts, packages/dispatcher/test/gates/checkbox-revert-pass.test.ts, packages/dispatcher/test/poller.test.ts, packages/dispatcher/test/pr-divergence-integration.test.ts, packages/dispatcher/test/staleness.test.ts, packages/dispatcher/test/state-issue.test.ts
Updates all test expectations to reflect schema version 7→9, adds comprehensive migration test assertions for new columns/defaults/backfill behavior, and updates test helpers to use renamed gateway types in fixtures and dependency injection contracts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • thejustinwalsh/middle#163: Adds self-audit cron/passes that intersect with the audit-cron/audit module gateway type updates in this PR.
  • thejustinwalsh/middle#156: Adds checkbox-revert pass implementation that shares the same checkbox-revert-pass.ts gateway wiring changes.
  • thejustinwalsh/middle#105: Implements recommender workflow that connects directly to the StateGateway type renaming and RecommenderDeps dependency contract updates in this PR.

Suggested labels

ready-for-review


Comment @coderabbitai help to get the list of available commands and usage tips.

…hybrid)

Per-repo opt-in `epic_store = "file"` mode: one Markdown file per Epic
under `planning/epics/`, recommender state in `.middle/state.md`. PRs and
CI stay GitHub-native in both modes ("hybrid"). Workflow bodies, gates,
watchdog, hook server, poller — all unchanged: the three existing single-
seam interfaces (`GitHubGateway`, `StateIssueGateway`, `GitHubPollGateway`)
get renamed (`EpicGateway`, `StateGateway`, `PollGateway`) and gain
parallel file implementations behind the same contracts. Bootstrap picks
the implementation per-repo from `repo_config.epic_store`.

The agent's `blocked.json` flow plugs in at one DI seam (`postQuestion`).
Round-trip-pure parser/renderer absorbs #178's class (structurally
distinct `question`/`answer` markers) and #180's class (renderer is the
sole writer for strict-marker content). Phase split: file-Epic dispatch
ships first (~2 wk) with a `mm resume` escape hatch; file-watcher Q&A
resume (~1 wk) rides the existing 120s poller cron.

See: docs/superpowers/specs/2026-05-29-file-backed-epic-store-design.md
…se 2

Bite-sized TDD steps, exact files + commands + expected output for each.
Phase 1 (~2 wk): rename gateways → schema migrations → parser/renderer
→ three file gateways → bootstrap selector → postQuestion wiring → mm
init/dispatch/doctor/resume → skill refactor → parity test → smoke.
Phase 2 (~1 wk): mtime poll helper → file-signal poll on poller cron →
parity test for file-edit Q&A resume → smoke.

Spec: docs/superpowers/specs/2026-05-29-file-backed-epic-store-design.md
…/Poll)

Preparation for the file-backed Epic store: the three existing single-
seam DI'd interfaces are renamed to reflect what they abstract (an Epic
store, not GitHub specifically). Implementations (ghGitHub,
ghStateIssueGateway, ghPollGateway) keep their gh* names — they're the
GitHub implementation of the renamed interfaces. Behavior unchanged.

- GitHubGateway → EpicGateway
- StateIssueGateway → StateGateway
- GitHubPollGateway → PollGateway

Mechanical codemod across 23 files. 1068 tests pass, typecheck clean.
Additive migration: adds epic_store ('github' default), epics_dir, and
state_file columns. All existing rows default to github mode — zero
behavior change for existing repos. File-mode opt-in is a single config
edit (epic_store.mode = "file" in .middle/<slug>.toml).

The bootstrap selector reads this column to pick the gateway trio at
buildImplementationDeps time. epics_dir / state_file are nullable —
only populated when mode = 'file'.
… (008)

Additive migration with backfill: epic_ref TEXT (nullable), populated
from CAST(epic_number AS TEXT) for every existing row whose epic_number
is non-null. Recommender / documentation workflows have null epic_number
and stay null epic_ref. github mode writes both columns; file mode
writes only epic_ref (slug). Application-level enforcement in
createWorkflowRecord ensures every implementation workflow has it set.

epic_number was already nullable — no table rebuild needed.
HTML-comment markers are the structural contract for the round-trip
parser/renderer that follows. Type model describes the fully-parsed Epic
shape: meta, acceptance, sub-issues, conversation. Every field a
renderer needs is on the model so the parser preserves it — required
for the byte-identical round-trip invariant.

Marker convention mirrors state-issue v1 (<!-- AGENT-QUEUE-STATE v1 -->):
the marker IS the structural contract; the renderer is the sole writer
of strict attribute lines (closes #180's class of writer/parser drift).
…rsation)

Strict on markers + attributes (the structural contract), lenient on
prose. Throws with a named-marker error when a structural element is
malformed — operators diagnose without log-tailing.

Conversation parser distinguishes dispatch-event vs question, threads
an answer block under its question, and treats an answer's html-comment-
only body as 'placeholder empty' (no resume) vs human-written text
('replied' — the file-watcher trigger).

12 tests covering: empty epic, missing markers, every meta key, checked
+ unchecked sub-issues, provenance suffix, conversation with empty
answer, conversation with non-empty answer.
…erty test

renderEpicFile(parseEpicFile(body)) === body for every fixture (empty-
epic, codex-adapter, mid-question, all-closed) — byte-identical, first
try. Round-trip purity replaces a lock: dispatcher and human can both
edit the file (dispatcher patches conversation entries via the
renderer; human edits between markers or inside their answer block)
without corrupting each other's writes.

The renderer is the sole writer of strict-marker attribute lines.
Agents/humans write between markers but never inside the attributes —
that single-writer rule closes #180's class for the file path.
- migration filenames: 008→009 (workflows.epic_ref) to clear collision
  with #181's 007_retention.sql
- db.test.ts + db-scripts.test.ts: bump schema-version assertions to 9
  (final post-008+009 state)
- re-apply rename codemod to references introduced by #185/#186 after
  the original rename codemod landed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant