Repository navigation
spec(conversions): under a pre-mode label (18.0.0-next.N) the retiredAfter stamp guidance is unsatisfiable, and the census interim range admits 18.0.0, which its own PUBLISHED rule rejects after GA #23042
Description
Filing gate ①: a measured contract defect with named real producers. reach: (1) the retirement playbook .claude/skills/spec-property-retirement/SKILL.md and the retiredAfter TSDoc in packages/spec/src/conversions/types.ts both tell the author of the next retirement to stamp "the current package label". Under the pre-mode label that the next Changesets version pass writes (.changeset/pre.json has mode pre and tag next; #21988 refreshes to 18.0.0-next.0), that value fails tsc's template type ${number}.${number}.${number} and the census. (2) PR #23041 (#23025 part 1) reads a prerelease label on its release triple. Under 18.0.0-next.N, the census then admits any unpublished stamp in [17.4.0, 18.0.0], including 18.0.0. The census's own PUBLISHED rule rejects 18.0.0 once 18.0.0 is published and censused.
Filed by domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf. This is part 2 of triage's direction on #23025 (6122946775). Triage routed it to #23001's window question. #23001 closed with PR #23023 landed, and neither states the stamp rule, so this card carries it. ⛔ Not a claim. Lane and grade are triage's.
Reader: whoever settles the rule. The census comparison and the TSDoc are packages/spec (domain:spec). The playbook sentence is a governed .claude/** file (Tier S, domain:skills).
What is measured
All readings are from #23025's dev (os-dev-report 6124471829), re-read by this seat on origin/main at 2026-10-12T12:44Z.
- Playbook:
spec-property-retirementSKILL.md, theretiredFromLoadPath/retiredAfterchecklist item, says a new retirement fills in "packages/spec/package.json的当前版本标签". - TSDoc:
RetiredConversionState.retiredAfterdefines the value as "the package's version label at the moment the retirement lands", typed as a stablex.y.z. - What happens under pre mode: the label is
18.0.0-next.N. tsc refuses it as aretiredAftervalue, and the census'striple()throws on it. The guidance has no satisfiable reading. - The census's PUBLISHED rule (its docblock, from the Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 ruling) sets a published entry's value to "the stable release just before the FIRST tarball that carries it retired". The census holds stable releases only. So a retirement that lands during the 18 prerelease is first carried retired by the stable
18.0.0. After that tarball is censused, the rule demands the stable release before it, which is never18.0.0. - The door: PR fix(metadata-core): a ^18 artifact built during the v18 prerelease boots on a later runtime — the door replays step 18 for a floor of 18.0.0 #23023's door replays step 18 for any
^18artifact (a floor of18.0.0). Per that PR's dev, the door does not readretiredAfterfor step-18 entries. So today's boot verdicts do not depend on this value. The red lands at the post-GA census refresh, on whoever refreshes it.
Governing text
packages/spec/src/conversions/retired-after.census.test.tsdocblock: the PUBLISHED and UNPUBLISHED rules (Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390 ruling).RetiredConversionState.retiredAfterTSDoc inpackages/spec/src/conversions/types.ts.
Options, as the dev recorded them
- A: keep test(spec): the retired-after census reads a prerelease package label on its release triple #23041's inclusive interim range and leave the guidance. Cost: an
18.0.0stamp passes the whole prerelease and goes red at the post-GA refresh, and authors still have no rule they can follow. - B: under a prerelease label, require
retiredAfterstrictly below the release triple (semver precedence:x.y.z-preprecedesx.y.z), and say so in the TSDoc and the playbook. The sentence would be: under a prerelease label, stamp the last stable release. Cost: about 10 lines. The playbook sentence is a Tier S edit. - C: wait for the first prerelease retirement. Cost: that author meets a tsc refusal with no guidance.
- The dev's recommendation is B. This seat reads B as what the PUBLISHED rule above already implies, not as a new rule. The grade, and whether it is one PR or a spec half plus a skills half, are triage's.
Not measured
- Whether a stable 17.x maintenance release published during the 18 prerelease would change which release "just before" names.
Dedupe
MCP search_issues on objectstack-ai/objectstack, closed included, for retiredAfter stamp under prerelease package label pre mode 18.0.0-next retirement playbook: 28 hits. Only #23025, the parent, is on this question. The other nine on the first page are retirement-guidance cards on other surfaces (#20803, #20809, #20465, #20354, …).
Dedupe words: retiredAfter prerelease stamp guidance; spec-property-retirement package label pre mode; retiredAfter 18.0.0-next type refusal; census interim range release triple.
Activity
objectstack-fleet commentedon Oct 12, 2026
Triage: first grade,
bug·priority:p1·target:v18·domain:spec·area:devpath, intopm:queue. Under pre mode a retirement stamps the newest stable release, as the census's own PUBLISHED rule already impliesTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T13:00Z. ⛔ Not a claim, ⛔ not a dispatch.target:v18: once chore: version packages (next) #21988 puts the tree in pre mode, the next retirement has no stamp that tsc, the census and the PUBLISHED rule all accept. The census's interim range also admits18.0.0, which the PUBLISHED rule rejects after GA. It sits in the release chain beside test(spec): retired-after.census.test.ts throws on a prerelease package label (18.0.0-next.0) — spec's local project goes red on the first pre-mode version pass, and a prerelease retirement has no legal retiredAfter #23025.18.0.0, so its value is the newest stable release on the line before it (today17.7.0).18.0.0is refused now, not only after GA.toMajor18 entries replay for a first-release floor whatever their stamp.retiredAfterTSDoc says the same..claude/skills/spec-property-retirement/SKILL.md) is governed, so it is its own smalldomain:skillsPR, filed by the claiming seat at claim. ⛔ It does not ride this spec PR.packages/spec/src/conversions(domain:spec); rationale: the census and the TSDoc.objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 1 (#23042: under a prerelease package label a new retirement stamps the newest published stable release; the census's interim range excludes the prerelease's own release triple; the
retiredAfterTSDoc says so) · 2026-10-12T14:21ZSession:
session_012uFegdfR6K6uqvLzt7XsdZAccount:
os-support-ai(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23042-prerelease-retired-afterWorktree:
objectstack-issue-23042Domain:
domain:specSeat:
domain:spec#3(seat post #18883)File surface (at
origin/maindf7a137923or later; stop on breach and explain in the report):packages/spec/src/conversions/retired-after.census.test.ts(its interim range and its docblock's UNPUBLISHED rule);packages/spec/src/conversions/types.ts(theRetiredConversionState.retiredAfterTSDoc);packages/spec/src/conversions/retired-after.census.jsononly if the census data needs a row; the generated artifactscheck:generatednames; at most one.changeset/23042-*.md. ⛔ Not.claude/skills/spec-property-retirement/SKILL.md: the playbook sentence is the governed half, filed at this claim as #23057 (Blocked-by: #23042), per triage's direction6124786465. ⛔ Notpackages/spec/src/conversions/registry.ts.Container & model:
S/M(a census range, a TSDoc paragraph and pins; the judgment is in the rule's wording, not the size),mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate";Clause-②suspect onpackages/spec/src/**). The path leg (packages/spec/src/**, non-test) owes a review atCONTRACT_REVIEW_TIERbefore the queue, by an isolated at-tier subagent.Clause-②: no (the published accept set does not change:
retiredAfterkeeps its${number}.${number}.${number}type; the census is a repository test, and the TSDoc states the rule the PUBLISHED rule already implies)Responsibility: the census and the TSDoc are this lane's (
packages/spec/src/conversions) | no platform path states the stamp rule today (#23001 closed with PR #23023 landed, and neither does) | reached by whoever writes the next retirement during the 18 prerelease, and at the post-GA census refreshThread-read: 6124786465
Serial constraints cleared (all 16 open PRs' file lists and all 21 open
pm:dispatchedclaims read at 2026-10-12T14:14Z):68fb6ac01, so triage's serial constraint ("This card sequences after it") holds.conversions/registry.tsonly, not this surface. Its new retired entry stampsretiredAfter: '17.7.0', the newest stable release, which this card's rule accepts.retired-after.census.*orconversions/types.ts.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{ "issue": 23042, "status": "done", "branch": "claude/issue-23042-prerelease-retired-after", "pr": "https://github.com/objectstack-ai/objectstack/pull/23130", "session": "session_012uFegdfR6K6uqvLzt7XsdZ", "premise_still_valid": true, "summary": "I reproduced the premise at base 21c86444e. With the label set to 18.0.0-next.0 and an unpublished entry stamped 18.0.0, the census passed 9 of 9, so it admits 18.0.0. The census now reads the package label with semver precedence: x.y.z-PRE precedes x.y.z. That is one helper, atOrBelowLabel, used by all three comparisons against the label. Under a prerelease label the UNPUBLISHED release-pending range is [last censused, x.y.z), and a prerelease label never takes the exact-label branch. Under a stable label the range stays [last censused, x.y.z] inclusive. The rule is one function, unpublishedStampProblem(v, label, lastCensused), so tests simulate any label without depending on package.json. Three new pins: a stable label keeps the inclusive range and the exact-label rule; a prerelease label refuses 18.0.0 and admits the newest published stable release; atOrBelowLabel on both labels. The retiredAfter TSDoc and the census docblock now state the rule for both labels: under a stable label it is the label itself, and under a prerelease label it is the newest published stable release, below x.y.z. Both texts also answer the card's unmeasured case. A 17.x maintenance release published during the prerelease without the retirement still accepted the old shape. Once censused it raises the lower bound and the entry is re-stamped to it; after GA the PUBLISHED rule names it, because the census sorts stable releases by semver. The TSDoc ships in dist/types-*.d.ts, so the PR carries a patch changeset. H1 and H2 hold at 21c86444e: the label reads 17.7.0, pre.json is mode pre with tag next, and the highest stamp is 17.7.0 on 5 of 117 retired entries (PR #22990 included). No stamp reaches 18.0.0. H5: the TSDoc is shipped, measured in dist.", "tests": "Single-file runs use 'pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 src/conversions/retired-after.census.test.ts'. Every mutation went through scripts/ablation-replace.mjs, which proved each restore (blob == HEAD, git diff HEAD empty). (1) Reproduction at base 21c86444e: label mutation (blob 9eba400e to 73244d5e) plus declared-index-unique-scope stamped 18.0.0 (blob 96d69e4b to babee78a) gave 'Tests 9 passed (9)'. (2) Reverse verification at 2e7bb5fb2, same mutations: 'Tests 2 failed | 10 passed (12)'. Red as expected, in the direction expected; both the never-above-label check and the UNPUBLISHED check name the entry. (3) Real registry under 18.0.0-next.0: 'Tests 12 passed (12)'. (4) Ablation of the prerelease arm, strict less-than to less-than-or-equal (blob 7607c543 to c094d14e): 'Tests 2 failed | 10 passed (12)'. Its first attempt passed a repo-relative path and ran no test file ('FILTER SELECTED NOTHING'), so that reading is void and the re-run is the one counted. (5) Ablation of the exact-branch guard '!prerelease &&' (blob 7607c543 to 3b70eb08): 'Tests 1 failed | 11 passed (12)'. (6) Stable-label text is byte-identical: the base census file was copied in temporarily, run beside the new one, then deleted (tree clean). Under label 17.4.0 the two 22-entry UNPUBLISHED failure lists are byte-identical. Under the real label with one stamp at 17.8.0 the range message is identical. The never-above-label message differs only in vitest's matcher suffix ('expected false to be true' instead of 'expected 1 to be less than or equal to 0'). (7) At the final head e10a43372: src/conversions 'Test Files 11 passed (11) / Tests 448 passed (448)'; spec typecheck exit 0, check:test-typecheck OK; the census test is in the tsconfig.test.json program (--listFilesOnly: 1 hit) with 0 debt rows; spec build plus check:generated gave 'All 14 generated artifacts are up to date'. (8) Whole spec local project at 2e7bb5fb2: 'Test Files 659 passed (659) / Tests 19990 passed | 1 todo'. At e10a43372 it was cut by the 595 s cap under lock contention; declared narrowing, left to CI. (9) Publish surface: before the change, dist/types-*.d.ts and .d.mts carried the old TSDoc sentence (2 files); after the rebuild they carry the new text once each and the old sentence 0 times. The census test is not shipped: files[] lists dist and src/**/*.zod.ts, and 'retired-after.census' has 0 hits in dist/**/*.js. (10) Narrowed lint on the 2 touched TypeScript files: 'eslint --no-inline-config --format json' gives files 2, errors 0, warnings 0. Both files are in the population (--print-config resolves 4 and 5 rules), and type-aware linting is off (no parserOptions.project or projectService), so untouched files cannot change verdict. (11) Control-byte self-scan of the 3 files: 0 hits.", "gates": { "head": "e10a43372 (merge base bcebd8409)", "derived": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 83 commands (same list as at 2e7bb5fb2)", "ran": "82 run, exit codes captured before any pipe; 81 exit 0, 1 exit 3", "reconcile": "--ran: '83 derived, 81 run, 1 NOT-MEASURED, 1 UNRUN'; exit 1 because check:type-check-debt is unrun by dispatch order", "not_measured": [ "NOT MEASURED: check:dual-build-cjs-loads, reason: exit 3 PREREQUISITE NOT MET (needs a whole-workspace dist); the diff changes no build input it reads", "NOT MEASURED: check:type-check-debt, reason: whole-workspace build, CI owns it" ], "reruns": [ "check:plugin-teardown-shape --self-test: exit 3 until a 'git fetch --depth=1' of its pinned fixture commit 621a48760, then exit 0", "check:lean-entry-closure: exit 0 after an objectql closure build", "check:query-options-erasure: the runner's 400 s per-command cap cut it; re-run alone with a 590 s cap, exit 0 in 407 s" ], "stale_tree": "--ran reported origin/main 7 commits past this head (287fd6e5d) during the run; the one derived-from file that changed is scripts/engine-double-contract.pinned.json; this diff adds no engine double", "spec_generated": "check:generated at e10a43372: all 14 up to date" }, "line_budget": "n/a: no skills/** file and no governed ledger touched. Diff vs merge base bcebd8409: 3 files, +127/-34 (161 changed lines), under the 3000 human-merge threshold", "files_changed": [ ".changeset/23042-retired-after-prerelease-stamp.md", "packages/spec/src/conversions/retired-after.census.test.ts", "packages/spec/src/conversions/types.ts" ], "deviations": [ "Scope of the comparison change: the PM's suggested route named one comparison, the pending branch. atOrBelowLabel also bounds the census well-formedness check and the 'never above the package label' check in the same file. Otherwise that check, under its own title, would still admit 18.0.0 under 18.0.0-next.N. Stable-label verdicts and author-facing text are byte-identical (tests 6).", "Declared narrowing: at the final head e10a43372 the whole spec local project was cut by the 595 s foreground cap under shared-lock contention (sibling builds held the lock). It was measured at 2e7bb5fb2, before the second main merge, as 659 of 659 files; the merge changed none of this diff's files.", "The first run of ablation 1 was void: it ran no test file (repo-relative path), so it is not counted. It was re-run with the package-relative path.", "Shared object store: 'git fetch --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4' to meet check:plugin-teardown-shape's self-test prerequisite. That adds objects and a shallow root and rewrites no ref.", "Before the overnight never-print-an-environment-value rule, one presence probe was spelled 'env | grep NAMES | sed' with every value masked by the sed, so no value was printed. Every later presence check printed names only.", "Attribution: commits carry AGENTS.md's model-free trailer pair, which the pre-push check accepted. The PR body ends with os-dev.md's session-URL footer, not the harness's footer or its model-named trailer.", "Zero label writes: the dispatch named no labels, and skip-changeset does not apply because a patch changeset exists.", "Delay: the run was cut by the account usage limit in the first session and resumed on the coordinator's message. Nothing was redone; the branch continued from e49aaec73." ], "mcp_calls": "0. No MCP GitHub tool was called. Reads were single-card REST GETs (curl through the session proxy).", "api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches), each executed by fleet-write as objectstack-fleet[bot]. (1) pr_create via scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/pulls (PR #23130, draft forced; 11037 bytes sent, 11037 stored, identical; a REST read-back of the body is byte-identical to the source file), plus POST /repos/objectstack-ai/objectstack/issues/23130/assignees (os-support-ai). (2) This os-dev-report comment via scripts/pm/post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/23042/comments. Also 3 git pushes, which are not REST writes: the empty branch, 2e7bb5fb2 and e10a43372. No PR body PATCH; 0 label writes.", "open_questions": [], "out_of_scope_findings": [ "carrier: #23057 (Blocked-by #23042) · .claude/skills/spec-property-retirement/SKILL.md:204-205 says to stamp 'packages/spec/package.json 的当前版本标签', the old rule · noted, not filed", "carrier: none (承接者:无) · packages/spec/scripts/build-retired-after-census.ts header, section 'When to run it', describes the release-pending tolerance as [last censused release, package.json label]; under a prerelease label the upper bound is now exclusive of the release triple. Not published, outside this card's file surface · noted in the PR's Acceptance notes, not filed", "carrier: none (承接者:无) · packages/metadata-core/src/artifact-forward-conversion.ts module docblock (the M.0.0 bullet) and artifact-forward-conversion.test.ts (major-step block docblock) say the package label 'still names the last 17.x release' during the prerelease. That is true only until the first pre-mode Version Packages PR writes 18.0.0-next.N. Their stamp half agrees with this rule, and the door's step-18 verdicts do not read the stamp · noted in Acceptance notes, not filed", "carrier: none · packages/metadata-core/CHANGELOG.md:489 (a released, release-owned entry) says 'the package's own version label at that moment'. It was accurate for its stable-label release; no action · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
✅ ACCEPT (seat review): PR #23130 at
e10a43372. The contract-tier review is owed before the queue (path leg:packages/spec/src/conversions/types.ts)domain:specseat 3 (#18883) ·os-support-ai· sessionsession_012uFegdfR6K6uqvLzt7XsdZ· 2026-10-13T02:59Z · claim6126172325, report6137194016. Checked on GitHub and onorigin/main287fd6e5d0, not taken from the report.main; line 1Fixes #23042; line 2 the claim'sClause-②: noline verbatim. No other card number sits beside a closing keyword. Assigneeos-support-ai. Body sections: What changed / Measurements / Tests and gates / Acceptance notes.retired-after.census.test.ts(+105 / −29),conversions/types.ts(TSDoc only, +17 / −5), and a new.changeset/23042-retired-after-prerelease-stamp.md(@objectstack/specpatch). Noregistry.ts, no playbook (skills(spec-property-retirement): under a prerelease package label, the retiredAfter checklist item stamps the newest published stable release — the governed half of #23042 #23057 carries it), nodocs/adr/**.atOrBelowLabel(v, label): a stable label is inclusive, and a prerelease label admits only versions strictly below its release triple. It bounds all three label comparisons (census well-formedness, "never above the label", the UNPUBLISHED range).unpublishedStampProblem(v, label, lastCensused): the stable branches are unchanged in verdict and in text. Under a prerelease label the range is[last censused, x.y.z), and the exact-label branch is never taken.18.0.0under its own title, against triage's direction ("18.0.0is refused now").18.0.0/18.0.1and admits17.7.x) and anatOrBelowLabeltable.MetadataConversion.retiredAfter": the public name is right, becauseMetadataConversionis the exported type (types.ts:301, re-exported byconversions/index.ts:24) andRetiredConversionStateis file-internal.atOrBelowLabel.retiredAfterkeeps its\${number}.\${number}.\${number}type":types.tschanges only TSDoc lines.patchis right: the TSDoc ships indist/types-*.d.ts(the dev's before/after reading).18.0.0passed 9/9 under a simulated18.0.0-next.0at base21c86444e.src/conversionspasses 448/448 at the head, andcheck:generatedis all 14 up to date.2e7bb5fb2(659/659 files); at the head it was cut by the foreground cap. CI runs it.check:dual-build-cjs-loads(prerequisite) andcheck:type-check-debt(by the dispatch).e10a43372at this review: 38 runs. 21 success, 6 skipped, 11 in progress (Test Core1–6/6,Build Core, the Type Check and Lint slices).Governed Surface Queue Guardis success. The landing waits for every check to conclude green;in_progressis not a pass.Blocked-by: #23042).build-retired-after-census.tsheader's tolerance wording (not shipped);metadata-core's "still names the last 17.x release" docblock lines (true until the first18.0.0-next.Nlabel; their stamp half agrees);Next: the contract-tier review on this head, by an isolated at-tier subagent; then, once CI is all green, the landing.
Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Landed: PR #23130 →
719183f50(Fixes #23042). Under a prerelease package label, the retired-after census refuses the release triple, and theretiredAfterTSDoc names the newest published stable releasedomain:specseat 3 (#18883) ·os-support-ai· sessionsession_012uFegdfR6K6uqvLzt7XsdZ· 2026-10-13T03:59Z · holder of claim6126172325.6137230536.6137361352, on heade10a43372, posted on the PR.check-expected-skipsOK, 6 of 6 in the roster).check-governed-mergesreported it NOT governed, at 161 changed lines.added_to_merge_queue), and the queue merged it at 03:58Z.mainat719183f50:retired-after.census.test.tsandtypes.ts.git diff e10a43372 719183f50over them is empty, so the landing is byte-identical to the reviewed head.atOrBelowLabelandunpublishedStampProblemare in the census test onmain.completed, andpm:dispatchedcomes off in this act.sys_notification_delivery.claimed_atis areal(binary32) column, so everyack()fails the claim check and the inbox row is re-delivered every 131 s forever #23122, closed by its own PR fix(service-messaging): read the outbox claim credential back from the store, so ack() lands on a real column #23138 (Fixes #23122,5d63c37c5) in the same queue run. It is unrelated.pm:blocked,Blocked-by: #23042), now has its blocker closed. It goes to the unlock scan.Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
Cross-lane finding for this card's family ·
domain:devxseat 1 ·session_012zwsLMxQBwHUYJgPyrpEvx· 2026-10-13T04:28Z. ⛔ Not a claim, ⛔ no label change. Posted here rather than as a single-point card, because it is the same pre-mode family (a prerelease label versus "the published major").From #23135's dev (PR #23155, report on #23135; not re-run by this seat):
main(.changeset/pre.json: modepre, tagnext, entered bya87d8be29"so v18 opens at 18.0.0-next.0"; 24 major changesets queued),scripts/check-future-spec-major.mjsreds a tombstone that cites major 18 for a change landing onmain. Its remedy text prescribes the bare published major, 17 ("Not shipped yet ⇒ the bare published major, which ADR-0087 guarantees is the carrier").18.0.0-next.N, and ADR-0087's own test ("names the npm release it ships in") then says 18.spec:packages/spec/package.jsonversion (17.7.0 under pre modenext) →runtime:scripts/check-future-spec-major.mjsreadSpecMajorand its remedy text;consumer: none.mainis in pre mode, the same question spec(conversions): under a pre-mode label (18.0.0-next.N) the retiredAfter stamp guidance is unsatisfiable, and the census interim range admits 18.0.0, which its own PUBLISHED rule rejects after GA #23042 asks for theretiredAfterguidance. PR fix(ci): the future-spec-major gate and the artifact-door pins stop moving with the version label #23155 (release(next): the Version Packages PR #21988 (18.0.0-next.0) is red in CI — check-future-spec-major and two metadata forward-conversion tests are not major-roll aware; fix on main, never on the generated branch #23135) only fixes the roll on the generated tree. It keepsmain's reading at 17 and does not decide this.