Skip to content

fix(spec): dist-freshness refusal derives package label and build remedy from pkgDir - #11471

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-11250-dist-freshness-label
Aug 23, 2026
Merged

fix(spec): dist-freshness refusal derives package label and build remedy from pkgDir#11471
os-steve merged 1 commit into
mainfrom
claude/issue-11250-dist-freshness-label

Conversation

@os-steve

@os-steveos-steve commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Fixes#11250

What

inspectDistFreshness() / inspectBundleFreshness() (packages/spec/scripts/lib/dist-freshness.ts)
take an arbitrary pkgDir, but their refusal cause strings and the printed
pnpm --filter PKG build remedy line hardcoded packages/spec / @objectstack/spec
regardless of it.

  • Both cause strings now interpolate a packages/NAME display label derived from
    pkgDir's own path (packageDirLabel — finds the last packages path segment and
    takes the one after it; falls back to the raw pkgDir when the shape doesn't match).
  • The build-remedy line now reads pkgDir/package.json#name (packageName — falls back
    to the same directory label when the file is missing or unparsable), since that's what
    pnpm --filter actually resolves against, not the directory label.
  • The rerun line (the caller's own re-run command) is unchanged — see "Decisions"
    below.

Why

Every real caller passed SPEC_DIR until #10969 gave check:skill-examples a second
surface (packages/client-react / packages/client), so a stale-dist refusal on that
surface printed a wrong package name in both diagnostic lines.

Live confirmation (issue comment, 2026-08-23 15:29Z): while working #11026, the
refusal printed

❌ packages/spec/dist holds no .d.ts declarations — the package is not built …

with packages/spec/dist holding 44 fresh .d.ts files — the actually-unbuilt packages
were client and client-react (dts=0 each). Following the printed remedy
(pnpm --filter @objectstack/spec build) verbatim rebuilds an already-fresh package and
reds again identically — the message was not merely mislabelled, it was non-actionable
for the surface it fired on.

Decisions

  • Cause label + build-remedy line: derived from pkgDir. Both now trace to the real
    stale package, whichever surface fired.
  • rerun line at the check-skill-examples.ts call site: left as-is. The issue asked
    me to weigh per-surfacing it. check-skill-examples.ts passes one fixed
    'pnpm --filter @objectstack/spec check:skill-examples' for every surface it loops
    over — but that command is correct for every surface: check:skill-examples itself
    is a packages/spec script that type-checks all three surfaces (skills+docs,
    packages/spec/src, and the client SDK) in one run, so re-running it is the right fix
    regardless of which surface's dist was stale. Per-surfacing it would print a command
    that doesn't exist (there is no check:skill-examples that runs only the client-SDK
    surface). So the printed remedy is actionable as shipped: build the named package, then
    re-run the one real gate command.

