Skip to content

chore(deps): upgrade gitsheets to 2.x (Rust core, #150)#151

Merged
themightychris merged 6 commits into
developfrom
chore/gitsheets-2x-bump
Jul 4, 2026
Merged

chore(deps): upgrade gitsheets to 2.x (Rust core, #150)#151
themightychris merged 6 commits into
developfrom
chore/gitsheets-2x-bump

Conversation

@themightychris

Copy link
Copy Markdown
Member

Upgrades gitsheets ^1.4.1^2.2.0 (the Rust-core line). Closes the version-bump half of #150; the "retire the 3 cache workarounds" half stays open (blocked on upstream gitsheets#184, which is not in 2.x).

Scope

Version bump only. The three #184 workarounds (swapPublic, attachments git cat-file, data-repo-lock) are kept/verified, not deleted — #184 (per-sheet refresh API) is open and not shipped in 2.x (2.x's Sheet still snapshots the pre-commit tree).

What actually broke (2.x is a "no public-API change" engine rewrite, so this is small)

  1. hologit dropped as a gitsheets dep → migrated the avatar/media blob-write path off hologit's BlobObject.write to 2.x repo.writeBlob(buf): Promise<BlobHandle> + setAttachments. Sites: routes/people.ts (avatar upload), import-laddr/importer.ts (legacy avatar + blog media). as unknown as string casts removed (2.x takes a Buffer).
  2. Null-marshal contract change (found during validation): 2.x's Rust core throws when asked to marshal a null/undefined-valued field; 1.4.1 silently dropped such keys. Our Zod schemas use .nullable().optional() and write services normalize cleared fields to ?? null. Fix: a stripNullish at the single validator write-boundary in store/public.ts drops null/undefined keys before marshalling — keeping the on-disk form byte-identical to 1.4.1 (an absent optional field is simply an absent TOML key). Covered by a new store.test.ts case.

Byte re-baseline (deliberate, lossless)

  • Integer underscores drop: legacyId = 31_61831618 (values unchanged; confirmed via the Rust core directly). Matches gitsheets#196.
  • Content-typed (markdown) sheets normalize bodies via native dprint. Our only such sheet is blog-posts.

Validation

  • type-check + lint clean under 2.x.
  • Full api suite: 423/423 green under 2.x (one test needed an assertion fix — a not.toContain('null') that false-matched the fixture's own "Nullish Person" name; the field-absence checks it duplicated already cover the real contract).
  • Native Rust core (@gitsheets/core-napi) loads and round-trips on darwin/arm64.

Follow-ups (in plans/gitsheets-2x-bump.md)

  • Data-repo re-normalization of codeforphilly-data (one deliberate commit to re-serialize all records to the new canonical form) — a data-ops task, bundled with cutover data prep. Until then records re-normalize as they're written.
  • Retire the #184 workarounds once the upstream per-sheet refresh API lands.

🤖 Generated with Claude Code

themightychris and others added 6 commits July 4, 2026 12:43
Version-bump-only scope: 2.x is a Rust-core engine rewrite behind an
unchanged Node API. Real work is the hologit-drop blob-write migration +
two documented byte re-baselines (integer underscores, markdown bodies).
The three cache workarounds are ported, not deleted — gitsheets#184 (the
per-sheet refresh API that would let them go) is open and not in 2.x.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upgrade gitsheets ^1.4.1 → ^2.2.0. The 2.x line rewrites the
core in Rust (via @gitsheets/core-napi) but keeps the Node public
API surface (openRepo/openStore/Sheet/Transaction) unchanged.

Command run: npm install gitsheets@^2.2.0 -w apps/api

hologit is dropped as a transitive dependency in 2.x; it no longer
appears in the lockfile. Code migration (avatar blob-write path) is
in the next commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gitsheets 2.x drops hologit as a dependency. Replace the
BlobObject.write(hologitRepo, buf) pattern with the 2.x surface:

  const blob = await repo.writeBlob(buffer: Buffer)  // → BlobHandle
  await sheet.setAttachments(record, { 'name.jpg': blob })

Sites migrated:
- apps/api/src/routes/people.ts: avatar upload (original + 128 thumb)
- apps/api/scripts/import-laddr/importer.ts: legacy avatar + blog-media

The `as unknown as string` casts are removed — 2.x writeBlob takes
a Buffer natively, matching the actual runtime types throughout.

The three workarounds for gitsheets#184 (swapPublic in store.ts,
git cat-file in attachments.ts, data-repo-lock.ts) are unaffected
and confirmed to still compile. They are kept as-is pending upstream
resolution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gitsheets 2.x (Rust core) refuses to marshal a null- or undefined-
valued field to TOML — `serializeRecords`/`upsert` throw "cannot
marshal JS value of type Null/Undefined to a TOML value". gitsheets
1.4.1 (`@iarna/toml`) silently dropped such keys, so they were never
written to disk (verified against the on-disk `published` snapshot —
no record carries a null-valued key).

Our Zod schemas mark optional fields `.nullable().optional()` and the
write services normalize cleared fields to `?? null`, so under 2.x
every write of a record with a cleared optional field threw a 500.
This surfaced as 16 test failures across write-api, people-lifecycle,
and import-laddr (all "cannot marshal ... Undefined/Null").

Fix: wrap the per-sheet Standard Schema validator in openPublicStore
so the validated record has null/undefined-valued keys stripped
(recursively) before it reaches the core marshaller. gitsheets runs
the validator host-side and marshals its output, so this is the
single authoritative write boundary. The result is byte-identical to
1.4.1's on-disk form: an absent optional field is an absent TOML key.

This is not one of the two documented 2.x re-baselines (integer
underscores, markdown bodies) — it's an undocumented marshal-contract
change in the Rust core that the upgrade plan did not anticipate.

Adds a focused store test asserting null-valued keys are dropped and
present fields survive (also pins the integer-underscore re-baseline).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion

Record the undocumented null/undefined marshal-contract change found
during the bump (Rust core throws where 1.4.1 dropped the key) and the
stripNullish fix at the write boundary. Check off the validation
checklist; note no test needed a re-baseline update.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The `not.toContain('null')` check tripped on the fixture's own
"Nullish Person" / "nullish-person" strings. The three field-absence
regexes above already verify null-valued keys aren't written; replace
the broad substring check with `not.toMatch(/=\s*null\b/)` — no field is
assigned a bare null value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@themightychris themightychris merged commit fec376e into develop Jul 4, 2026
1 check passed
@themightychris themightychris deleted the chore/gitsheets-2x-bump branch July 4, 2026 17:51
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