Skip to content

Commit 2672f85

Browse files
feat(devx): 给 check:dev-prereqs 补上「陈旧 dist」判定(内容指纹,非 mtime) (#5864) (#6892)
check:dev-prereqs 此前只判存在性,#5726 的另一半——dist 在盘上但内容陈旧——仍无门禁, 而绿灯还替它作了一次没挣来的反向保证。 packages/spec 的 build 现在把自身构建输入的 sha256 打进 dist/.build-input-hash, 门禁重算并比对:两者不等即陈旧。判据是内容而非 mtime,所以 git worktree add / git checkout / touch / 时钟偏移都不会误报——这正是 PR #5863 拒绝做这半边的原因。 Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8828b9e commit 2672f85

4 files changed

Lines changed: 558 additions & 73 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
fix(devx): gate `pnpm dev` on a STALE `packages/spec/dist`, not only a missing one (#5864)
6+
7+
`check:dev-prereqs` (PR #5863) asserted that every workspace package's declared
8+
`dist/` entry point exists. That covered one half of #5726 — a **missing**
9+
artifact, which fails loudly — and left the other half ungated: a dist that is
10+
present but **stale**, which does not fail at all. It lies, and it lies about
11+
somebody else's code. #5726's 20+ TypeScript errors read exactly like real
12+
contract drift while `isAppResolvedDefaultToken` was exported from `src/` the
13+
whole time and merely absent from a stale `packages/spec/dist`.
14+
15+
Worse, the existence gate made that half slightly more misleading than before:
16+
the developer was told the workspace was fine seconds before the fake drift
17+
appeared, so a green line was vouching for something it had never checked.
18+
19+
**The definition, so a gate can decide it.** `packages/spec`'s build now records
20+
a sha256 of its own build inputs into `packages/spec/dist/.build-input-hash` as
21+
its last step; `check:dev-prereqs` recomputes that hash and compares. Stale ⇔
22+
the two differ. Inputs are everything under `src/`, the package manifest, the
23+
package's own tsconfig/tsup config, and turbo.json's `globalDependencies`
24+
read from turbo.json rather than restated, so the build's own declaration of a
25+
global input is also the gate's.
26+
27+
**Content, never mtime.** PR #5863 refused this half because comparing source
28+
mtimes against `dist` false-reds after any checkout, and a gate that cries wolf
29+
on day one gets switched off. A content hash is immune to all of it — `git
30+
worktree add`, `git checkout`, restored backups, clock skew, `touch`. Verified
31+
on the real tree: a source file rewritten with identical bytes and an mtime one
32+
hour in the future (so `src` is strictly newer than `dist`) stays green.
33+
34+
**Scope, stated rather than implied.** Freshness is asserted for `packages/spec`
35+
alone — AGENTS.md §9's stale-artefact table names exactly one dist that presents
36+
as *other people's* contract drift. The pass line now says which claim is which:
37+
existence for all 67 packages, freshness for the one. An unstamped amplifier
38+
dist is red rather than a warning, because that is precisely the tree #5726 was
39+
run on, and a gate that cannot find its freshness input has verified nothing
40+
(#4690).
41+
42+
**For consumers of `@objectstack/spec`:** no API, type or runtime change. The
43+
published tarball gains one 65-byte file, `dist/.build-input-hash`, which is the
44+
build's own input digest and is read only by this repo's dev gate.

AGENTS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,15 @@ Even inside your own worktree, operate defensively:
412412
None of this is CI-visible: CI checks out fresh and installs clean. It costs
413413
only *your* time, which is exactly why it is worth recognising in one step
414414
rather than re-diagnosing per gate.
415+
416+
**One row of that table now has a gate, and only one** (#5864). `pnpm dev`
417+
refuses to boot on a stale `packages/spec/dist`: its build stamps a hash of
418+
its own inputs into `dist/.build-input-hash`, and `check:dev-prereqs`
419+
recomputes and compares. Content, never mtime — so a checkout, a `touch` or
420+
a clock skew cannot false-red it, and `git worktree add` is invisible to it.
421+
For every other row this prescription is still the whole remedy: the gate's
422+
pass line says "existence, not freshness" about the other ~66 packages
423+
precisely so its green cannot be read as vouching for them.
415424
10. **A clean merge is not a working merge — but scope the re-check to the
416425
overlap.** Git conflicts on overlapping lines; nothing warns you when two
417426
changes are individually fine and jointly wrong. Real examples from one

packages/spec/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@
182182
"spec-changes.json"
183183
],
184184
"scripts": {
185-
"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",
185+
"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",
186186
"dev": "tsc --watch",
187187
"clean": "rm -rf dist",
188188
"gen:schema": "OS_EAGER_SCHEMAS=1 tsx scripts/build-schemas.ts",

0 commit comments

Comments
 (0)