Uh oh!
There was an error while loading. Please reload this page.
fix(platform,cli,spec): converge runtime docs URLs on the canonical host, and retarget protection docsUrl at a live page - #10935
Conversation
…ost, and retarget protection docsUrl at a live page Two defects sharing one string literal, in the code that emits text to users. The host half is spelling ratification. `docs.objectstack.ai` redirects to `https://objectstack.ai` path-preservingly, so nothing here was broken -- it was the unratified spelling sitting where users copy from. Maintainer ruling, 2026-08-21: 「这个仓的文档站规范 URL 是 https://objectstack.ai」. #10641/#10659 converged the docs-site half (link gate, docs.site.json, Dockerfile, published READMEs); these are the references outside that surface. The path half is a real dead link. All 29 `protection.docsUrl` values pointed at `/adr/0010-metadata-protection`, and `/adr/...` is not a route on any host: `apps/docs/lib/source.ts` mounts `content/docs` under `/docs`, `docs/adr/` is not published, `apps/docs/app` has no `/adr` segment, and `check-docs-redirects` confirms 0 redirect sources outside the `/docs` route space. The slug was wrong too -- the record is `0010-metadata-protection-model.md`, the same filename drift `check-adr-links.mjs`'s header already records. Studio renders this URL in the lock banner, so an operator asking why an item is locked was sent nowhere. Retargeted to `https://objectstack.ai/docs/references/shared/protection` -- the published reference for the very schema carrying the field, and a `/docs/` route that `check:published-readme-links` assertion 3 and `check:docs-redirects` both keep alive. A github.com blob of the ADR was the alternative and was rejected: the consumer is a browser banner aimed at tenant operators, not contributors, and no gate would notice the blob path rotting. `packages/spec/src/shared/protection.zod.ts` is a generated-reference source, so `content/docs/references/shared/protection.mdx` moves with it (one line). `authorable-surface.base.json` is unchanged -- no acceptance face moved. Deliberate survivors of the repo-wide sweep, each owned elsewhere: `skills/objectstack-data/SKILL.md` and the two ADRs (#10656, governed surfaces needing a maintainer-landed PR); the create-objectstack templates (#10321); `scripts/check-published-readme-links.mjs`, whose classifier accepts the aliases by design; and 10 CHANGELOG entries plus #10659's changeset, all historical prose. Fixes#10655 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
📓 Docs Drift Check1 anchor(s) derived from 6 changed package(s); no hand-written page names any of them. ✅ What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 80c0529fb38d63c57e0e3cd8f737423c08de4ea9 && git checkout 80c0529fb38d63c57e0e3cd8f737423c08de4ea9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3d7deb70005ce2297b5b1a311b629106c3c39bb8 baea11c39ca68c6726d1677a20cdac94395dd336 && git checkout -B drift-repro 3d7deb70005ce2297b5b1a311b629106c3c39bb8 && git merge --no-ff baea11c39ca68c6726d1677a20cdac94395dd336
node scripts/docs-audit/affected-docs.mjs --json 3d7deb70005ce2297b5b1a311b629106c3c39bb8 |
os-elon
commented
Aug 21, 2026
Review: ACCEPT (reviewer of record: PM seat domain:spec, session_01B4h3medzvhB9rpfoja9jcw) Verified against GitHub, not the report:
Landing: in the merge queue. On merge: drop Generated by Claude Code |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32537778840 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
…nonical host The scaffolded blank template's README.md linked docs.objectstack.ai (an accepted-but-unratified alias) in three places, while the root README.md already used the ruled canonical origin, https://objectstack.ai. One `npm create objectstack@latest` run therefore handed the newcomer two different documentation hostnames. Maintainer ruling, 2026-08-21: 「这个仓的文档站规范 URL 是 https://objectstack.ai」. scripts/check-published-readme-links.mjs's CANONICAL_DOCS_ORIGIN prescribes the same origin, and #10659/#10935 already converged the docs-site build config and the runtime-emitted docsUrl strings onto it — 02b3b07 explicitly deferred the create-objectstack templates to this issue (#10321). Fixes#10321 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r
Fixes#10655
Follows #10641 / #10659, which converged the docs-site half earlier today (the link
gate,
content/docs.site.json,docker/Dockerfile, the published READMEs). This is thehalf that lives outside that surface: code that emits text to users. Diffed against
current
mainfirst — nothing here re-does or contradicts what #10659 landed.Maintainer ruling, 2026-08-21, verbatim and untranslated:
The routing question, answered first
The card's sequencing constraint was to resolve what serves
/adr/...before movingthe host, so the sweep would not just relocate a dead link. Egress is blocked here, so
this was answered from the repo, using the repo's own resolvers rather than a
re-derivation:
/adr/0010-metadata-protectiondocsRelative()→null— not in the/docsroute space; redirect rescue: none/adr/0010-metadata-protection-modelnull, no rescue/docs/adr/0010-metadata-protectioncontent/docs/docs/references/shared/protectioncontent/docs/references/shared/protection.mdx/docs/references/kernel/metadata-protectionFour independent facts agree that nothing serves
/adr/on any host:apps/docs/lib/source.tsmountscontent/docswithloader({ baseUrl: '/docs' });the only other loader is
blogat/blog.apps/docs/apphas no/adrroute segment — the route dirs are[lang]/docs,[lang]/blog, plusllms.txt/llms.mdx/og/api/search.apps/docs/redirects.mjscarries 0 sources outside the/docsroute space —asserted independently by
check:docs-redirects, whose own success line reads0 outside the /docs route space.adrdoes not appear anywhere inapps/docs/.There was a third defect underneath the other two: the slug was also wrong. The
record is
docs/adr/0010-metadata-protection-model.md; everydocsUrlsaid0010-metadata-protection. That is the same filename driftscripts/check-adr-links.mjsalready records in its header — a link to
./0010-metadata-protection.md, "a file thathas never existed", which sat long enough for a triage comment to inherit the wrong
attribution and repeat it.
The target chosen, and the one rejected
https://objectstack.ai/docs/references/shared/protection.The card allowed either the published reference page or a GitHub blob URL, asking that
the choice be judged against what the
docsUrlconsumer surface actually renders for.It renders for a browser banner shown to tenant operators:
and the TSDoc above it: "Optional documentation link rendered next to the reason in the
Studio lock banner. Use it to point operators at the package's protection policy." The
reader is an operator who just hit a lock in Studio — not a contributor reading the source
tree. So:
that carries this field, it opens with "Package-level metadata protection (ADR-0010
§3.7)", and it explains the two-layer
protection/_lockmodel — which is exactlythe "why is this locked" question the banner is answering.
/docs/destination is covered bycheck:published-readme-linksassertion 3 andcheck:docs-redirects. Agithub.com/.../docs/adr/0010-metadata-protection-model.mdblob is covered by nothingthat would notice a rename — and a rename in exactly this filename is the incident
already on record above.
/docs/references/kernel/metadata-protectionalso resolves but documents the privateruntime envelope, which is not the operator's question.
What moved
44 string occurrences across 35 source files, plus one regenerated line.
packages/platform-objects/src/identity/*.object.tsdocsUrl→ host and pathpackages/platform-objects/src/apps/{account,setup,studio}.app.tsdocsUrl→ host and pathpackages/plugins/plugin-security/src/objects/*.object.tsdocsUrl→ host and pathpackages/spec/src/shared/protection.zod.tspackages/spec/src/shared/protection.test.tspackages/cli/src/utils/spec-version.tsRELEASES_BASE→ host onlypackages/cli/src/utils/spec-version.test.tspackages/apps/setup/src/setup-overview.doc.tspackages/apps/studio/src/studio-overview.doc.tsexamples/app-showcase/src/ui/actions/index.tscontent/docs/references/shared/protection.mdxprotection.zod.tsis a generated-reference source, so the reference was regeneratedwholesale (
gen:schema→gen:openapi→gen:docs;gen:openapire-run becausegen:schema's cleanup clears its output). The regeneration moved exactly one line, andpackages/spec/authorable-surface.base.jsonis unchanged — mechanical confirmationthat no acceptance face moved. Clause-② holds: string values and prose only.
Sweep completeness
After the edits, a repo-wide
grep -rn 'docs.objectstack.ai'returns 37 hits in 19files, zero of them in this PR's surface. Every survivor is deliberate and owned:
scripts/check-published-readme-links.mjsDOCS_HOSTSclassifier accepts the aliases by design (#10659's header: dropping one reclassifies it asexternal, which would delete the page/anchor checks from the URLs most likely to rot — links already shipped in npm tarballs). The rest are self-test fixtures.skills/objectstack-data/SKILL.mdskills/**is onGOVERNED_SURFACES; PD #14 means a maintainer-landed PR. Folding it in would fork this whole PR onto that path.docs/adr/0010-...-model.md,docs/adr/0046-...mddocs/adr/**is governed).packages/create-objectstack/src/templates/blank/{README.md,Dockerfile,docker-compose.yml}packages/**/CHANGELOG.md.changeset/canonical-docs-host-in-published-links.mdNeighbour-word control for the zero-hit claim (same run, same command shape, so the grep
is demonstrably working rather than silently matching nothing):
objectstack.ai283,https://objectstack.ai185,protocol.objectstack.ai7,www.objectstack.ai2.Note for whoever lands #10656:
docs/adr/0010-metadata-protection-model.md:565quotesthe same
docsUrl:literal this PR changes in source. #10656 flagged that it should movewith the code rather than ahead of it — the code has now moved, so that line's target is
https://objectstack.ai/docs/references/shared/protection.Verification
Gate union and tests pinned to final HEAD
baea11c39c, clean tree (git status --porcelain→ 0 files).Tests — per package, all green:
@objectstack/spec@objectstack/cli@objectstack/plugin-security@objectstack/platform-objects@objectstack/example-showcase@objectstack/setup,@objectstack/studiotestscript — verified by reading theirpackage.json, not inferred from an empty runturbo run typecheckover all seven touched packages (setup and studio included, which istheir real readout): 66/66 tasks successful.
Generated-artifact gates:
check:generated— "All 14 generated artifacts are up todate";
check:authorable-surface— "1257 default(s) unchanged";check:docs— "228generated files in sync with packages/spec".
Gate set re-derived with
node scripts/pm/dispatch-gates.mjs(no path arguments — ittakes its own change set from the merge base), which named 36 paths and 31 families. Run
and green:
check:published-readme-links("173 outbound link(s) across 60 publishedmarkdown file(s): 0 root-relative, 0 non-canonical origin(s), 27 docs-site page(s)
resolved" — assertion 2 is the canonical-origin one),
check:docs-redirects,check:doc-anchors,check:doc-frontmatter,check:doc-authoring,check:docs-audit-scope,check-affected-docs,check:nul-bytes("scanned 6302 textfile(s) … no raw ASCII control bytes"),
check:i18n,check:examples-live-imports,check:quick-reference-counts,check:role-word,check:spec-parsed-alias,check:test-source-alias,check:cross-package-test-inputs,check:merge-driver,check:engine-double-contract,check:where-matcher,check:query-options-erasure,check:slot-lookup,check:type-source-resolution,check:ci-filter-parity,check:plugin-teardown-shape,check:dev-prereqs, spec'scheck:liveness/check:empty-state/check:variant-docs/check:strictness-ledger, and the fivechangeset families.
The ratchet family was run after the final commit:
check:type-check-coverageOK(64/77 packages type-checked) and
check:type-check-debt --re-measureOK — "33 ledgerentr(ies) re-measured in 385.9s, 1908 raw tsc error(s) total, none above its recorded
number." Its first attempt refused outright because I had built with
OS_SKIP_DTS=1and@objectstack/service-knowledgehad nodist/*.d.ts; that refusal means not measured,so the closure was rebuilt with declarations and the gate re-run rather than waved
through. It reports a pre-existing
-12surplus on@objectstack/plugin-auth, untouchedby this PR and left for its owner.
Changeset:
.changeset/canonical-docs-host-in-runtime-strings.md— patch across the sixpublished packages whose emitted strings changed.
@objectstack/example-showcaseisprivate and takes none.
Generated by Claude Code