Skip to content

fix(devx): assert that a published README's repo-relative targets exist - #10892

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-10813-readme-relative-paths
Aug 21, 2026
Merged

fix(devx): assert that a published README's repo-relative targets exist#10892
os-zhuang merged 1 commit into
mainfrom
claude/issue-10813-readme-relative-paths

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#10813

Verified at cc222637a3 — the gate union below was run on that exact commit.

What was wrong

check:published-readme-links read the docs-site URLs a published README carries and said nothing about its repo-relative paths. packages/runtime/README.md linked six targets that are not in the tree; a census across the whole published population found nine, in four packages.

Nothing read them, and that is structural rather than accidental: assertions 1–4 of that gate read absolute URLs, check:published-readme-exports reads fenced import lines, and the lychee lane (check-links.yml) never sees packages/**/README.md. A relative href could name anything and ship to npm green.

Assertion 5, in the existing gate rather than a second script

It reuses that gate's population (publishedDocs), its extractor (extractLinks, all three link shapes) and its relative classifier bucket — which the gate already counted and labelled "not this gate's business". The gate's own header had named the omission: "whether a relative link resolves — that is a different claim, owned by nothing here yet." A second script would have grown a second population and a second extractor, which is exactly what that file argues against elsewhere.

resolveRelativeTarget() strips the fragment and query, percent-decodes, and resolves against the document's own directory — the base GitHub uses to render a relative href in a blob. Four outcomes, not two: ok, missing, escapes (a climb above the repo root, kept separate because the remedy differs — an off-by-one in the ../ count, not a target that moved), and empty.

The half this deliberately does not decide

Whether a relative href means anything to a reader on npmjs.com is a different question. npm renders the README outside the repo, so every relative link is unresolvable there — including the 101 that are perfectly correct in-tree, and this repo's published READMEs lean on the relative form throughout ([`@objectstack/spec`](../spec)). Failing them all would be a ruling on link rewriting, not a scan. So assertion 5 says GITHUB in its message, and a self-test pin asserts that a correct relative link passes — so a later reader of the header cannot mistake the fence for a hole.

Vacuity is refused at the population level, never the document level

