feat(platform-node): a second host over better-sqlite3 + run the conformance TCK against it (spike) - #277
Conversation
Stand up @lunora/platform-node — a Node host over better-sqlite3 plus an in-process socket/directory/scheduler registry — and run the existing @lunora/platform and @lunora/shard-engine conformance TCKs against it (38/38 pass). Register NODE_CAPABILITIES honestly rated per feature, wire a `node` target into codegen's PLATFORM_MATRICES, and exercise the fail-closed capability gate against a matrix that is mostly unsupported/emulated. Registering `node` exposed that @lunora/config's driver-registry invariant conflated "codegen can gate this target" with "the CLI can deploy to this target" — narrow that test to the direction that actually matters (every deploy driver needs a matrix; the reverse no longer holds now that a dev/test-only host exists). Every place construction needed something the contracts didn't promise is classified in plans/234-node-host-findings.md, including two latent bugs found by contrast in the existing node:sqlite reference host and an architecture gap where ShardDirectory's jurisdiction contract member has no real caller in the codebase.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thank you for following the naming conventions! 🙏 |
|
Thank you for confirming the Contributor License Agreement! 🙏 |
…an't fire on a closed DB and timers/handles don't leak Nothing previously closed the better-sqlite3 handle or cleared pending timers: a caller that set a future alarm and then closed the database hit an uncaught TypeError from inside a setTimeout callback (a crash that never touches the caller's own try/catch), and the composition root plus the conformance host's cleanup both leaked the alarm and scheduler timers, keeping the process alive well past teardown. - Guard the alarm callback on `database.open` so a closed connection can never take a `.run()` again. - Give createNodeShardHost/createNodeSchedulerHost a `dispose()` that clears their own timers (and, for the shard host, closes the DB). - Add NodePlatform.close()/[Symbol.dispose] composing both disposers, and route the conformance host's cleanup() through the same path instead of a bare `database.close()`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Turns portability from a claim into a tested property:
PLATFORM_MATRICESheld exactly one entry (platformMatrixIds()'s own comment: "both hold exactlycloudflare, which is why nothing has noticed"). This stands up a real second host —@lunora/platform-node(5 contract adapters + composition root overbetter-sqlite3and an in-process socket registry) — and runs the existing conformance TCK against it.Headline result: the contract generalizes
@lunora/platform/conformance: 28/28.@lunora/shard-engine/conformance(OCC/RLS/reactive fan-out): 10/10. Plus 2 composition-root smokes → 40/40.idForstability, the fail-closed capability gate, the tagged fan-out) hold on a structurally different host, not just Cloudflare. There was nothing to "resist patching."Honest
nodecapability matrixNODE_CAPABILITIESadded to@lunora/platform(rates every feature Cloudflare rates — enforced by a same-key-set test), registered asnodein codegen'sPLATFORM_MATRICES. This is the first time the fail-closed gate has run against a mostly-unsupported/emulatedmatrix: verified end-to-end thatgatePlatformFeatures(usage, "node")flipsbrowser/containeroff withplatform_unsupported_featurediagnostics while leavingkv/scheduleron, and that{ "target": "node" }resolves through the real registry (noplatform_unknown_target).Findings (the deliverable) —
plans/234-node-host-findings.md, 7 classifiednode:sqlitereference host, found by contrast: a write-onlydurableAttachmentsmap (dead code hidden by a blanket lint exemption) and a text-sniffing ("select") read/write heuristic weaker thanbetter-sqlite3'sStatement.reader.ShardDirectory.jurisdictionhas zero real callers (five packages reimplement the check against the raw CF type instead); "durable survives host recycling" doesn't distinguish in-process recycle from process restart;ShardKvStore.put's "structured-clonable" pins no wire format (three hosts, three serializers).databaseSizehas no conformance coverage on any host.Two edits flagged for your judgment (both documented)
scripts/api-snapshot.js:platform-nodeexcluded from the API-surface guard until it graduates past spike stage (nolunora dev/deploy wiring yet, surface still moving) — alongside the other experimental packages. api:check is 43/43 with this.packages/configinvariant test: registeringnodebroke a strict-equality invariant (deployTargetIds() === platformMatrixIds()) that only ever held because both were[cloudflare]. Relaxed to the one dangerous direction — every deploy driver must have a matrix (else codegen emits an un-gated surface) — while allowing the benign codegen-gateable-but-not-yet-deployable case thatnodelegitimately is. Reasoning is inline + in the findings doc.Verification
platform-node 40, platform 11, codegen 11, config 16; full regression across the 6 affected packages +
api:check(43/43),dist:check,lint:package-json, prettier, eslint all green. Out of scope (follow-ups):lunora devwiring, a real deploy driver.Plan:
plans/234-*.md.🤖 Generated with Claude Code