Repository navigation
P0: ship .d.ts + deterministic export - #1
Merged
Merged
Conversation
Fixes two P0 issues from the spec audit (roadmaps/graphbase.md, code-reviews/graphbase/CONSOLIDATED.md).
1. DTS / typecheck build break (types silently did not ship)
- src/core/Graph.ts: TraverseDirection was imported as a *value* but used
only inside a JSDoc fenced @example, tripping noUnusedLocals (TS6133).
tsc --noEmit exited 2, the tsup DTS phase failed, and no dist/index.d.ts
was produced -- yet tsup exits 0, so naive CI shipped an untyped package.
Dropped the unused value import (the JSDoc text needs no symbol).
- Build now asserts the declaration file: `build` runs a `check:dts`
postbuild step (fs.accessSync on dist/index.d.ts) so the type contract is
enforced even though tsup swallows DTS errors. Added .github/workflows/ci.yml
(npm ci / typecheck / test / build + explicit d.ts existence assertion).
2. Deterministic / canonical export (was insertion-order + wall-clock dependent)
- src/bundle/exporter.ts: sort nodes by id, edges by (source,type,target,id),
schemas by type key; recursively canonicalize object-key order via a new
small src/bundle/canonicalize.ts (the seam intended to be replaced by the
spine's canon v1). canonicalize also deep-clones, so the bundle no longer
shares references with storage.
- Dropped the injected `new Date().toISOString()` from metadata: the hashed
envelope must not depend on production time. createdAt is now caller-supplied
or absent. Updated the stale JSDoc that promised auto-generation.
- Added a round-trip determinism suite: same logical graph built in two
different insertion orders (including nested property key order) exports to
byte-identical JSON; verified non-vacuous (removing canonicalize turns the
test red).
Misc: removed the broken `lint` script (no ESLint config present); synced the
stale package name in package-lock.json (core-graph -> graphbase).
Tests: 356 passing. 347 of the original 348 unchanged; the lone rewrite
(`auto-generate createdAt`) is required because P0-2 removes wall-clock
injection; +8 net new tests. tsc --noEmit clean; dist/index.d.ts produced (79KB,
exports verified).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6NtqFtBPRyge7mZuLyrbs
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.
P0 — Deterministic export + ship types
Implements Phase P0 from the
specaudit roadmap (roadmaps/graphbase.md) and the consolidated review (code-reviews/graphbase/CONSOLIDATED.md). Two gating issues, both fixed, with enforcement so they can't silently regress.1. DTS / typecheck build break — types silently did not ship
src/core/Graph.ts:18importedTraverseDirectionas a value but used it only inside a JSDoc fenced@example. WithnoUnusedLocals: true,tsc --noEmitexited 2 (TS6133), the tsup DTS phase failed, and nodist/index.d.tswas produced — yettsupexits 0, so naive CI shipped an untyped package despitepackage.jsondeclaring"types": "dist/index.d.ts".buildruns acheck:dtspostbuild step (fs.accessSyncondist/index.d.ts) that fails the build if the d.ts is missing — necessary because tsup swallows DTS errors. Added.github/workflows/ci.yml(npm ci / typecheck / test / build + an explicit d.ts existence assertion).2. Deterministic / canonical export — was insertion-order + wall-clock dependent
src/bundle/exporter.tspreserved Map insertion order and injectednew Date().toISOString()into metadata, so two semantically identical graphs produced different bytes / hashes.id, edges by(source, type, target, id), schemas by type key.src/bundle/canonicalize.tsrecursively canonicalizes object-key ordering (including nested property and schema shapes). It is a deliberately small, isolated seam intended to be replaced by the spine'scanon v1. It also deep-clones, so the returned bundle no longer shares references with storage (defensive at the boundary).new Date()— the hashed envelope must not depend on production time.createdAtis now caller-supplied or absent; stale JSDoc updated.canonicalizeturns the test red.Scope notes (beyond the literal one-liner ask, surfaced explicitly)
lintscript (no ESLint config present; roadmap-sanctioned). eslint devDeps left untouched.check:dtsalready satisfies that — the workflow is the roadmap's "CI-verified" wording, additive).package-lock.json(core-graph→graphbase).canonicalize/canonicalStringifyare exported from./bundlebut intentionally not re-exported at the package root — the canonical-serialization surface is provisional pending spinecanon v1and the P2CanonicalGraphBundleenvelope, so this PR avoids committing to it as a root-level public contract.Exit gate — met
dist/index.d.tsproduced (79KB;canonicalize/canonicalStringifypresent in it) and CI-asserted.tsc --noEmitclean.auto-generate createdAt) is required because P0-2 removes wall-clock injection; +8 net new tests.Does not include P1/P2 (import safety, facade updates, vendored-copy consolidation).
🤖 Generated with Claude Code
https://claude.ai/code/session_01G6NtqFtBPRyge7mZuLyrbs