18 of the 60 published documents carry no relative destination at all, so on nearly a third of the population this assertion is vacuous by construction — and per-document silence therefore proves nothing. run() now throws when the whole scan classifies nothing relative, the same way it already throws on zero links (#4690). The green line prints the resolved count, so a classifier that quietly stopped recognising the bucket surfaces as a number that fell rather than as continued silence:

✓ check:published-readme-links — 173 outbound link(s) across 60 published markdown file(s):
0 root-relative, 0 non-canonical origin(s), 27 docs-site page(s) resolved (0 via redirect),
1 anchor(s) verified, 101/101 relative target(s) found in the tree.

Two ablations, each with the prediction stated before the run

1. Do the repairs actually close findings? Reverted all four READMEs to origin/main, assertion 5 in place. Predicted: exactly 9 dead-relative-target, 0 escapes-repo, at nine named file:line pairs, and not on the three backticked bullets. Observed: ✗ check:published-readme-links — 9 finding(s), all nine kinds dead-relative-target, at exactly those nine file:line pairs, and zero mentions of the code-span bullets. Mutation and restore each proven on disk by grep (MINI_KERNEL_GUIDE 1→0, examples/app-showcase 0→1, truncated bullet 1→0) rather than by an editor exit code.

2. Are the new self-test pins load-bearing? Deleted assertion 5's branch from checkDocument (if (kind === 'relative') continue;). Predicted: 12 failures — every pin reading findings or relative-resolved, while the strip / discrimination / out-of-scope / vacuity pins stay correctly green. Observed: ✗ check:published-readme-links --self-test — 12 failure(s), matching the predicted set pin for pin. Restored and re-proven on disk.

One pin was rewritten mid-task for exactly this reason: A5 SILENT on a ./ target was first written as findings.length === 0 || stats.relative === 1, whose left branch is true for a resolver that does nothing. It is now two limbs against a package that really has a src/.

The six runtime targets — traced, not deleted

README claimwhere it actually went
MINI_KERNEL_GUIDE.md, ..._ARCHITECTURE.md, ..._IMPLEMENTATION.mdDeleted from the repo root 2026-01-28 (d709ecce68, "Remove redundant markdown files" — 14 files, 5051 deletions, nothing added). The kernel reference is the docs site now, so the section is the same footer eight sibling READMEs already use.
examples/host/examples/app-hostapps/serverapps/objectos → split to objectstack-ai/cloud (#1259). In-repo, an HTTP server in front of the runtime is @objectstack/plugin-hono-server + the @objectstack/hono adapter.
examples/msw-react-crud/examples/app-react-crudapps/console → ships as @object-ui/console from another repo.
test-mini-kernel.tsA root-level scratch script (test-mini-kernel.js/.ts, added and removed in early prototyping). Its successor is this package's own suite — 179src/**/*.test.ts.

The truncated bullet - `packages/runtime/src/ (unterminated backtick) was reaching for exactly that last successor; it is now a real pointer to it.

The other three packages — file surface beyond the card, named here with its evidence

A gate cannot land red and this one refuses a baseline by design, so the six sibling findings had to be repaired for assertion 5 to ship. All four are published packages whose README is in the tarball, hence the changeset.

Gates

All 19 run at cc222637a3, exit codes captured before any pipe. 16 derived by node scripts/pm/dispatch-gates.mjs (no path list passed — it takes its own change set from the merge base) + check:i18n (convention-triggered by the plugin-security edit) + check:nul-bytes + check:published-readme-exports (the sibling gate in this family).

Two needed a build before they said anything true, which is why they are reported here rather than skipped: check:i18n first printed PREREQUISITE NOT MET — the workspace CLI is not built … Nothing was checked, and check:published-readme-exports printed 40+ Build first lines. After building, both are real verdicts:

  • ✓ check:published-readme-links — 173 outbound link(s) … 101/101 relative target(s) found in the tree.
  • ✓ check:published-readme-links --self-test — … all five assertions observed both FAILING and SILENT
  • check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys) — incl. plugins/plugin-security in sync (4 bundle(s))
  • ✓ check:published-readme-exports — 60 published document(s) across 77 workspace package(s); 214 import statement(s)
  • ✓ check-nul-bytes --self-test: 75 assertions over a temp git repo (real scan() path)
  • ✓ check-entry-guard self-test: 52 cases pass · ✓ check:parse-guard self-test: 46 cases pass
  • ✓ slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new
  • ✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added). · ✓ This diff introduces no major bump.
  • OK: all 82 declared cross-package glob(s) … · OK: 13 package(s) read outside themselves, all declared
  • ✓ check:plugin-teardown-shape: 57 Plugin implementation(s) across 4407 source(s) · ✓ affected-docs self-test: 323 cases pass. · ✓ objectui-changeset-digest · ✓ check-adr-0087-registration · check-test-source-alias --self-test OK · check-type-source-resolution --self-test OK

Console Pin Gate is red on main for an unrelated cross-repo reason (#10856) — non-blocking, not from this branch.


Generated by Claude Code

…st (#10813)
`check:published-readme-links` read the docs-site URLs a published README
carries and said nothing about its repo-relative paths. `packages/runtime/README.md`
linked six targets that are not in the tree, and a census across the whole
published population found nine, in four packages. Nothing read them: the
assertions above this one read ABSOLUTE urls, `check:published-readme-exports`
reads fenced import lines, and the lychee lane never sees `packages/**/README.md`.
Adds assertion 5 — a repo-relative destination must name something in the
tree — to the existing gate rather than a second script, so it reuses that
gate's population, extractor and classifier bucket. The gate's own header had
already named this omission ("a different claim, owned by nothing here yet").
Deliberately only the decidable half. Whether a relative href means anything to
a reader on npmjs.com is a separate question: npm renders the README outside the
repo, so EVERY relative link is unresolvable there, including the 101 that are
correct in-tree. Failing them all would be a ruling on link rewriting, not a
scan, and this repo's published READMEs lean on the relative form throughout.
Assertion 5 says GITHUB in its message for that reason.
Vacuity is refused at the population level, never the document level: 18 of the
60 published documents carry no relative link at all, so per-document silence
proves nothing. `run()` throws when the whole scan classifies nothing relative,
the same way it already throws on zero links, and the green line prints the
resolved count so a classifier that stopped recognising the bucket shows up as
a number that fell.
The six runtime targets were each traced to where the content went rather than
deleted: the three MINI_KERNEL_*.md docs were removed from the repo root in
January (d709ecc, 5051 deletions, nothing added) and the kernel reference is
the docs site now; `examples/host/` became app-host, then apps/server, then
apps/objectos, then moved to objectstack-ai/cloud; `examples/msw-react-crud/`
became app-react-crud, then apps/console, and ships as `@object-ui/console`;
`test-mini-kernel.ts` was a root scratch script whose successor is this
package's own 179-file suite under `src/` — which is what the truncated bullet
with the unterminated backtick was reaching for.
The other three packages: `../../plugins/driver-sql` is stale since the driver
moved to `packages/drivers/` (#5618); `plugin-org-scoping`, `service-tenant`
and `service-marketplace` are in no directory of this repo, so those links are
dropped and the names kept as code spans — the spelling those same files
already use for a package they cannot point at in-tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
@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 4 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/adapters/hono/README.md, packages/plugins/plugin-security/README.md, packages/runtime/README.md, …) — pages documenting those are invisible to this run

Coarse fallback — 34 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 7c02a4529c72ad3fc3a69349c14af98202d16c3epackageMentionDocs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tooling labels Aug 21, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 21, 2026 16:18
@os-zhuang
os-zhuang added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit 145ba75Aug 21, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-10813-readme-relative-paths branch August 21, 2026 16:30
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 32502313796 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Console Pin Gate — 失败步骤: Build the Console SPA at the pinned objectui SHA

    ✗ Build failed in 4.90s
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 33 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mtooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Published READMEs' relative repo paths are read by nothing — packages/runtime/README.md names six targets that do not exist

2 participants

@os-zhuang@claude