chore: cover the published Node floor in CI and record two invisible couplings - #6
Merged
Conversation
…couplings - oxfmt: ignore .claude/ so `format --check` stops failing on editor state that is gitignored globally but not by this repo's .gitignore - ci: add Node 20 to the matrix so the package's engines floor is proven - tsconfig: record how the construction seal reaches the published d.ts - docs: state the equals rule for sibling subclasses, with a test
btravers
commented
Aug 6, 2026
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.
Four small, independent cleanups from a code review. No runtime behaviour
changes.
1.
pnpm format --checkno longer fails on editor state.claude/settings.local.jsonis gitignored via the user's global gitignore(
~/.config/git/ignore), not this repo's.gitignore. oxfmt honours.gitignorefiles but not the global excludes file, so it scanned the file andpnpm format --checkfailed on something that is not part of the repo.Added
.claude/toignorePatternsin.oxfmtrc.json.I checked whether the other tool-state directories need the same treatment by
dropping a deliberately mis-formatted file into each and running the check.
They do not —
.turbo/,coverage/and**/dist/are all in this repo's.gitignoreand were skipped; a control file in a non-ignored directory wascorrectly flagged. So
.claude/was the only gap.Verified before (fails on
.claude/settings.local.json) and after (clean).2. CI now covers the published Node floor
packages/entitydeclaresengines: { node: ">=20" }, but the matrix was'["", "22.19"]'— the floor was declared and never tested.CONTRIBUTING.mddocumented the gap and offered two ways out; this takes the first:
"20"isadded to
node-versions.Also updated the two places that still described the gap: the "Node versions"
section of
CONTRIBUTING.mdand the explanatory comment inci.yml.Caveat, stated plainly: GitHub Actions cannot run locally, so this PR does
not prove the package builds and tests green on Node 20 — it only makes CI
check the claim for the first time. If the Node 20 job fails on this PR, that
is a real finding about the declared floor and should be addressed on its own
terms (fix the incompatibility, or raise
enginesto match what CI proves).It should not be papered over by dropping
20back out of the matrix.3. The construction seal's declaration-emit coupling — and a correction
The review asked for a comment in
packages/entity/tsconfig.jsonrecordingthat
declarationmust stayfalsein the extended base, per theCtorKeycomment in
src/types.ts. That premise turns out to be wrong, so I recordedthe measured behaviour instead of the claimed one:
@btravstack/tsconfig/base.jsonsetsdeclaration: trueanddeclarationMap: true, notfalse. It also setsnoEmit: true, so thetscpass emits nothing either way.--declaration --emitDeclarationOnly), including with an exported entity subclass insrc/, produces no TS4020 on TypeScript 5.9.3 or 7.0.2. TypeScript emitsa
ProbeOrg_basealias andSealedis exported, so the private name isnameable.
The constraint is real, but it lives one level out: a consumer that exports
an entity subclass with
declaration: truedoes hit it against the publishedd.ts. Reproduced on both compilers with the built package:
So the comment I added points at what actually matters: tsdown reads this
tsconfig for its
--dtsemit, which is what shapes the published declarations,and the seal's private names surface as TS4020 in consumers that re-export a
subclass. It also flags that the
src/types.tscomment is stale.Two follow-ups worth their own issues, deliberately not done here:
CtorKeycomment insrc/types.tsshould be rewritten — it attributesthe constraint to a tsconfig setting that is not set the way it says.
library author re-exporting an entity subclass with declaration emit on is
blocked. Exporting
Sealed/BaseInstancefrom the public types would fixit, but that is a design change, not a cleanup.
No settings were changed.
4.
equalsacross sibling subclasses is now specifiedequalstestsother instanceof Base, whereBaseis the class produced byone
Entity(...)call — so two bare subclasses of the same entity compareequal when their data matches. Defensible, but the README only said "the same
entity type", which did not settle it.
Added a sentence to the
equalsbullet in "Instance members" stating the rule(identity is the entity a class was built from, not the class itself), and a
test in
equality.spec.tspinning the sibling-subclass case alongside theexisting "different entity types are unequal" test.
Changeset
Included (
patch). Items 1–3 are repo tooling and carry no user-facing change,but item 4 changes the published README — the documented contract a consumer
reads — so it belongs in the release notes even though the behaviour it
describes is unchanged.
Gate
All green locally:
pnpm format --check,pnpm lint,pnpm typecheck,pnpm test(62 tests, 8 files),pnpm knip,pnpm build.