Tests

  • dist-freshness.test.ts: updated the two pinned-literal assertions that hardcoded
    packages/spec text against the (non-packages/spec-shaped) sandbox fixture to
    assert against the dynamically-derived label instead, and added a package.json seed to
    the one case that needs the build-remedy line to keep naming @objectstack/spec.
    Added two new cases:
    • a non-spec-shaped pkgDir (packages/widgets, package.json#name = @acme/widgets)
      proving both the cause label and the build-remedy line are genuinely derived, not a
      surviving packages/spec hardcode with different test data;
    • the packageName fallback branch (no package.json at all) falls back to the
      directory label rather than throwing.
  • dist-freshness-adoption.test.ts (the end-to-end suite covering
    check:dual-source-exports / check:exported-any / check:skill-examples against real
    spawned gate runs) is unaffected — every one of its cases exercises tree.spec, a
    genuinely packages/spec-shaped sandbox with a real @objectstack/specpackage.json,
    so the dynamically-derived label there is correctlypackages/spec before and after
    this change.

Verification transcript (at ca8116159855bc936c3b443666bd73caa3653b6e)

  • pnpm --filter @objectstack/spec build — exit 0.
  • pnpm --filter @objectstack/spec exec vitest run scripts/dist-freshness.test.ts scripts/dist-freshness-adoption.test.ts --reporter=verbose --maxWorkers=225/25 passed (15 + 10). Also ran the full pnpm --filter @objectstack/spec test once (419 files / 11163 tests, all green) — an accidental over-broad run (a stray -- defeated the vitest path filter), kept only as extra confirmation, not the intended scope.
  • Reverse verification: with packages/spec/scripts/lib/dist-freshness.ts checked out from origin/main (pre-patch) and the new/updated test bodies unchanged, 4 tests fail as expected — most tellingly, the non-spec-shaped case fails with expected '\n❌ packages/spec/dist/**/*.d.ts is O…' to contain 'OLDER than packages/widgets/src', i.e. the pre-patch code reproduces the exact live-confirmed bug (prints packages/spec for a packages/widgetspkgDir). Restored the fix from HEAD afterward and confirmed byte-identity (git hash-object on the working-tree file equalled git rev-parse HEAD: plus the path) before re-running the suite green (25/25).
  • node scripts/check-cross-package-test-inputs.mjs --self-test && node scripts/check-cross-package-test-inputs.mjs — both exit 0 (104/104 self-test cases; OK: 14 package(s) … all declared).
  • eslint on both touched files (--no-inline-config --format json) — 0 problems.
  • Gate list derived at HEAD via node scripts/pm/dispatch-gates.mjs (not hand-picked) — 18 path-derived + 4 convention-triggered (test-file-add) local gates identified. Ran all of them: 21 green (check:changeset-gate-self-tests, spec check:empty-state/check:liveness/check:strictness-ledger/check:variant-docs, check:merge-driver, check:objectui-changeset, check:published-files, check:slot-lookup, check:test-source-alias, check:type-source-resolution, check-adr-0087-registration, check-changeset-no-major, check-ci-filter-parity, check-empty-changeset, check-plugin-teardown-shape, check-affected-docs, check:query-options-erasure, check:type-check-coverage, check:engine-double-contract, check:where-matcher). 2 red for the same declared reason: check-dev-prereqs and check:type-check-debt --re-measure both refuse outright ("NOT MEASURED", not a false verdict) because this worktree has only @objectstack/spec built, not the full 67-package closure their measurement needs — check:type-check-debt names the exact remedy itself: pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*'. That full-workspace build is a lint.yml-CI-side precondition, not something this diagnostic-text-and-tests-only diff can affect either way; declared here rather than silently skipped.

Clause-② confirmed not triggered: diff touches only packages/spec/scripts/lib/dist-freshness.ts
and packages/spec/scripts/dist-freshness.test.ts — no packages/spec/src/**, no accept/reject
contract change, diagnostic text + tests only.


Generated by Claude Code

…edy from pkgDir
inspectDistFreshness() / inspectBundleFreshness() hardcoded packages/spec in
their refusal cause strings and the pnpm --filter @objectstack/spec build
remedy line, regardless of pkgDir. Every real caller passed SPEC_DIR until
#10969 gave check:skill-examples a second surface (packages/client-react /
packages/client) -- confirmed live: a stale-dist refusal on that surface
misnamed the stale package and printed a non-actionable remedy.
Both cause strings now interpolate a packages/<name> label derived from
pkgDir's own path, and the build-remedy line reads pkgDir/package.json#name
-- both falling back sensibly when the shape doesn't match / the file is
unreadable. dist-freshness.test.ts's pinned literals are updated to match,
and two new cases prove the derivation is genuine: a non-spec-shaped pkgDir
(packages/widgets, package.json name @acme/widgets), and the package.json-
missing fallback.
Fixes#11250
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9cDbY2NBiVJWYx3BpWfH2
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 23, 2026
@os-steve
os-steve marked this pull request as ready for review August 23, 2026 21:13
@os-steve
os-steve added this pull request to the merge queueAug 23, 2026
Merged via the queue into main with commit 64baa68Aug 23, 2026
36 checks passed
@os-steve
os-steve deleted the claude/issue-11250-dist-freshness-label branch August 23, 2026 22:26
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] inspectDistFreshness()'s refusal message hardcodes "packages/spec/dist" regardless of the pkgDir argument

2 participants

@os-steve@claude