Skip to content

Three more dist-reading spec gates carry the same unenforced freshness precondition as gen:api-surface #7181

Description

@os-help

Out-of-scope finding from implementing #7122 (PR pending), recorded per PD #10. Unassigned, not queued. Duplicate search: no open issue matches "dist freshness precondition" or "exported-any stale dist"; in:title dist returns only #7122 itself.

#7122's third suggestion asked whether any other dist-reading generator has the same unenforced precondition. It does — three, and they were deliberately left out of that PR to keep it scoped.

The three

Each resolves its public entry points to the built dist/** declarations with the same collectEntries shape #7122 fixed, and each documents the precondition in prose while asserting nothing:

script npm script docblock line
packages/spec/scripts/check-dual-source-exports.ts check:dual-source-exports ":46 — Reads the built dist — run after pnpm --filter @objectstack/spec build."
packages/spec/scripts/check-exported-any.ts check:exported-any ":28 — asked structurally instead, from the built .d.ts a consumer's import resolves to"
packages/spec/scripts/check-skill-examples.ts check:skill-examples ":51 — Because it reads the built dist/*.d.ts, this runs AFTER the workspace build"

Verified on origin/main @ 5087ac6: zero reads of distIsStale, .build-input-hash, or any other freshness signal in all three. Positive control on the same scan — distIsStale matches in check-generated.ts and (after #7122) in lib/dist-freshness.ts, so the scanner is not returning zero for the wrong reason.

Why this is filed as an observation and not as #7122's severity

#7122's damage came from the WRITE: gen:api-surface commits a baseline that deletes a live export, and check:api-surface then agrees with it against the same stale dist. All three above are check-only — none writes a tracked artifact, so none can launder a wrong baseline into a commit. check:exported-any and check:skill-examples each carry an anti-vacuity floor of their own, and check-dual-source-exports.ts reads a hand-maintained baseline rather than regenerating one.

What remains is a false verdict on a stale dist, and it is local-only: CI runs all three after the build step, so the stale condition cannot arise there. The exposure is a developer running one of them directly in a worktree whose dist predates their edit — the same tree #7122 measured — and reading a verdict about a build nobody made.

Cheap fix, if triage wants it

#7122 landed the primitive as packages/spec/scripts/lib/dist-freshness.ts. Adopting it in each of the three is an import plus a four-line guard before the first .d.ts is read, with mode: 'check'. The wording it prints already says "a verdict now would be computed against a build that no longer matches src", which is exactly these three cases.

Worth grading rather than assuming: the mode argument would want a third value, since none of these three is gen-shaped and the current check wording names check:api-surface by hand.

Refs: #7122, #4687, #5726, #5864.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions