Skip to content

docs(releases): the 17.2.0 and 17.3.0 upgrade checklists, written from a measured upgrade - #15369

Merged
hotlong merged 2 commits into
mainfrom
claude/issue-15322-pass2-upgrade-checklist
Sep 4, 2026
Merged

hotlong merged 2 commits into
mainfrom
claude/issue-15322-pass2-upgrade-checklist

Conversation

@hotlong

@hotlong hotlong commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #15322

Pass 2 of a two-pass card, and the last one. Pass 1 landed as PR #15333 and is on main; this branch builds on it and does not re-touch anything it wrote.

Baseline shas, both read from origin/main at worktree creation:

sha what it is
pass 1's merge 19be9dfcd docs(releases): 17.2.0 and 17.3.0 detail sections, and repair the upgrade entry page (#15322) (#15333)
this branch's base dc46c4ec1 origin/main tip at git worktree add; 19be9dfcd verified an ancestor of it

What this delivers

The card's remaining half: the two Upgrade checklists on content/docs/releases/v17.mdx, and the structural documentation defects in content/docs/upgrading.mdx that two checklist tables could not have repaired.

The spine is a real upgrade of an application repository across one minor, driven as a customer: the reader was allowed the docs site, the CHANGELOG.md inside each published npm tarball, and the os CLI's own output, and nothing else. Its headline reading was that the documentation covered zero of the eleven breakages that run hit.

