fix(data): repair missing tiebreak_key column before first node_properties read - #498
Merged
Conversation
Contributor
|
✓ Changelog fragment found — thanks! |
crs48
force-pushed
the
claude/agitated-wilson-fe12f3
branch
from
July 13, 2026 19:25
6d6b902 to
0ea419f
Compare
…rties read A database created before schema v8 has no node_properties.tiebreak_key column, and the runtime upgrade path (full DDL re-exec via CREATE TABLE IF NOT EXISTS) cannot add it. The lazy repair guard only ran on write paths, so a fresh session that opened a /doc/ page hit 'no such column: p.tiebreak_key' on its first hydrate query — reads run long before any write can trigger the guard. - Run ensureNodePropertyColumns() before the read entry points that select p.tiebreak_key (getNode, getNodes, listNodes, queryNodes), and share ONE in-flight repair promise across boot's concurrent hydrates. - Add the missing v8 entry to SCHEMA_MIGRATIONS (ALTER TABLE node_properties ADD COLUMN tiebreak_key TEXT). - Regression test: recreate the pre-v8 table shape and assert the first read repairs it instead of throwing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
crs48
force-pushed
the
claude/agitated-wilson-fe12f3
branch
from
July 13, 2026 19:25
0ea419f to
e2e78cd
Compare
Signed-off-by: xNet Test <test@xnet.dev>
…enchmark capture The benchmark captures each mode's chunk SQL positionally; the pre-v8 tiebreak_key repair (0305) now emits a PRAGMA on a fresh adapter's first read, which shifted both captures onto the wrong queries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Contributor
|
Preview removed for PR #498. |
crs48
added a commit
that referenced
this pull request
Jul 15, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @xnetjs/plugins@2.0.0 ### Major Changes - [#496](#496) [`6a5a15e`](6a5a15e) Thanks [@crs48](https://github.com/crs48)! - AI page-markdown surface re-targeted to the BlockNote editor (exploration 0312). - **Breaking**: the page-markdown apply adapter mode `'tiptap-yjs'` is renamed to `'blocknote-yjs'` in `AiPageMarkdownApplyAdapterResult['mode']` and `AiPageMarkdownApplyResult['mode']`. Adapters that returned `mode: 'tiptap-yjs'` must return `'blocknote-yjs'` (or `'yjs'`/`'custom'`). - New Yjs-fragment ↔ markdown conversion for BlockNote (`content-v4`) documents, dependency-light (walks the Yjs XML tree directly, no editor/DOM): - `xnetPageFragmentToMarkdown(doc)` reads the BlockNote fragment (paragraph/heading/lists/check items/code/quote/callout/table + inline `mention`/`hashtag`/`wikilink`/`inlineMath` atoms), falling back to the legacy TipTap `content` fragment when `content-v4` is empty (`blockNoteFragmentToMarkdown` / `legacyFragmentToMarkdown` are also exported). - `replaceXNetPageFragmentWithMarkdown(doc, markdown)` writes the AI markdown subset (paragraphs, headings, bullet/numbered/check lists with nesting, fenced code, quotes, callouts, wikilinks) as BlockNote PM XML — `blockGroup > blockContainer` (unique `id` per block) wrappers — in one Yjs transaction. - `createBlockNotePageMarkdownAdapter({ resolveDoc })` packages both as an `AiPageMarkdownApplyAdapter` (plus `readMarkdown`) for `xnet_apply_page_markdown`, replacing the TipTap-era document bridge. - `XNET_PAGE_FRAGMENT_FIELD` (`'content-v4'`) and `XNET_PAGE_LEGACY_FRAGMENT_FIELD` (`'content'`) constants. - `@xnetjs/plugins` now depends on `yjs`; the unused `@tiptap/core` devDependency is gone. - [#496](#496) [`2a7b80f`](2a7b80f) Thanks [@crs48](https://github.com/crs48)! - `EditorContribution` carries BlockNote specs instead of TipTap extensions (exploration 0312). - **Breaking**: `EditorContribution.extension` (TipTap `Extension`) and `EditorContribution.toolbar` (`ToolbarContribution`, removed entirely) are gone. Plugins now contribute `blockSpecs` / `inlineContentSpecs` / `styleSpecs` (opaque BlockNote spec objects keyed by spec name) plus behavior-only `slashMenuItems`. - **Breaking**: the editor schema-skew guard is spec-based — `isSchemaDefiningExtension` is replaced by `isSchemaDefiningContribution`, and `findEditorSchemaRisks` / `warnOnEditorSchemaRisks` take the host's statically bundled spec names and flag any contributed spec outside that set (0205 invariant: schema specs must be identical across all collaborators or Yjs silently drops content). - `SlashCommandContext.editor` is now a BlockNote editor instance. - The `@tiptap/core` dependency is removed. ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/abuse@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/data@2.0.0 ### Minor Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - [#523](#523) [`a91f278`](a91f278) Thanks [@crs48](https://github.com/crs48)! - Drafts P2/P3 (exploration 0329): Patchwork-style branching on the change log. - `@xnetjs/data`: `Draft` node schema (`DRAFT_SCHEMA_IRI`, entries map, no nesting); `NodeStore` draft overlay — `setCheckedOutDraft` swaps member reads to clone content under original ids, redirects member writes to clones with lazy copy-on-write, mirrors clone change events to original-id subscribers, and exposes `getRaw` for overlay-free reads; device-local draft privacy set (`markDraftPrivate`/`isDraftPrivate`). - `@xnetjs/history`: draft lifecycle (`createDraft`, `forkNodeIntoDraft` — signed snapshot-create + pinned fork point + Yjs blob fork with state vector, `discardDraft`, `listDrafts`, never-fork policy); merge (`threeWayPropertyMerge`, `mergeDraft` — one merger-signed squash batch with draft-born promotion via temp ids, relation remapping, deletion conflict cards, idempotent Yjs delta lane, provenance) and `refreshDraftFromMain` (floating drafts). - `@xnetjs/runtime`: `NodeStoreSyncProvider` gains a `shouldPublish` predicate; the personal node-sync room excludes draft-private nodes, and draft privacy is rehydrated before sync starts. - [#523](#523) [`dd956e5`](dd956e5) Thanks [@crs48](https://github.com/crs48)! - Drafts UI plumbing (exploration 0329 P2/P3). - `@xnetjs/react`: new `useDraft(hostId)` hook (hooks sub-barrel) binding the draft engine and the NodeStore checkout overlay — list/create open drafts for a host, `checkout` (content-swap reads + lazy copy-on-write via `onMissingMember` → `forkNodeIntoDraft`), `returnToMain`, `discard` (leaves the checkout first), `merge` (merger-signed squash; returns conflict cards), `refresh` (fold main into the draft; pauses on conflicts), `setReviewRequested`, and `computeReview` — per-property three-way review cards (base at fork vs main now vs draft now) plus Yjs document-differs indicators, computed without applying anything. Database hosts widen the member scope to their row nodes. Re-exports `DraftMergeConflict`, `MergeDraftResult`, `RefreshDraftResult` for consumers. - `@xnetjs/data`: the `Draft` schema gains an optional `reviewRequested` checkbox (default `false`) — the P4 request-surfacing flag the Inbox/Requests surface lists open drafts by. - [#499](#499) [`e4cb876`](e4cb876) Thanks [@crs48](https://github.com/crs48)! - Add the `DebugReport` schema (`DebugReportSchema`, `type DebugReport`) for first-party crash/debug-report triage nodes (exploration 0315). Reports carry code-level diagnostics only, group by fingerprint, and inherit access from their diagnostics Space via the standard space cascade. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - [#498](#498) [`e2e78cd`](e2e78cd) Thanks [@crs48](https://github.com/crs48)! - Fix "no such column: p.tiebreak_key" on databases created before schema v8. The `tiebreak_key` column repair now runs before the first `node_properties` read (`getNode`/`getNodes`/`listNodes`/`queryNodes`), not just before writes — a fresh session that opened a document page hit the missing column on its first hydrate query before any write could trigger the lazy guard. Also adds the missing v8 entry to `SCHEMA_MIGRATIONS` (`ALTER TABLE node_properties ADD COLUMN tiebreak_key TEXT`). - Updated dependencies [[`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/sqlite@2.0.0 - @xnetjs/storage@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/history@2.0.0 ### Minor Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - [#523](#523) [`a91f278`](a91f278) Thanks [@crs48](https://github.com/crs48)! - Drafts P2/P3 (exploration 0329): Patchwork-style branching on the change log. - `@xnetjs/data`: `Draft` node schema (`DRAFT_SCHEMA_IRI`, entries map, no nesting); `NodeStore` draft overlay — `setCheckedOutDraft` swaps member reads to clone content under original ids, redirects member writes to clones with lazy copy-on-write, mirrors clone change events to original-id subscribers, and exposes `getRaw` for overlay-free reads; device-local draft privacy set (`markDraftPrivate`/`isDraftPrivate`). - `@xnetjs/history`: draft lifecycle (`createDraft`, `forkNodeIntoDraft` — signed snapshot-create + pinned fork point + Yjs blob fork with state vector, `discardDraft`, `listDrafts`, never-fork policy); merge (`threeWayPropertyMerge`, `mergeDraft` — one merger-signed squash batch with draft-born promotion via temp ids, relation remapping, deletion conflict cards, idempotent Yjs delta lane, provenance) and `refreshDraftFromMain` (floating drafts). - `@xnetjs/runtime`: `NodeStoreSyncProvider` gains a `shouldPublish` predicate; the personal node-sync room excludes draft-private nodes, and draft privacy is rehydrated before sync starts. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/react@2.0.0 ### Minor Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - [#523](#523) [`dd956e5`](dd956e5) Thanks [@crs48](https://github.com/crs48)! - Drafts UI plumbing (exploration 0329 P2/P3). - `@xnetjs/react`: new `useDraft(hostId)` hook (hooks sub-barrel) binding the draft engine and the NodeStore checkout overlay — list/create open drafts for a host, `checkout` (content-swap reads + lazy copy-on-write via `onMissingMember` → `forkNodeIntoDraft`), `returnToMain`, `discard` (leaves the checkout first), `merge` (merger-signed squash; returns conflict cards), `refresh` (fold main into the draft; pauses on conflicts), `setReviewRequested`, and `computeReview` — per-property three-way review cards (base at fork vs main now vs draft now) plus Yjs document-differs indicators, computed without applying anything. Database hosts widen the member scope to their row nodes. Re-exports `DraftMergeConflict`, `MergeDraftResult`, `RefreshDraftResult` for consumers. - `@xnetjs/data`: the `Draft` schema gains an optional `reviewRequested` checkbox (default `false`) — the P4 request-surfacing flag the Inbox/Requests surface lists open drafts by. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/history@2.0.0 - @xnetjs/runtime@0.5.0 - @xnetjs/data-bridge@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/runtime@0.5.0 ### Minor Changes - [#523](#523) [`a91f278`](a91f278) Thanks [@crs48](https://github.com/crs48)! - Drafts P2/P3 (exploration 0329): Patchwork-style branching on the change log. - `@xnetjs/data`: `Draft` node schema (`DRAFT_SCHEMA_IRI`, entries map, no nesting); `NodeStore` draft overlay — `setCheckedOutDraft` swaps member reads to clone content under original ids, redirects member writes to clones with lazy copy-on-write, mirrors clone change events to original-id subscribers, and exposes `getRaw` for overlay-free reads; device-local draft privacy set (`markDraftPrivate`/`isDraftPrivate`). - `@xnetjs/history`: draft lifecycle (`createDraft`, `forkNodeIntoDraft` — signed snapshot-create + pinned fork point + Yjs blob fork with state vector, `discardDraft`, `listDrafts`, never-fork policy); merge (`threeWayPropertyMerge`, `mergeDraft` — one merger-signed squash batch with draft-born promotion via temp ids, relation remapping, deletion conflict cards, idempotent Yjs delta lane, provenance) and `refreshDraftFromMain` (floating drafts). - `@xnetjs/runtime`: `NodeStoreSyncProvider` gains a `shouldPublish` predicate; the personal node-sync room excludes draft-private nodes, and draft privacy is rehydrated before sync starts. - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - [#496](#496) [`85c9700`](85c9700) Thanks [@crs48](https://github.com/crs48)! - Yjs fragment readers understand the BlockNote document schema (exploration 0312). Documents now live in the `content-v4` fragment using BlockNote's ProseMirror shape (`blockGroup > blockContainer > blockContent`); the legacy TipTap `content` fragment remains readable as a fallback until each doc is lazily imported. - `@xnetjs/data`: `getRichTextPlainText` extracts text from BlockNote-shaped rich-text cells, including the new inline atoms (`mention` → `@label`, `hashtag` → `#name`, `wikilink` → title, `inlineMath` → latex), while still reading legacy TipTap-shaped cells. - `@xnetjs/history`: version-diff text extraction prefers `content-v4` (legacy `content` fallback) and renders BlockNote inline atoms as readable text. - `@xnetjs/react`: new `useMergedEditorContributions` / `mergeEditorContributions` (+ `MergedEditorContributions` type) collect plugin-contributed BlockNote `blockSpecs`/`inlineContentSpecs`/`styleSpecs` and slash menu items from the plugin registry, running the editor schema-skew guard (`warnOnEditorSchemaRisks`) against the host's statically bundled spec names and excluding un-bundled (skew-hazard) specs. - `@xnetjs/runtime`: blob-CID retention scanning now also walks the `content-v4` and `content` fragments, so blobs referenced from page documents are discovered. - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/history@2.0.0 - @xnetjs/data-bridge@2.0.0 - @xnetjs/storage@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/sqlite@2.0.0 ### Minor Changes - [#523](#523) [`0f7ef43`](0f7ef43) Thanks [@crs48](https://github.com/crs48)! - Time Machine P1 (exploration 0329): frontiers, checkpoints, pins, prune horizon, scope timelines, production Yjs snapshot capture, and a React scrub hook. - `@xnetjs/history`: new `Frontier` primitive (hash-anchored per-node positions: `captureFrontier`, `frontierAtWallTime`, `frontierTarget`, `materializeAtFrontier`, Yjs snapshot refs + pin keys); named checkpoints (`createCheckpoint`, `listCheckpoints`, `deleteCheckpoint`, `pinFrontier`, `restoreToFrontier`); `ScopeTimeline`/`ScopeScrubCache` generalizing `SchemaTimeline` to arbitrary node sets; `HistoryHorizonError` + `HistoryEngine.getHorizon` — targets below the prune horizon now fail loudly instead of silently remapping to the wrong change. - `@xnetjs/data`: `Checkpoint` node schema (`CHECKPOINT_SCHEMA_IRI`); pin registry on storage adapters (`NodeStorageAdapter.pins`, `PinEntry`, `PinRegistry`) protecting pinned changes and Yjs snapshots from pruning and eviction (memory + SQLite implementations). - `@xnetjs/sqlite`: `pinned_changes` table (additive migration). - `@xnetjs/runtime`: Yjs history snapshots are now captured on production doc persists (throttled session-boundary/min-interval capture in NodePool). - `@xnetjs/react`: new `useTimeMachine` hook (hooks sub-barrel) binding a scrubber UI to the merged scope timeline: position/step navigation, preview + property diff at the scrub position, named versions, one-transaction restore, and history-horizon reporting. ### Patch Changes - [#498](#498) [`e2e78cd`](e2e78cd) Thanks [@crs48](https://github.com/crs48)! - Fix "no such column: p.tiebreak_key" on databases created before schema v8. The `tiebreak_key` column repair now runs before the first `node_properties` read (`getNode`/`getNodes`/`listNodes`/`queryNodes`), not just before writes — a fresh session that opened a document page hit the missing column on its first hydrate query before any write could trigger the lazy guard. Also adds the missing v8 entry to `SCHEMA_MIGRATIONS` (`ALTER TABLE node_properties ADD COLUMN tiebreak_key TEXT`). ## @xnetjs/abuse@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/cli@0.1.7 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/runtime@0.5.0 - @xnetjs/sqlite@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/crypto@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/core@2.0.0 ## @xnetjs/data-bridge@2.0.0 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/sqlite@2.0.0 - @xnetjs/sync@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/identity@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/storage@2.0.0 ### Patch Changes - Updated dependencies [[`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/sqlite@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/sync@2.0.0 ### Patch Changes - Updated dependencies []: - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## @xnetjs/core@2.0.0 ## xnet-cloud@0.0.18 ### Patch Changes - Updated dependencies []: - @xnetjs/telemetry@0.0.1 - @xnetjs/cloud@0.0.1 - @xnetjs/crypto@2.0.0 - @xnetjs/core@2.0.0 ## xnet-demos@0.1.2 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/react@2.0.0 - @xnetjs/devtools@0.1.0 - @xnetjs/identity@2.0.0 ## @xnetjs/brain@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/vectors@0.0.1 ## @xnetjs/comms@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/dashboard@0.0.19 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 - @xnetjs/react@2.0.0 - @xnetjs/social@0.0.19 ## @xnetjs/labs@0.0.19 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 ## @xnetjs/licenses@0.0.19 ### Patch Changes - Updated dependencies []: - @xnetjs/crypto@2.0.0 ## @xnetjs/maps@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 ## @xnetjs/meetings@0.0.12 ### Patch Changes - Updated dependencies [[`6a5a15e`](6a5a15e), [`2a7b80f`](2a7b80f), [`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/plugins@2.0.0 - @xnetjs/data@2.0.0 ## @xnetjs/server@0.0.18 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/data-bridge@2.0.0 - @xnetjs/identity@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/social@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 - @xnetjs/crypto@2.0.0 ## @xnetjs/unreal@0.0.19 ### Patch Changes - Updated dependencies [[`85c9700`](85c9700), [`a91f278`](a91f278), [`dd956e5`](dd956e5), [`e4cb876`](e4cb876), [`e2e78cd`](e2e78cd), [`0f7ef43`](0f7ef43)]: - @xnetjs/data@2.0.0 ## xnet-desktop@2.0.0 Desktop shell release riding the @xnetjs/core 2.0.0 train. Desktop-specific changes are not tracked here; see the core packages' changelogs for what shipped.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A fresh browser profile could not open any
/doc/page against the web dev server: the page surface showedExploration 0305 added
tiebreak_keyto the v8 DDL and to the hydrate queries, but:@xnetjs/sqlitepredating 0305 — the web dev server resolves workspace packages to their builtdist, so a stale build creates v7-shaped tables) never gets the column:applySchemare-execs the full DDL withCREATE TABLE IF NOT EXISTS, which cannot add a column, andSCHEMA_MIGRATIONShad no8:entry.ensureNodePropertyColumns()only ran on write paths. Opening a doc page is pure reads — the first hydrate query selectingp.tiebreak_keythrew before any write could run the ALTER.Fix
@xnetjs/data: runensureNodePropertyColumns()before the read entry points that selectp.tiebreak_key—getNode,getNodes,listNodes/listNodesOptimized,queryNodes(covers the materialized-view page hydrate). The memo is now a shared in-flight promise so boot's concurrent hydrates wait on ONE repair instead of racing PRAGMAs.@xnetjs/sqlite: add the missing8:migration entry (ALTER TABLE node_properties ADD COLUMN tiebreak_key TEXT).Tests
node_propertiesshape and assertsgetNode/listNodes/queryNodesrepair the column instead of throwing (both fail without the fix)./doc/<id>with zero console errors.🤖 Generated with Claude Code