Skip to content

feat(branches): declare tables into a branch through @table, ensureTable and defineTable - #2523

Merged
kriszyp merged 3 commits into
mainfrom
kris/2264-branch-table-factory
Sep 9, 2026
Merged

feat(branches): declare tables into a branch through @table, ensureTable and defineTable#2523
kriszyp merged 3 commits into
mainfrom
kris/2264-branch-table-factory

Conversation

@kriszyp

@kriszyp kriszyp commented Sep 7, 2026

Copy link
Copy Markdown
Member

Summary

Rebased this draft onto current main while retaining branch-scoped declarations: scopedTableFactory routes @table, scope.ensureTable(), and defineTable() into an application's private branch without changing the unbranched factory identity. The integration proof exercises the exported route, scoped import, and base isolation for a GraphQL declaration in the branch-schema integration suite.

Closes #2264. Part of #642.

For the human reviewer

  1. The change reuses the global declaration engine with a branch-bound target rather than creating a second schema implementation. The highest-risk boundary is reloadBranch: schema changes must remain private while another worker refreshes its branch catalog. Reverting this choice restores the former refusal of branch declarations; a branch-specific engine would be broader and duplicate lifecycle behavior.
  2. Cross-model review confirmed the long-standing incomplete-index readiness gap applies to branch reloads too (Secondary index opened by a schema-change rescan serves partial results during another worker's backfill #2520), but this rebase intentionally preserves the existing narrow issue scope rather than adding a cross-worker indexing redesign. A reviewer may require that follow-up before merging if branch schema evolution with live index backfills must be supported now; otherwise the contained declaration behavior remains independently verified.

Verification

  • npm run build, npm run test:unit:main, npm run test:unit:resources, and npm run test:integration:all passed before the rebase push.
  • integrationTests/components/branched-database-schema.test.ts passed; its route, scoped-import, and base-isolation assertions are linked above.
  • Required GitHub Actions checks for the rebased head are passing.
  • Full independent pre-push review and Harper-domain adjudication completed for a33222d47248; no code was changed after that review.

Complexity: complicated

🤖 Rebase and review coverage refreshed by GPT-5 Codex.

Review-Coverage: authored=codex; ran=gemini,cursor-grok; adjudicated=domain; blocked=claude(exit-1); declined=cursor-composer; rounds=1 @ a33222d

Human-Review-Need: 4 (decisions: shared-declaration-engine, unbranched-name-fallback, declaration-only-evolution) @ a33222d

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements support for landing table declarations (via GraphQL @table, scope.ensureTable, and defineTable) directly into an application's private database branch rather than refusing them or misdirecting them to the base database. It introduces scopedTableFactory to route declarations dynamically, updates branch lifecycle management to track and clean up opened stores and tables, and implements cross-thread schema propagation for branches via ITC signals. Feedback was provided regarding a potential resource leak in openBranchDatabase where the catch block does not pass tables to closeBranchHandles on failure, which could leak expiration/eviction timers or reclamation handlers for partially initialized tables.

Comment thread resources/databases.ts
kriszyp added a commit that referenced this pull request Sep 7, 2026
The failure path of openBranchDatabase closed the stores a partial open had created but never ran
Table.cleanup on the classes built over them, leaving expiration and eviction timers and reclamation
handlers alive for a branch that no longer exists. (Review finding on #2523.)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@kriszyp

kriszyp commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

CI note: the Unit Test failures on this PR are main's, not this change's. Main's own Unit Test run at its current head (38fcbf1) fails with the same MQTT subscribe to retained record with patch operations assertion ('update 3' == 'update 2', the flake previously addressed in #1347 and #1697), and the second failure seen here (lookup scans every entry sharing the transaction log key) is in unitTests/resources/dualClockAuditRecord.test.js, added by #2497, which is also red on main from 6d5a463. Neither test touches the table factory or branch code in this PR; the branch, deploy and both branched-database integration suites pass here.

— Claude Fable 5.1

kriszyp and others added 3 commits September 8, 2026 18:54
…ble and defineTable (#2264)

A branched application's `schema.graphql`, `scope.ensureTable()` and `defineTable()` all resolved
through the process-global `table()` factory, so its declared tables would have landed in the base
database while its code read and wrote the branch. Those paths were fenced (`branchGuard.ts`);
this replaces the fence with real branch-scoped declaration.

`table()` is now the global binding of `declareTable(target, definition)`: a `TableTarget` supplies
the root store, the `tables` graph a class is published into, the reload after a lost create race,
and who owns the column-family wrappers a declaration opens. The global target is the same code with
the same objects behind it, so an unbranched application still receives `table` by identity.
`scopedTableFactory(branches)` routes each declaration to the branch of the database it names, or
to `table()` for a database the application did not branch; GraphQL, `ensureTable` and
`defineTable` (via `defineTableUsing`) take that factory from the application scope.

A branch's schema-change signal carries the branch path; a thread holding that branch open reloads
its catalog (`reloadBranchAt`) instead of rescanning the global map. Branch classes announce to no
global `updateTable` subscriber. Every wrapper a declaration opens is recorded on the branch so
`close()` releases it, and table classes are cleaned up before their stores.

The Table statics that resolve the global schema by logical name (`dropTable`, `addAttributes`)
stay refused through a branch.

Implemented by the dispatch dev-agent (task harper-2264); brought onto main after harper#2517.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The failure path of openBranchDatabase closed the stores a partial open had created but never ran
Table.cleanup on the classes built over them, leaving expiration and eviction timers and reclamation
handlers alive for a branch that no longer exists. (Review finding on #2523.)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: GPT-5 Codex <noreply@openai.com>
@kriszyp
kriszyp force-pushed the kris/2264-branch-table-factory branch from 3671da5 to a33222d Compare September 9, 2026 01:14
@kriszyp
kriszyp marked this pull request as draft September 9, 2026 01:14
@claude

claude Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found.

@kriszyp
kriszyp marked this pull request as ready for review September 9, 2026 12:54
@kriszyp
kriszyp merged commit 0762b90 into main Sep 9, 2026
48 checks passed
@kriszyp
kriszyp deleted the kris/2264-branch-table-factory branch September 9, 2026 12:55
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.

Branched databases: scope the table factory through GraphQL @table, ensureTable, and defineTable

1 participant