content/docs/releases/v17.mdx

  • ## Upgrade checklist gains ### 17.2.0 and ### 17.3.0; pass 1's interim paragraph (the one that sent readers to the per-change Migration notes instead) is deleted.
  • The section lead is now a provenance frame rather than a promise of completeness.
  • Two operational hard requirements are written in as steps, because skipping either one breaks a deployment: deduplicating autonumber columns before the new unique indexes can build on an existing database (the driver neither fails the boot nor skips silently — it logs on the error channel, and the constraint is simply not enforced until you act), and OS_PLATFORM_OWNER_EMAIL on walled deployments, without which AuthPlugin.init() refuses to start.
  • The dev-admin lockout (17.3.0: the seeded dev admin cannot sign in — objectstack dev prints credentials that return 401, and with invite_only the default there is then no way into the app at all #15337) is written as a known issue with no workaround, not as a step, and with no prediction about which release might carry a remedy. It says what is affected, what the symptom is, that no workaround was found, and where to follow along.

content/docs/upgrading.mdx — the four structural defects

Defect What the page now says
1 — no npm/dependency upgrade path at all New "Moving the dependency pins": the shape create-objectstack scaffolds had no documented upgrade action, only a Docker tag and a systemd artifact swap. Three steps — bump every @objectstack/* pin as one set (including devDependencies, because @objectstack/cli is the binary that runs the author-time rules), regenerate and commit the lockfile, and check specVersion / engines.protocol, which move only at a major boundary.
5 — "same major means move the tag and restart" New "A runtime move can still force metadata edits", narrowed by measurement rather than by assertion: os validate and os build refused a stack that compiled on the previous release, a lint family widened from 0 findings to 429, and an inert declared constraint started being enforced. The two-halves table at the top no longer answers the metadata question with a bare "no".
6 — os migrate meta saying "Nothing to migrate" reads as "you are done" The limit is now stated next to the command, in the section about what the command does not do. It is scoped to the protocol chain and silent about author-time rules, lint families, flipped defaults and newly-enforced constraints — measured, it printed that line and exited 0 while nine separate changes were live on that stack.
7 — the per-package CHANGELOG.md files are authorised only for v10/v11 New "The per-package changelogs are the exhaustive record". I picked point every upgrader at them over compile them into the release page, and said why on the page: the release pages are deliberately triaged (17.3.0 alone carries 862 entries), so compiling everything would destroy the property that makes them readable, while the changelogs already ship inside every tarball the reader has installed. The two are complementary, and the page now says which question each answers.

The v17.3.0 / v17.2.0 rows in the per-release table repoint from the Migration-note anchors to the new checklists.

The scope fact this PR is built around

⛔ The drill measured one hop: 17.2.0 → 17.3.0. The card was filed believing the app was pinned at 17.1.0 and crossing two minors; that premise was corrected on the card, and I did not build on it. Nobody has walked 17.1.0 → 17.2.0.

So the two lists are kept apart, and the page says so in as many words:

  • 17.3.0 — every line marked measured was walked on the real upgrade using published artifacts only. Several of those lines are changes whose own changelog entries are not marked breaking (delete ctx.input.field becoming real, the undeclared-key refusal, unknown flow functions, the widened i18n family), which is exactly why a checklist is not a restatement of the breaking-changes section. Lines that the run did not reach — the SDK namespace rename, the record-share backfill, the loud permission-store failure — are marked not exercised and cite the change's own Migration note.
  • 17.2.0 — the whole list is marked not exercised, with a warning at the top of it: accurate about what changed, unproven about what it costs to cross.

A step nobody has run, presented beside steps that were, is how a reader finishes a checklist and believes they are done — which is the exact harm the drill recorded under its sixth documentation defect.

Transcript drift

Four pages printed Running author-time rules (41)...; the registry resolves 42 (AUTHORING_RULES has 42 entries, 39 declaring ALL and 3 declaring ['validate', 'build'], so authoringRulesFor('validate').length is 42 — matching what the CLI printed on the measured run). The drill named two of the four pages; leaving the other two stale would have left the identical claim in place, so all four moved. Number only — no surrounding prose was touched, and no new gate was built here. Nothing currently reads these transcripts, which is filed back to the PM as an out-of-scope finding rather than fixed in this card.

Gate readings — actual numbers

All 42 runnable gate commands were derived at the final commit 1207c1312 by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (the script reads the change set from the merge base itself; it is not fed a hand-made file list). Each ran with its output redirected to a file before the exit code was captured, so no pipe could rewrite a status. 41 of 42 exit 0.

The two the card asked for by name:

✅ check-doc-anchors --self-test: slug parity, custom ids, duplicate counters,
   extraction discrimination and both finding classes verified (305 live fragment links)
✅ check-doc-anchors: 305 internal #fragment link(s) across 410 source file(s)
   all resolve to a real heading
node scripts/check-release-section-coverage.mjs             exit 0   (the PR-blocking form)
node scripts/check-release-section-coverage.mjs --self-test exit 0
node scripts/check-release-section-coverage.mjs --strict    exit 1   1 finding — see below

305 fragment links is up from the 302 pass 1 measured, and the gate resolves cross-file fragments, so it is what proves the six new inbound anchors land — #1720 and #1730 from the per-release table, plus #moving-the-dependency-pins, #a-runtime-move-can-still-force-metadata-edits, #the-loop and #what-it-does-not-do--read-the-output.

The single non-zero is pre-existing and inherited, measured on both trees rather than argued. A detached compare worktree at base dc46c4ec1 reports exit 1, 1 finding; this branch reports exit 1, 1 finding, with byte-identical text. Both name content/docs/releases/index.mdx — a file this diff does not touch, because its repair is a separate card (#15332) already dispatched to another seat. The compare worktree was removed after the measurement. Only release-coverage-patrol.yml:203 passes --strict; the PR-blocking invocation at lint.yml:2729 is the non-strict form, which exits 0 here.

⚠️ One honest non-reading: running check-doc-anchors.mjs in that compare worktree answered PREREQUISITE NOT MET / exit 3 (github-slugger is not installed in a fresh worktree). That measured nothing and is reported as neither a pass nor a failure; the branch's own green above stands on its own.

Changeset

None, deliberately. The diff publishes nothing from any package — it is six files under content/docs/ — so this wants the skip-changeset label, which the PM applies. ⛔ Not an empty changeset: an empty frontmatter file is rejected, and an all-empty set stalls the release silently.

Guardrail note

content/docs/releases/ is release-owned and must never be a rider on a code PR. This is the guardrail's own escape hatch, used as written — a dedicated docs-only PR, no code, no drive-by edits — and it is the vehicle the card was filed to use.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m

Generated by Claude Code


Generated by Claude Code

…ured upgrade

Pass 2 of #15322. Pass 1 (#15333) wrote the 17.2.0 / 17.3.0 detail sections and
repaired the upgrade entry page's per-release table; this pass writes the two
Upgrade checklists that were left open, and answers the structural defects the
upgrade drill found in `upgrading.mdx`.

The spine is a real upgrade of an application repository across one minor,
17.2.0 -> 17.3.0, driven as a customer using only published artifacts: the docs
site, the CHANGELOG.md inside each npm tarball, and the CLI's own output. Its
headline reading was that the documentation covered zero of the eleven breakages
that run hit.

content/docs/releases/v17.mdx
- `## Upgrade checklist` gains `### 17.2.0` and `### 17.3.0`; pass 1's interim
  paragraph is removed.
- Provenance is marked per line. The drill covered ONE hop. Nobody has walked
  17.1.0 -> 17.3.0, so every 17.2.0 line is marked "not exercised" and kept in
  its own list rather than blended with measured steps.
- Two operational hard requirements are written in: deduplicating autonumber
  columns before the new unique indexes can build on an existing database, and
  `OS_PLATFORM_OWNER_EMAIL` on walled deployments.
- #15337 (the dev-admin lockout) is written as a known issue with no workaround,
  not as a step.

content/docs/upgrading.mdx
- New "Moving the dependency pins": the npm-consumer upgrade path the page did
  not have at all, which is the shape create-objectstack scaffolds.
- New "A runtime move can still force metadata edits": the page's "move the tag,
  restart" framing, narrowed by measurement rather than by assertion.
- "Nothing to migrate" is qualified next to the command it comes from.
- The per-package CHANGELOG.md files are authorised for every upgrader, not only
  for v10/v11, with the reason the release pages cannot replace them.
- The v17.3.0 / v17.2.0 rows repoint at the new checklists.

Transcript drift: four pages printed `author-time rules (41)`; the registry
resolves 42 for both `validate` and `build`. Number only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Sep 4, 2026
@hotlong hotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026 — with Claude

hotlong commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

复核 — PASS(内容与范围),两个开放问题已裁

⚠️ 同会话自审披露:本 PASS 与本卡两趟的派发、前提修正、以及第一趟的裁决出自同一会话(session_01UHvF5hyiZjnCyExFnfQB8m)。本轮我复核的是范围守约与读数可信度,不是重新独立推导内容。请人工复核者按同会话基线折价。

我自己验的三条(不是复述你的正文)

我查的 读数
有没有碰 releases/index.mdx(会撞 #15332 的席位) 没有。六个文件是 v17.mdx、upgrading.mdx 和四个转录页
已发布的 17.0.0 / 17.1.0 与第一趟的详细段有没有被"顺手整理" v17.mdx 的 diff 只有两处:清单节的 lead,和 ## References 之前的纯插入。没有动到既有散文
转录 41 → 42 是抄来的还是验过的 你从 AUTHORING_RULES 自己数的(42 条 = 39 ALL + 3 ['validate','build'],authoringRulesFor('validate').length = 42),不是从演练日志抄的数。这一条我特意看,因为抄一个别人测的数正是这张卡在修的缺陷形状

范围守约我逐条对过:未演练的标注、两条运维硬要求进步骤、#15337 写成已知问题且不预测哪个版本会修、只改数字不造新门禁——⛔ 全部守住。

你比派发单多做对的一件事:我只点了两个转录页(演练发现的那两个),你查出是四个,并且说明了理由——修两个会让完全相同的断言原样留在另外两个页面上。对的。

那七条,是这一趟的真正价值

十一个实测破坏点里有七条(delete ctx.input.x、未声明键拒绝、未知 flow 函数、ctx.dispatch、FORBIDDEN 散文、i18n 扩面、CLI 封子路径)在第一趟的详细段里一个字都没有 —— 因为它们自己的 changelog 条目没有自标 BREAKING,第一趟的分流看不见它们。

这句话应该被记住:清单不是 breaking-changes 段落的复述。 第一趟按「自标 BREAKING」分流是正确的做法,而这七条证明了那个口径有系统性盲区——破坏性由后果决定,不由作者当时勾没勾那个框决定。这是这次演练最贵的产出之一,比补两张表贵得多。

裁决 Q1 —— 17.2.0 清单整体「未演练」

裁 A(照现状接受),你的推荐我采纳,理由也认同。 补三点让它不至于烂在这里:

  • 不立卡。 立一张「等某天有 17.1.0 客户」的卡,只会变成一张永远不动的开卡。改为触发条件:一旦出现真实的 17.1.0 部署,就跑同一套文档优先演练,把那些行从「未演练」升级为「实测」。我记在这里,不占一个卡号。
  • ⛔ C 是错的,而且错在两个方向:它既丢掉了准确的逐变更指引,又拿走了 17.1.0 读者唯一的起点。你说得对。
  • ⛔ 不许为了拿到「实测」标签去做一次合成演练。 这次演练的价值全部来自它是真应用的真升级;为挣一个标签而走一遍,产出的是最弱的一类证据,却要付一整轮成本。标签的意义在于它说真话,不在于它好看。

裁决 Q2 —— 转录漂移门禁

裁 A,已立卡 #15373,连同你 grep 出来的证据(scripts/ 下无一处提及 author-time rules、四个页面的具体路径)。卡里把 C 写成「在那张卡里评估、不预设」,按你自己那句怀疑:check:corpus-claim-drift 是对措辞的共现棘轮,一个钉在 registry 导出上的数值断言未必套得进它的模型。

我在卡里把它和 #15332 并置:一周之内两类"陈旧字面量"缺陷,都是被偶然撞见而不是被门禁抓到的 —— 那才是要修的东西。

你那条没做去重搜索的旁发现,我已经先立了

@objectstack/lint 那条「corpus 上 0 findings」而点名的对象实际会触发规则的事——已立卡 #15357,在你交报告之前。你没搜到是对的:那时它还不存在。⛔ 不用再动它。

下一步

CI 收敛后由我翻 ready 并武装 auto-merge。⛔ 你不要动。你没有自己武装、没有碰 index.mdx、把该留给 PM 的两件事(标签、立卡)真的留住了——三条纪律都守住了。

Check Changeset 已在 13:13:57 绿(标签赶在那一跑之前打上,没有多烧一圈)。


Generated by Claude Code

…attributed

The Callout claimed 17.3.0 shipped an unrecoverable sign-in lockout. That is
false, and it was a false alarm on a customer-facing page.

Root cause of the misattribution: the server that produced the reading was
started as `objectstack serve --ui` under NODE_ENV=production, not `objectstack
dev`. The dev-admin seed is by design not armed in that shape --
`isDevAdminSeedArmed()` returns false whenever NODE_ENV is not 'development' --
so the account was never created and the 401 was correct behaviour. An ablation
holding tree, published install and database path fixed and varying only the
start mode reproduced the 401 on the serve/production leg and answered 200 on
the dev leg; three further independent shapes all answered 200. The two readings
that made it look like a platform defect (a transplanted hash having no effect,
an authenticating user missing from the database) were measurement artefacts of
`rm -rf .objectstack/data` run against a live server, which left the process
holding deleted file descriptors.

Two sites removed, both introduced by this branch:

- the "Known issue" Callout at the head of the 17.3.0 checklist, in full;
- the trailing half of the audience-posture bullet, which existed only to
  explain why the lockout was total and carried a now-dangling back-reference.
  The bullet keeps its changelog-sourced first half and is marked "not
  exercised"; the "measured only as a non-change" claim came from the same
  retracted run and goes with it.

Nothing is added in their place. Whether the `os dev` / `os serve` difference in
dev-admin seeding deserves a sentence anywhere is referred to the maintainer; it
is not a 17.3.0 change and would not belong in an upgrade checklist.

check:doc-anchors still reports 305 internal fragment links across 410 files,
unchanged -- the deleted block's only link was an absolute URL, not an anchor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m

hotlong commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

裁决:撤稿后的那个 open question —— 什么都不加

你问的是要不要留一句关于 os dev 与 os serve 在 dev-admin 播种上差别的真话。裁:不加。 三条理由,按分量排:

  1. 它不是 17.3.0 引入的。 isDevAdminSeedArmed() 的 NODE_ENV !== 'development' 门在 17.2.0 上同样成立 —— 这正是根因席位那个消融证明的东西(同一棵树、同一个安装,只变启动模式)。一条跨版本不变的行为,不属于升级清单;清单是给「从 A 到 B 要做什么」的读者看的。
  2. ⛔ 它会把一次归因错误固化成产品文档。 加进去,读者会以为这里曾经有过一个坑;实际上坑在测量方法里,不在产品里。文档不该替一次测错留下纪念碑。
  3. 这一趟的口径是只写实测过的东西。 我们没有测过「serve 与 dev 在播种上的差别对升级者意味着什么」—— 我们只是排除了一个误报。⛔ 拿排除误报的副产物去写一条指引,正是这张卡从头到尾在防的形状。

⚠️ 如果这个差别本身值得写,它的位置是 deployment/ 或 os dev 的命令文档,不是 v17 的升级清单,而且要有人先去测它对客户意味着什么。我不为此立卡:目前没有任何证据说有人被它绊倒过 —— 唯一那次是我们自己的测量失误。⛔ 立一张"也许有用"的卡,只会变成一张没人动的开卡。

撤稿本身:PASS

你 grep 出的第二处是我没点到的 —— audience-posture 那条 bullet 的后半截,删了 Callout 之后会变成悬空引用。这正是我要求"扫全文、不要只改记得的那一处"想防的东西,你做到了。

更值得说的是这个判断:那句「measured only as a non-change」也出自被撤回的同一次演练,你把它一并撤了而不是留着。同一次读数里只信对自己方便的那一半,是撤稿最容易犯的错。 你没犯。

check:doc-anchors 删前删后都是 305、并且你说清了原因(那段里唯一的链接是绝对 issue URL,从来不在内部 fragment 计数里)—— 这比只报一个"仍然 305"有用得多,因为它排除了"数字没变是因为两处抵消了"。

你最后那句自我记录

我复述了症状,没有复核「这到底测到了什么」。清单里每条 measured 标签,担保的应该是后者。

这句话是对的,而且这不是你一个人的错:演练席位写了那份日志,我读了它、把它当 P0 转达给了维护者,你在它基础上写了文档 —— 三道都过了,没有一道追问「这台服务器到底是怎么起的」。⛔ 责任不在最后接手的那一环。

真正可复用的教训是那条我写进 #15337 的:一个 A/B 对照的价值取决于两边只差一个变量;而命令行与环境变量本来可以从 /proc/<pid>/cmdline 和 /environ 直接读出来,不必靠回忆自己敲了什么。

CI 在 1d594aeb4 上收敛后由我翻 ready 并武装。⛔ 你仍然不要动。


Generated by Claude Code

@hotlong
hotlong marked this pull request as ready for review September 4, 2026 13:54
@hotlong
hotlong enabled auto-merge September 4, 2026 13:54
@hotlong
hotlong added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit a12b15e Sep 4, 2026
32 of 33 checks passed
@hotlong
hotlong deleted the claude/issue-15322-pass2-upgrade-checklist branch September 4, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(releases): v17.mdx has no 17.3.0 section and no Upgrade checklist for 17.2.0 either — two releases shipped without upgrade guidance

2 participants