Skip to content

fix(data): repair missing tiebreak_key column before first node_properties read - #498

Merged
crs48 merged 3 commits into
mainfrom
claude/agitated-wilson-fe12f3
Jul 13, 2026
Merged

fix(data): repair missing tiebreak_key column before first node_properties read#498
crs48 merged 3 commits into
mainfrom
claude/agitated-wilson-fe12f3

Conversation

@crs48

@crs48 crs48 commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Problem

A fresh browser profile could not open any /doc/ page against the web dev server: the page surface showed

Error: SQLITE_ERROR: sqlite3 result code 1: no such column: p.tiebreak_key

Exploration 0305 added tiebreak_key to the v8 DDL and to the hydrate queries, but:

  • A database created before v8 (e.g. by a build of @xnetjs/sqlite predating 0305 — the web dev server resolves workspace packages to their built dist, so a stale build creates v7-shaped tables) never gets the column: applySchema re-execs the full DDL with CREATE TABLE IF NOT EXISTS, which cannot add a column, and SCHEMA_MIGRATIONS had no 8: entry.
  • The lazy repair guard ensureNodePropertyColumns() only ran on write paths. Opening a doc page is pure reads — the first hydrate query selecting p.tiebreak_key threw before any write could run the ALTER.

Fix

  • @xnetjs/data: run ensureNodePropertyColumns() before the read entry points that select p.tiebreak_keygetNode, 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 missing 8: migration entry (ALTER TABLE node_properties ADD COLUMN tiebreak_key TEXT).

Tests

  • New regression suite recreates the pre-v8 node_properties shape and asserts getNode/listNodes/queryNodes repair the column instead of throwing (both fail without the fix).
  • RPC-count tests (0264 fusion/arity) now prime the one-time repair before counting steady-state queries.
  • Verified live: fresh OPFS + localStorage profile against the worktree dev server boots to the workbench, creates a Page, and opens /doc/<id> with zero console errors.

🤖 Generated with Claude Code

@crs48
crs48 temporarily deployed to pr-498 July 13, 2026 19:22 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

✓ Changelog fragment found — thanks!

@crs48
crs48 force-pushed the claude/agitated-wilson-fe12f3 branch from 6d6b902 to 0ea419f Compare July 13, 2026 19:25
…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
crs48 temporarily deployed to pr-498 July 13, 2026 19:25 — with GitHub Actions Inactive
@crs48
crs48 force-pushed the claude/agitated-wilson-fe12f3 branch from 0ea419f to e2e78cd Compare July 13, 2026 19:25
@crs48
crs48 temporarily deployed to pr-498 July 13, 2026 19:25 — with GitHub Actions Inactive
Signed-off-by: xNet Test <test@xnet.dev>
@crs48
crs48 temporarily deployed to pr-498 July 13, 2026 19:36 — with GitHub Actions Inactive
…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>
@crs48
crs48 temporarily deployed to pr-498 July 13, 2026 19:37 — with GitHub Actions Inactive
github-actions Bot added a commit that referenced this pull request Jul 13, 2026
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Preview removed for PR #498.

github-actions Bot added a commit that referenced this pull request Jul 13, 2026
@crs48
crs48 merged commit 85ec8f6 into main Jul 13, 2026
21 checks passed
@crs48
crs48 deleted the claude/agitated-wilson-fe12f3 branch July 13, 2026 19:50
github-actions Bot added a commit that referenced this pull request Jul 13, 2026
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.
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