Skip to content

fix(spec): the default-change ratchet authorises from the baseline major, so the version branch major roll stops refusing rows declared under 17 - #23123

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-23113-spec-build-next
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-23113-spec-build-next

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23113

Clause-②: no

Unblocks #21988, the Version Packages PR for 18.0.0-next.0. The release branch is not edited, and neither the smoke nor any workflow is touched.

The failing line

I read it from the job log of run 38323903662, job 115058075855 (Packed-tarball smoke (release candidate), step 10 Build). The tree is changeset-release/main at 9a73a93f1, with @objectstack/spec@18.0.0-next.0:

❌ 10 authorable key(s) changed the DEFAULT they apply when the author omits them:
     - api/InstallPackageRequest:enableOnInstall: true → (none)  (removed)
     - api/PackageInstallRequest:enableOnInstall: true → (none)  (removed)
     - data/Cube:public: false → true  (changed)
     - kernel/InstallPackageRequest:enableOnInstall: true → (none)  (removed)
     - marketplace/MarketplaceInstallRequest:enableOnInstall: true → (none)  (removed)
     - marketplace/Package:visibility: "private" → "org"  (changed)
     - system/MetricsConfig:slis: (none) → []  (added)
     - system/TracingConfig:sampling: (none) → {"rules":[],"type":"always_on"}  (added)
     - ui/PaginationConfig:pageSize: 25 → 50  (changed)
     - ui/UserActionsConfig:editInline: false → true  (changed)

   Baseline: upstream 85c6d76ec42a
  • The exit site is packages/spec/scripts/build-schemas.ts:3352 on 9a73a93f1: the default-change refusal, with its message at :3317.
  • The exit is not silent. The message was printed, but it sits at log lines 2371–2466, after turbo's own ERROR … exited (1) lines at 1639–1641. The rest of the task's output is flushed after turbo reports the failure, so reading only from [System] to turbo's error line shows no error.

