Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .changeset/dev-prereqs-stale-dist-gate.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
---
"@objectstack/spec": patch
---

fix(devx): gate `pnpm dev` on a STALE `packages/spec/dist`, not only a missing one (#5864)

`check:dev-prereqs` (PR #5863) asserted that every workspace package's declared
`dist/` entry point exists. That covered one half of #5726 — a **missing**
artifact, which fails loudly — and left the other half ungated: a dist that is
present but **stale**, which does not fail at all. It lies, and it lies about
somebody else's code. #5726's 20+ TypeScript errors read exactly like real
contract drift while `isAppResolvedDefaultToken` was exported from `src/` the
whole time and merely absent from a stale `packages/spec/dist`.

Worse, the existence gate made that half slightly more misleading than before:
the developer was told the workspace was fine seconds before the fake drift
appeared, so a green line was vouching for something it had never checked.

**The definition, so a gate can decide it.** `packages/spec`'s build now records
a sha256 of its own build inputs into `packages/spec/dist/.build-input-hash` as
its last step; `check:dev-prereqs` recomputes that hash and compares. Stale ⇔
the two differ. Inputs are everything under `src/`, the package manifest, the
package's own tsconfig/tsup config, and turbo.json's `globalDependencies` —
read from turbo.json rather than restated, so the build's own declaration of a
global input is also the gate's.

**Content, never mtime.** PR #5863 refused this half because comparing source
mtimes against `dist` false-reds after any checkout, and a gate that cries wolf
on day one gets switched off. A content hash is immune to all of it — `git
worktree add`, `git checkout`, restored backups, clock skew, `touch`. Verified
on the real tree: a source file rewritten with identical bytes and an mtime one
hour in the future (so `src` is strictly newer than `dist`) stays green.

**Scope, stated rather than implied.** Freshness is asserted for `packages/spec`
alone — AGENTS.md §9's stale-artefact table names exactly one dist that presents
as *other people's* contract drift. The pass line now says which claim is which:
existence for all 67 packages, freshness for the one. An unstamped amplifier
dist is red rather than a warning, because that is precisely the tree #5726 was
run on, and a gate that cannot find its freshness input has verified nothing
(#4690).

**For consumers of `@objectstack/spec`:** no API, type or runtime change. The
published tarball gains one 65-byte file, `dist/.build-input-hash`, which is the
build's own input digest and is read only by this repo's dev gate.
9 changes: 9 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -397,6 +397,15 @@ Even inside your own worktree, operate defensively:
None of this is CI-visible: CI checks out fresh and installs clean. It costs
only *your* time, which is exactly why it is worth recognising in one step
rather than re-diagnosing per gate.

**One row of that table now has a gate, and only one** (#5864). `pnpm dev`
refuses to boot on a stale `packages/spec/dist`: its build stamps a hash of
its own inputs into `dist/.build-input-hash`, and `check:dev-prereqs`
recomputes and compares. Content, never mtime — so a checkout, a `touch` or
a clock skew cannot false-red it, and `git worktree add` is invisible to it.
For every other row this prescription is still the whole remedy: the gate's
pass line says "existence, not freshness" about the other ~66 packages
precisely so its green cannot be read as vouching for them.
10. **A clean merge is not a working merge — but scope the re-check to the
overlap.** Git conflicts on overlapping lines; nothing warns you when two
changes are individually fine and jointly wrong. Real examples from one
Expand Down
2 changes: 1 addition & 1 deletion packages/spec/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -182,7 +182,7 @@
"spec-changes.json"
],
"scripts": {
"build": "pnpm gen:schema && pnpm gen:openapi && tsup && if [ -z \"$OS_SKIP_DTS\" ]; then NODE_OPTIONS=\"--max-old-space-size=12288\" BUILD_DTS=true tsup; fi",
"build": "pnpm gen:schema && pnpm gen:openapi && tsup && if [ -z \"$OS_SKIP_DTS\" ]; then NODE_OPTIONS=\"--max-old-space-size=12288\" BUILD_DTS=true tsup; fi && node ../../scripts/check-dev-prereqs.mjs --stamp",
"dev": "tsc --watch",
"clean": "rm -rf dist",
"gen:schema": "OS_EAGER_SCHEMAS=1 tsx scripts/build-schemas.ts",
Expand Down
Loading
Loading