Skip to content
🚧 Early alpha — building the foundation. See the roadmap →

Inline SQLite distribution and startup readiness — synthesis

Created Updated

Crosswalker now carries its reporting-database engine inside the standard three-file plugin package. Isolated real-Obsidian tests passed for both a seeded vault and a new empty vault, including automatic startup. This is local installation-shaped acceptance, not a published release or a BRAT download/install test.

Two fixes landed together. First, the Tier 2 (the deletable, recoverable SQLite sidecar Crosswalker projects vault frontmatter into) loader used to expect two loose files — sqlite3.wasm and sqlite3.mjs — beside main.js, but the documented three-file install path never places them there. The engine is now embedded directly in main.js at build time and checked byte-for-byte by a build-time verifier. Second, the startup barrier that decides when Tier 2 is safe to query used to count a fixed number of metadata-cache events and assume that meant every file’s frontmatter had arrived; it now polls the actual pending-metadata predicate on a bounded, monotonic-clock timer instead. Both fixes are in the same bundle validated in the 2026-09-13 runtime pass below.

What did not need fixing. Obsidian’s own search, Bases views, and Crosswalker’s own file-backed pivot tables read directly from vault Markdown — they never depended on Tier 2 and are unaffected by any of this. Tier 2 is a separate, deletable SQLite sidecar that only Crosswalker’s own reporting and housekeeping commands query; it is what needed the missing engine files, and it is what needed correct startup timing.

Why “the UI looks ready” isn’t the same question as “the data is ready.” Obsidian tells a plugin its workspace layout is ready before it has necessarily finished handing that plugin every file’s parsed frontmatter. The old startup check counted a fixed number of “resolved” metadata events (three) as a stand-in for “all files are parsed” — on a vault where a few files resolve late or out of order, that undercounted and let a projection start with incomplete data. The fix asks the direct question instead, repeatedly: are there still files with no parsed metadata yet? It rechecks that at most every 100 milliseconds, for up to a 12-second waiting budget, and proceeds the moment the answer is no — not after a fixed count of “3 resolved events.” The 2026-09-13 runtime pass shows both ends of this: the seeded-vault run was not ready at the first check and became ready after one polling interval (checks: 2, elapsedMs: 100.3), while the empty-vault first run was ready immediately (checks: 1, elapsedMs: 0, no timer even scheduled). Existing error-handling and orphan-pruning safety behavior is unchanged either way.

A user following the documented install steps — copy three files, enable the plugin — got a Tier 2 that could never start, because the loader’s actual file dependency didn’t match what that install path provides. That gap is now closed at the packaging level, and the timing gap that could let a projection start against partially-loaded metadata is closed at the startup level. Both were demonstrated broken in isolation before the fix (a real-Obsidian ENOENT probe for the first; an isolated old-code replay for the second) and are now demonstrated working together in a real-Obsidian runtime pass.

4. Evidence (verdict only — detail lives in CHANGELOG)

Section titled “4. Evidence (verdict only — detail lives in CHANGELOG)”
EvidenceVerdict
Real-Obsidian ENOENT probe (2026-09-10), old loaderReproduced the missing-file failure directly, once
Isolated Jest reproduction, old loader dependencyConfirms the same dependency in code, separately from the probe above
Isolated replay of the old readiness helper against a preserved snapshotConfirms premature settlement: Expected false; Received true
Inline-SQLite delivery: full unit suite, lint, build, release verifier, six static gatesAll green / exit 0
Startup-readiness delivery: full unit suite, lint, build, release verifier, six static gatesAll green / exit 0
Later, separate focused test-only correction (3 suites, 36 tests)Green; not a new combined full-suite count
Independent source-only review, inline-SQLite deliveryCleared
Independent source-only review, startup-readiness deliveryCleared, with four non-blocking findings
Real-Obsidian runtime, seeded vault (2026-09-13)29 of 29 declarations passed
Real-Obsidian runtime, new empty vault, first run (2026-09-13)1 of 1 declaration passed

Exact byte counts, SHA-256 hashes, suite/test counts, and finding text are in CHANGELOG.md under [Unreleased] — read there, not duplicated here.

WhatDecision
Distribution shapeEmbed the resolved @sqlite.org/sqlite-wasm package files inside main.js at build time; no loose SQLite files ship
VerificationA build-time verifier proves byte/SHA-256 equality between embedded and source package files; it is a static proof, not a runtime test
Startup barrierPoll the real pending-metadata predicate on a bounded, monotonic-clock timer (≤100ms interval, ≤12s deadline) instead of counting a fixed number of events
Standalone early Tier 2 callsOut of scope — this fix covers only the normal startup path, not a standalone openTier2/schema-rebuild call made outside it
Release workflow.github/workflows/release.yml gains a verifier-invocation step; trigger, tag delete-and-recreate behavior, and the missing --prerelease flag are unchanged — see §7
  • Verify package.json and manifest.json versions agree
  • Resolve the tag delete-and-recreate behavior and the missing --prerelease flag in release.yml — pre-existing gates, not something this delivery clears
  • Confirm the tagged commit matches (or is source-identical to) the reviewed and runtime-tested commit
  • Confirm exactly three release assets (main.js, manifest.json, styles.css)
  • Run an actual BRAT install test only after a separately approved release — not authorized by this work
  • No real BRAT or release-artifact install was exercised — only a local development-vault install shape
  • No mobile Obsidian execution
  • OPFS-versus-in-memory persistence mode was not observed in either runtime run
  • No load-timing or performance measurement beyond the two data points in §2
  • The 12-second waiting budget is a bound, not a proof of sufficiency for larger or slower vaults than the two tested here
  • An unrelated Mocha/screenshot-capture hang seen on an earlier attempt did not recur here, but was neither explained nor patched by this work
  • No milestone status changes anywhere as a result of this delivery