Root cause

  • authoriseDefaultChanges (packages/spec/scripts/lib/authorable-defaults.ts:392) admitted DEFAULT_CHANGES_BY_MAJOR rows only at CURRENT_MAJOR, which is Number.parseInt of package.json's version (build-schemas.ts:767).
  • The stated premise was: "By the time a major rolls, the baseline already carries the new value."
  • That premise fails on the version branch:
    • The smoke checks out at depth 1, so merge-base HEAD origin/main cannot answer. The gate re-anchors on baseRev 85c6d76ec42a, at spec 17.4.0. That anchor is allowed to lag.
    • The version pass rolls the branch to major 18.
    • The ten keys main changed after 85c6d76ec42a are all declared under 17: in scripts/lib/default-changes.ts.
    • A window holding only major 18 has no rows, so all ten read as undeclared.
  • Control: same anchor, same mechanism, one major earlier.
    • The last green smoke, run 38247570907 (12:12Z), built 17.8.0 (HEAD is now at d00f5f2 chore: version packages). It re-anchored on the same 85c6d76ec42a and printed 📌 9 declared default change(s) accepted (#4666).
    • The first red, 38248923046 (12:25Z), built 18.0.0-next.0 (d85ff7c chore: version packages (next)), on the same main sha 4267484fe5.
    • The tenth key, ui/UserActionsConfig:editInline, landed on main in between.
  • Not the prerelease label. Number.parseInt('18.0.0-next.0', 10) is 18, which is correct. The input that changed is the major itself.

The fix

The authorising window now runs from the baseline tree's own protocol major up to this build's major (authorisingMajors):

  • Where the baseline major comes from. readProtocolMajorAtRev reads it from package.json at the baseline rev, the same rev the baseline defaults are read from. The in-tree baseline is this tree, so it uses this build's major.
  • Chaining. Each major's rows chain in ascending order, so contiguity and the chain-tip rule still apply across the roll. Rows older than the baseline's major still authorise nothing.
  • Fallbacks. If the baseline major is unreadable, the window falls back to this build's major alone, which is the old and narrowest window, and the build says so. If the baseline is at a newer major than the build, the window is this build's major alone.
  • Visibility. Whenever the window spans more than one major, the build prints it.
  • On main (baseline 17, build 17) the window is [17], the same as before this change.

Where the change lands: packages/spec/scripts/lib/authorable-defaults.ts, packages/spec/scripts/build-schemas.ts, packages/spec/scripts/lib/default-changes.ts (header comment only) and packages/spec/scripts/authorable-defaults.test.ts.

  • The producer is the gen:schema script's own library under scripts/lib/, which falls inside the declared surface ("the spec build's gen:* scripts").
  • No packages/spec/src/** file is touched.
  • build-openapi.ts needed no change: the spec build passes through it in proof (b).

Proofs

All runs went through scripts/pm/os-verify-lock.sh.

(a) The failure reproduces on 9a73a93f1, unmodified.

  • Setup: a fresh git clone --depth=1 --branch changeset-release/main, the same shape as the smoke's checkout (HEAD 9a73a93f1c56, shallow).
  • Command: pnpm --filter @objectstack/spec run build.
  • Result: VERDICT command-exit 1. The run re-anchors (shallow history … anchors on 85c6d76ec42a), then prints ❌ 10 authorable key(s) changed the DEFAULT… with the same ten keys and Baseline: upstream 85c6d76ec42a.

(b) The same tree passes with this fix applied locally, uncommitted.

  • Setup: the patch git diff 21c86444e 7f04259ce applied with git apply to the same clone. The tree shows four M paths and HEAD is still 9a73a93f1.
  • Spec build: pnpm --filter @objectstack/spec run build gives VERDICT command-exit 0. It prints:
    • ℹ️ authorable-defaults/ baseline (upstream 85c6d76ec42a) was read at protocol major 17, and this build is major 18, so DEFAULT_CHANGES_BY_MAJOR rows at majors 17–18 authorise
    • 📌 10 declared default change(s) accepted
    • then gen:openapi, tsup and DTS, check-dts-emitted (38/38), check-dts-references, and the dev-prereqs stamp, all passing.
  • Whole-repo build: the smoke's Build step (turbo run build --filter=!@objectstack/docs, run here at --concurrency=2) gives Tasks: 73 successful, 73 total, VERDICT command-exit 0.
  • After the build, the tree carries no tracked change beyond the four patched files.

(c) main still builds @objectstack/spec green, on this branch (7f04259ce, off main 21c86444e).

  • Command: pnpm --filter @objectstack/spec run build gives VERDICT command-exit 0, with 🔒 authorable-defaults/ verified against upstream 21c86444ec6e — 1121 default(s) unchanged. The window is a single major, so the notice is not printed.
  • git status is clean afterwards.

Ablation (the fix was committed first; done with scripts/ablation-replace.mjs):

  • Mutation: for (const major of authorisingMajors(majors)) replaced by for (const major of [majors.current]). The mutation landed: anchor 1 → 0, blob 68f3f7ea8795 → b2f5507f9afb.
  • Result: authorable-defaults.test.ts gives 3 failed | 33 passed (36). The three failures are the rolled-major acceptance, the cross-roll chain and the cross-roll chain-tip tests. The red direction was expected.
  • Restore: blob == HEAD (68f3f7ea8795), git diff HEAD empty.

Tests and gates (all at 7f04259ce)

  • pnpm --filter @objectstack/spec exec vitest run --project local scripts/authorable-defaults.test.ts: 36 passed: the 27 existing tests (their 10 authoriseDefaultChanges calls now pass the majors object), plus 9 new tests covering the window, the roll and readProtocolMajorAtRev.
  • The spec local suite: Test Files 659 passed (659), Tests 19996 passed | 1 todo.
  • pnpm --filter @objectstack/spec run typecheck: exit 0. That covers tsc --noEmit, check:scripts-typecheck and check:test-typecheck. tsc -p tsconfig.scripts.json --listFiles lists all four changed files.
  • Gate commands: 80 in total. That is the union of the 74 derived at dispatch and the 62 derived on this branch, with 56 in both. Every final exit is 0.
    • node scripts/pm/dispatch-gates.mjs --ran answers ✓ 62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED.
    • Three gates first exited 3 (PREREQUISITE NOT MET). Each was re-measured green after its prerequisite was supplied:
      • check-plugin-teardown-shape --self-test: its pinned fixture commit was absent from the shallow clone. That one commit was fetched.
      • check:dual-build-cjs-loads and check:lean-entry-closure: no package dist/. The repo was built first (73 tasks).
  • Lint, narrowed to the changed files: eslint --no-inline-config --format json on the four files reports 4 results, 0 errors, 0 warnings, and no file is ignored. eslint.config.mjs never enables type-aware linting (no parserOptions.project), so this diff cannot move a verdict on any untouched file. The repo-wide pnpm lint is CI's to run.

Changeset: skip-changeset

The diff is confined to packages/spec/scripts/**, which is not in packages/spec's files[]. After the build, the shipped paths dist, json-schema, liveness, prompts, llms.txt, README.md, CHANGELOG.md, api-surface and src/**/*.zod.ts were grepped:

  • authorisingMajors, readProtocolMajorAtRev, DefaultChangeMajors and authoriseDefaultChanges have 0 hits.
  • The positive control, defineStack, hits 86 files in dist.

The change decides the gate's verdict and its console output. It changes no emitted artifact, and the tracked artifacts are unchanged after the build.

Pin

The next Publish Smoke after the fix lands is green on the release branch. The release refresh carries this fix onto changeset-release/main, and the seat reads that run after landing. Locally, the smoke steps after Build (pack, fresh install, auth and CRUD) were not run, so they are NOT MEASURED here.

Acceptance notes

  • Inferred, not measured: once chore: version packages (next) #21988 lands, main itself would be at major 18 while baseRev still names 85c6d76ec42a (17.4.0). Every depth-1 build of packages/spec would then re-anchor and reach the same refusal. The fix covers that case by the same mechanism.
  • Environment observation, not filed: under a checkout path deep enough that tsx's IPC socket path passes the 108-byte Unix limit, tsx fails with EADDRINUSE. A repro clone under the agent scratchpad hit this during pnpm install's prepare. A shorter path cleared it. No public door is affected.

Generated by Claude Code

…major

`authoriseDefaultChanges` admitted DEFAULT_CHANGES_BY_MAJOR rows at this
build's major only, on the premise that by the time a major rolls the
baseline already carries the new value. Two baselines break that premise:
the version pass rolls `package.json` inside the branch under test, and a
shallow checkout's re-anchored baseline (`baseRev`) lags by design. On the
`18.0.0-next.0` version branch both held - a major-17 baseline, a major-18
build - so the ten default changes main had declared under 17 were refused
as undeclared and `gen:schema` exited 1 in the release-candidate smoke.

The window now runs from the baseline tree's own protocol major (read out
of git at the baseline rev, as its defaults are) up to this build's, with
each major's rows chained in ascending order: contiguity and the chain-tip
rule hold across the roll, rows older than the baseline's major still
authorise nothing, and an unreadable baseline major falls back to this
build's major alone, announced. The widened window is printed whenever it
spans more than one major.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
@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.

What this run could not see

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c9b5b24b0f0457b19132dd2f9870230e42cbd9a5 → packageMentionDocs.

@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 13, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 13, 2026 01:50
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 13, 2026 01:50
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 0d43004 Oct 13, 2026
46 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23113-spec-build-next branch October 13, 2026 02:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests tooling

Projects

None yet

2 participants