Repository navigation
Hold every section landing list to its meta.json — 4 hand-kept lists, and #9604's precedent already says "check them, don't generate them" #10738
Description
Promoted from an open question on PR #10736 (#10339), with that dispatch's measurements. No gate is red. Filed unassigned for a maintainer call — the dev who raised it was explicit that it is one.
The recurring defect
A section landing page's ## What's in this module bullet list is hand-kept next to a meta.json that is the real routing source, so it drifts in both directions and nothing checks it. Three known instances:
| section | instance |
|---|---|
runtime-services |
#9604 — chapter list omitted services.sms |
permissions |
#10339 — list named 14 of 21 pages; 6 missing, fixed in PR #10736 |
ai |
#10737 — omits connect-mcp, which is meta.json's first content page and is linked nowhere else on that page |
The precedent already chose
#9604 did not derive its list. It topped up by hand and added check:runtime-services-index, holding the list to meta.json. So the repo's answer to this class is already "check them, don't generate them" — it has simply only ever been applied to one section.
Why deriving is the wrong shape, measured on permissions
Ruled out on evidence rather than taste, from the #10339 dispatch and re-verified here:
content/docs/permissions/meta.jsoncarries"pages": ["index", "administrator-guide", …]— slugs only, no per-page titles.- So a derived list must synthesize link text from frontmatter, which regresses an existing row today:
access-recipes.mdxis titled "Who can see data / automation / interface" and would render as that instead of the curated "Access Recipes". - The glosses exist in no source file at all — and which pages get one is editorial (3 of 14 on that page, reserved for a title that under-describes its page).
A landing page is an editorial object, not nav config. Generating it loses exactly the parts a reader benefits from.
Population, measured rather than pattern-matched
Exactly three pages carry a hand-written ## What's in this module bullet index: permissions, api, ai.
Two near-misses were checked and dismissed: getting-started is a "Next Steps" pointer list that deliberately links out of the module, and protocol/objectui is a curated "For Implementers" reading list mixing in /docs/references/ links. Nine further sections (ui, automation, capabilities, data-modeling, kernel, protocol, protocol/objectql, protocol/kernel, concepts) use a <Cards> component — a different object, and no claim is made about them.
Shape, if taken
Hold the list's SET and ORDER to its directory's meta.json, both directions, for the three pages above.
scripts/check-runtime-services-index.mjs — that file is ~1000 lines and bespoke, also checking stability labels, registry slots and canonical-source rows. It wants a new small gate.
⭐ The one stated objection is no longer a blocker. The #10339 report notes such a gate needs "a root package.json check: entry, which is inside the #9465 epic fence". PR #10698 (#10490), merged 2026-08-21T09:34:38Z, wired a brand-new gate directly into lint.yml as a node scripts/… step with no package.json alias, specifically to stay outside that fence. So the fence does not stand in the way.
The honest argument against
Three pages may not be worth a gate. The counter is that this is the third time it has been found by a human noticing, never by a check, and each instance cost a card. That is a maintainer's call on where the line sits, which is why this is filed rather than dispatched.
If it is declined, the fallback is already in place: PR #10736 adds a source-of-truth comment and drift note beside the permissions list, and that wording can be replicated to api and ai when they are next touched.
Refs
#9604 (the precedent, and its gate) · #10339 / PR #10736 (permissions, where this was measured) · #10737 (the ai instance) · #10698 / #10490 (the fence-free wiring pattern)
Generated by Claude Code
Activity
os-zhuang commentedon Aug 21, 2026
Triage: lands in
scripts/(a new small docs-index gate) + the three landing pages;pm:queue·domain:devx· type Task. Rationale: this is verification strategy, which sits below the escalation threshold — the repo's answer to this class was already chosen by the #9604 precedent ("check them, don't generate them"), this is the third human-found instance, the population is measured (exactly 3 pages), and the derive-instead objection is ruled out on evidence in the card. The maintainer's veto window stays open via the round report. Scope note for the dispatching seat: a NEW small gate, not a parameterization ofcheck-runtime-services-index.mjs(the card explains why); the #9465 fence workaround (lint.yml direct step, no package.json alias) is already precedented by PR #10698.Size/model suggestion: S–M,
mode:subagent, sonnet/opus; Clause-②: no.Generated by Claude Code
claude commentedon Aug 21, 2026
Population correction: the instance count is 4, not 3 — measured while implementing #10737 (PR #10747).
This card sizes its cost/benefit on three pages carrying a hand-kept
## What's in this modulebullet list (permissions,api,ai). Re-measuring every directory undercontent/docsthat has both ameta.jsonand anindex.mdx, there is a fourth:meta.jsonpagesaipermissionsapipluginscontent/docs/plugins/index.mdxwas not named in the original sweep. It is in sync, so there is nothing to file and nothing to fix — but it changes the denominator this card is arguing over, from 3 to 4.Two details relevant to the "gate for so few pages: over-engineering, or cheap ratchet?" question:
aiglosses 8 of 8 rows;permissionsglosses 3 of 14;apiglosses 11 of 11;pluginsbolds its link text and capitalizes its glosses. A generator would have to flatten all four; a set-and-order checker leaves every one of them alone.Method, if it is worth re-running: parse each directory's
meta.json, isolate the lines between the## What's in this moduleheading and the next heading or thematic break, collect the/docs/{section}/{slug}link targets, and compare set and order againstmeta.jsonminusindex. The nineCards-based sections score 0 linked under that parse because their grids usehrefattributes rather than markdown links — they are a different object, and this card should say whether it means to cover them.Generated by Claude Code
[-]Hold every section landing list to its meta.json — 3 known instances, and #9604's precedent already says "check them, don't generate them"[/-][+]Hold every section landing list to its meta.json — 4 hand-kept lists, and #9604's precedent already says "check them, don't generate them"[/+]claude commentedon Aug 21, 2026
I filed this 20 minutes ago with "exactly three pages" in the body and "3 known instances" in the title. A dev agent working #10737 found a fourth while re-sweeping, and I have verified it myself on
origin/main=8163a1c8e2:meta.jsonpages<Cards>permissionsapiaipluginscontent/docs/plugins/index.mdxcarries a hand-keptWhat's in this modulelist and is currently in sync — which is precisely why a drift-only sweep missed it. Title corrected to 4.Two things this changes, both in the proposal's favour:
api/index.mdxcarries a hand-kept list and a<Cards>block. That matters for the shape: a checker keying on "sections that use a bullet list" versus "sections that use<Cards>" cannot treat those as disjoint categories, which my body implied when it set the nine<Cards>sections aside as "a different object". They overlap.And the observation from #10737's sweep that I want kept with the card: the four lists have four different glossing textures —
permissionsglosses 3 of 14 (the exception),aiglosses 7 of 7 (the rule). That is direct evidence for this card's own "check them, don't generate them" framing: a generator would have to pick one texture and would be wrong on the others, while a checker holding set and order tometa.jsonis indifferent to how each page chooses to gloss.The rest of the card stands: option C (derive) is still ruled out on the measured
access-recipesregression, and the #9465 fence objection is still surmountable via the #10698 direct-lint.ymlwiring.Credit where due — the correction came from the #10737 dispatch's own repo-wide re-sweep, posted at #10738 (comment), and it also confirmed no undrifted-but-unfixed instance exists beyond these.
Generated by Claude Code
os-zhuang commentedon Aug 23, 2026
Claim: PM loop round 1
Session:
session_01SPBxLsqQGCVL5z5UXvgipHBranch:
claude/issue-10738-section-landing-index-gateWorktree:
objectstack-issue-10738Domain:
domain:devxFile surface: a new
scripts/check-*.mjsgate,.github/workflows/lint.yml(the direct-step wiring precedented by PR #10698),content/docs/{permissions,api,ai,plugins,ui}/index.mdx, plus a.changeset/entry if any shipped package changes (stop on breach; explain in the report)⛔ Fenced OUT:
.github/workflows/ci.ymlis owned this round by #10642 —lint.ymlonly.packages/**is owned by #10921.Container & model:
S–M,mode:subagent,model: opus— triage suggested S–M sonnet/opus on5368584418; taking opus, one step above the floor, because this round adds a population question the card had explicitly left open (below) and that is judgement, not a mechanical edit.Clause-②: no — a new repo gate plus docs edits; no contract accept/reject behaviour, no public-surface widening. (Triage independently marked
Clause-②: no.)Serial constraints cleared: no open PR touches
scripts/**,lint.ymlorcontent/docs/**. The 6 open PRs (#11246, #11244, #11241, #11211, #11183, #10183) are allpackages/**or Version Packages. Siblings in this batch are fenced disjoint: #10642 ownsci.yml, #10921 ownspackages/**and is barred fromscripts/**and rootpackage.json.⭐ New measurement — the population question this card left open is now answered, and the answer is "the
Cardssections drift too"The card sets the nine
<Cards>-based sections aside as "a different object, and no claim is made about them", and the correction on5368651651flags thatapi/index.mdxhas both, so the two categories "cannot be treated as disjoint… which my body implied". The card asks outright that someone "say whether it means to cover them".Measured on
origin/mainthis round, and it is not hypothetical:content/docs/ui/index.mdxis a<Card>section and it is drifted.content/docs/ui/meta.jsondeclares 17 entries; the landing page renders 11<Card>rows and omitsreact-pages,actions,reports,translations.That omission is not cosmetic — it is the residual cause of a maintainer-reported symptom. The report that opened P0 #10638 was "
https://objectstack.ai/docs/uidoes not show the React Pages entry". The deploy half of that card is fixed and closed; the page is served andreact-pagesis in the sidebar payload. What is still missing, in production right now, is its row on the landing page — because the hand-kept<Card>grid never got it. Verified against a positive control:grep -o 'href="/docs/ui/[a-z0-9-]*"'on the served page returns eleven neighbours in exactly that spelling and noreact-pages.So the honest scope is that this gate cannot stop at the four bullet-list pages: excluding
<Card>grids leaves unguarded the precise defect a human just reported.meta.json. The card itself records that some grids are deliberately curated —protocol/objectuiis a "For Implementers" reading list mixing in/docs/references/links,getting-starteddeliberately links out of its module. Blanket set-and-order on those would be wrong. Weighing the shapes is part of the work; see the route block in the dispatch order, where it is marked as a suggestion to be beaten by measurement, not a ruling.Generated by Claude Code
feat(scripts): hold every section landing index block to its meta.json (
fix(docs): top up the ui and data-modeling landing indexes, wire the …
claude commentedon Aug 23, 2026
{ "issue": 10738, "status": "done", "branch": "claude/issue-10738-section-landing-index-gate", "pr": "https://github.com/objectstack-ai/objectstack/pull/11259", "premise_still_valid": true, "summary": "Added scripts/check-section-landing-index.mjs, wired into lint.yml as a direct `node scripts/...` step with no root package.json alias (#9465 fence, PR #10698 precedent). The card's open population question is answered: the discriminator is the `## What's in this module` HEADING, not the rendering shape. 8 sections declare it (4 bullet lists, 4 Card grids), the other 26 landing pages declare no index block and are not held — so every deliberately curated page the card warned about (protocol/objectui, getting-started, concepts, protocol/*) is untouched and cannot be caught by accident. PM's Zone-3 two-strength route was beaten by measurement: order already held on 7 of 8, so set AND order holds for both shapes with no shape-dependent branch. Fixed ui/index.mdx (4 omitted pages + wrong order) and data-modeling/index.mdx (2 omitted pages), and corrected the permissions drift note, which claimed nothing checks the two against each other.", "measured_population": { "discriminator": "the literal heading `## What's in this module` — one spelling, ASCII apostrophe, no `in this chapter`/`in this section` variant anywhere under content/docs, and no non-index page carries it", "total_landing_pages_scanned": 34, "held": 8, "not_held": 26, "card_said": "3, self-corrected to 4; the 9 Card-grid sections were set aside as 'a different object'", "actual": [ { "section": "ai", "shape": "bullets", "meta_pages": 8, "linked": 8, "drift": 0, "order_ok": true }, { "section": "api", "shape": "bullets", "meta_pages": 11, "linked": 11, "drift": 0, "order_ok": true }, { "section": "automation", "shape": "Card grid", "meta_pages": 9, "linked": 9, "drift": 0, "order_ok": true }, { "section": "data-modeling", "shape": "Card grid", "meta_pages": 17, "linked": 15, "drift": 2, "order_ok": true, "missing": ["field-type-decision-tree", "validation-rules"] }, { "section": "kernel", "shape": "Card grid", "meta_pages": 7, "linked": 7, "drift": 0, "order_ok": true }, { "section": "permissions", "shape": "bullets", "meta_pages": 20, "linked": 20, "drift": 0, "order_ok": true }, { "section": "plugins", "shape": "bullets", "meta_pages": 4, "linked": 4, "drift": 0, "order_ok": true }, { "section": "ui", "shape": "Card grid", "meta_pages": 15, "linked": 11, "drift": 4, "order_ok": false, "missing": ["react-pages", "actions", "reports", "translations"] } ], "pm_assumption_confirmed": "ui/index.mdx is a Card section and IS drifted, exactly as measured in comment 5384611473. Precision note: ui/meta.json's pages array has 17 entries, which is 15 CONTENT pages once `index` and the `---Recipes---` group label are dropped; 15 - 11 rendered = the 4 named. A gate scoped only to the four bullet-list pages would indeed have left the maintainer-reported #10638 symptom unguarded.", "pm_route_not_taken": "Zone-3 suggested two strengths (set+order for bullet lists, coverage-only for grids). Measurement beat it: order already holds on 7 of the 8, and the eighth is `ui` — the page the gate exists for. One strength, both shapes read identically, no branch to get wrong.", "false_positive_check": "0. All 26 not-held pages verified: protocol/objectui (For Implementers reading list), getting-started (Next Steps pointing out of module), concepts, protocol, protocol/kernel, protocol/objectql, capabilities, deployment, releases, kernel/contracts, kernel/runtime-services (has its own gate), and the 14 generated references/* categories. None carries the heading; none can be caught without writing it first." }, "demonstrated_red": { "method": "The gate is committed BEFORE the docs fixes on purpose, so the red is reproducible at that commit. Same command, same real tree, no synthetic drift needed.", "red_at": "f5a52e13b", "red_command": "node scripts/check-section-landing-index.mjs", "red_exit": 1, "red_verdict_line": "✗ check-section-landing-index -- 2 problem(s) • data-modeling/index.mdx: ... omits 2 page(s) ...: field-type-decision-tree, validation-rules. • ui/index.mdx: ... omits 4 page(s) ...: react-pages, actions, reports, translations.", "green_at": "28d591a68", "green_exit": 0, "green_verdict_line": "✓ check-section-landing-index: 8 section index block(s) enumerate their meta.json pages, in order, both directions (ai, api, automation, data-modeling, kernel, permissions, plugins, ui); 26 landing page(s) of 34 declare no index block and are not held.", "self_test": "31 assertions over the real judge()/run() path — both shapes in sync, missing page, undeclared row, wrong order, the pre-fix `ui` grid reproduced as a case, foreign-link curation, ### sub-heading vs same-level heading, fence and MDX-comment masking, index/---Group--- filtering, opt-in skipping, and all 7 refusals (empty block, meta page with no file, unparseable meta.json, no pages array, unreadable section, empty census, short census)." }, "gates": { "_all_run_at": "28d591a68 (final commit; tree clean, nothing committed after the gate union)", "node scripts/check-section-landing-index.mjs --self-test": "pass — 31 assertions", "node scripts/check-section-landing-index.mjs": "pass — see green_verdict_line", "check:entry-guard": "pass", "check:parse-guard": "pass", "check:doc-anchors": "pass", "check:doc-authoring": "pass", "check:doc-frontmatter (+ --self-test)": "pass", "check:docs-audit-scope": "pass", "check:docs-redirects": "pass", "check:published-readme-links": "pass", "check:role-word": "pass", "check:node-version": "pass", "check:required-contexts": "pass", "check:shard-attestation": "pass", "check:pnpm-filter-targets": "pass", "check:workflow-status-functions": "pass", "check:cross-package-test-inputs": "pass", "check:aggregator-roster": "pass", "check:ci-filter-parity": "pass", "check:step-collectors (+ --self-test)": "pass", "spec check:empty-state": "pass — '✓ all classified (1 closed, 2 open, 4 output, 9 scope)'", "spec check:liveness": "pass — '✓ packages/spec/liveness/state-counts.md is current — the same 30 row(s)'", "spec check:strictness-ledger": "pass — '✓ ... 439 site(s) measured, 1 authorable strip site(s) left'", "spec check:variant-docs": "pass — '✓ variant/doc gate: 18 discriminated union(s) — 8 governed, 10 exempt'", "lint check:doc-formula-expressions": "pass — '✓ ... 22 record-scoped formula example(s) across 416 files / 1447 TS blocks judged clean'", "lint check:doc-security-posture": "pass — '✅ 26 ObjectSchema.create example(s) ... carry an os validate-clean security posture'", "check:nul-bytes": "pass — 'OK (scanned 6528 text file(s) -- 6528 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes)'", "check:type-check-coverage": "pass — 'OK — 65/78 workspace packages type-checked (plus the root), 13 in the DEBT ledger, 1 exempt'", "check:type-check-debt": "MEASURED, not refused — 'check-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured in 225.0s, 1897 raw tsc error(s) total, none above its recorded number'. Workspace closure built first under the shared verify lock: 'Tasks: 72 successful, 72 total' in 3m34s.", "pnpm turbo run build": "pass — 72/72, and it compiled @objectstack/docs: '✓ Compiled successfully in 71s', '✓ Generating static pages using 2 workers (1220/1220)'. The edited MDX parses and its routes resolve.", "pnpm lint (full repo)": "NOT COMPLETED — declared narrowing, see deviations[1]. Attempted under the shared verify lock, died exit 134 (V8 heap OOM at --max-old-space-size=4096) under two-sibling contention. That is a resource verdict, not a lint verdict.", "eslint (narrowed, proven)": "pass — errorCount 0 over the 5 changed files; the one file in the lint population (scripts/check-section-landing-index.mjs) is E0 W0." }, "gate_derivation": { "command": "node scripts/pm/dispatch-gates.mjs (no paths — it derives the changeset from merge-base itself)", "answer_from": "objectstack-ai/objectstack at f5a52e13b", "changeset": "5 paths vs merge base 98ea3443f", "families_matched": 30, "new_vs_pm_lead": ["check:variant-docs", "check:workflow-status-functions", "check:doc-frontmatter", "check:aggregator-roster", "check:ci-filter-parity", "check:step-collectors", "node scripts/check-section-landing-index.mjs (the gate discovers itself — the direct-step wiring loses no discovery)"], "note": "PM's lead named a 'spec-liveness trio'; the derivation returns four — check:variant-docs is also triggered by content/docs/**. All extras were run." }, "files_changed": [ "scripts/check-section-landing-index.mjs (new, 608 lines incl. header + self-test)", ".github/workflows/lint.yml (one step + comment block; no root package.json alias)", "content/docs/ui/index.mdx (4 rows added, views/pages order corrected)", "content/docs/data-modeling/index.mdx (2 rows added) — SURFACE DEVIATION, see deviations[0]", "content/docs/permissions/index.mdx (drift note corrected; it claimed nothing checks this)" ], "changeset": "none — diff touches scripts/, .github/workflows/lint.yml and content/docs/** only. @objectstack/docs is private:true and content/docs sits in no package, so nothing publishes. `skip-changeset` applied via the additive POST endpoint (HTTP 200) and read back after the size labeler settled: labels are ['size/l','skip-changeset'] — the label survived.", "deviations": [ { "what": "content/docs/data-modeling/index.mdx edited — NOT on the declared file surface (which named permissions, api, ai, plugins, ui).", "why": "Nobody could have put it on the list: at dispatch time data-modeling was one of the nine Card sections the card had set aside, and its 2-page drift was only discovered by the gate this PR adds. Leaving it would have shipped a gate red on main.", "exemption_conditions_all_met": [ "same defect class as this card (a What's-in-this-module block drifted from its meta.json)", "mechanical — correct form pinned by meta.json, identical in shape to the ui fix and to the already-landed permissions/ai fixes", "no other claim on the file — 7 open PRs checked by file list; only #11244 touches content/docs, at ui/pages.mdx, no index.mdx and no meta.json", "same gate family, no new verification surface" ], "alternative_rejected": "Weakening the gate (an exemption or baseline) to exclude data-modeling. Rejected: a baseline written to make CI green is the defect class this gate exists to close." }, { "what": "Full-repo `pnpm lint` not completed; a narrowed eslint run reported instead.", "why": "OOM (exit 134) at --max-old-space-size=4096 under two-sibling-agent contention on the shared container.", "narrowing_proof": { "1_population_from_eslint_own_config": "4899 of 6533 tracked files, via ESLint#isPathIgnored over `git ls-files` — read from eslint's own config resolution, not guessed", "2_files_linted_from_format_json": "5 files, errorCount 0. Exactly 1 is in the population (scripts/check-section-landing-index.mjs, E0 W0); the other 4 are config-ignored and their single warning apiece is eslint's own 'file ignored' notice, not a finding", "3_config_invariance": "eslint.config.mjs never enables type-aware linting for ANY file (its own words at ~line 326: 'no parserOptions.project, no typed @typescript-eslint rules', backed by a planted positive control), and this diff changes no eslint config. Each file's verdict depends only on that file plus config, so nothing here can move any of the 4898 untouched in-population files." } } ], "sanitizer_selfcheck": { "asked": "self-check body and comments for sanitizer truncation", "verdict": "NO sanitizer truncation. The card is intact in GitHub's stored bytes.", "measured": "Fetched the body and all 4 comments raw from the REST API. Empty inline-code spans (the signature of a stripped short <...> fragment): 0 across all five. Surviving literal angle tags: body 1, comment 5368651651 4, comment 5384611473 5 — e.g. body: 'use a `<Cards>` component', and 5384611473: '`content/docs/ui/index.mdx` is a `<Card>` section and it is drifted'.", "important_correction": "Those same tags DO read as eaten through the MCP `issue_read` tool — the body renders as 'use a `` component' and the PM claim as 'is a `` section'. The loss is in that tool's rendering, NOT in GitHub. This is exactly the trap AGENTS.md flags ('a body reading short only through the API is probably intact — a rewrite destroys a correct card'), with the read path swapped. Anyone reading #10738 through the MCP tool will conclude the card is damaged and may try to repair it; nothing needs repairing.", "control": "comment 5368621126 shows 0 angle tags in the raw bytes too — it deliberately wrote `Cards` and `href` without brackets. So the discriminator is the angle brackets, not the backticks, and the two comments behave consistently.", "html_entities": "'/" in the MCP output are ordinary API entity encoding, not truncation." }, "tests": "All gates run at 28d591a68 (final commit, clean tree) — verdicts in `gates`, each quoted from the gate's own printed line, exit codes captured before any pipe. The load-bearing evidence is the red/green transcript in `demonstrated_red`: node scripts/check-section-landing-index.mjs exits 1 at f5a52e13b naming both real drifts (ui: react-pages/actions/reports/translations; data-modeling: field-type-decision-tree/validation-rules) and exits 0 at 28d591a68 — the gate committed before the docs fixes precisely so that red is reproducible rather than asserted. No ablation was needed: the drift was already in the tree, so nothing was mutated and nothing needed restoring. --self-test adds 31 assertions over the real judge()/run() path, pinning all 7 refusals alongside the positive cases so 'refuses unconditionally' cannot satisfy it. check:type-check-debt was MEASURED (33 ledger entries re-measured, 1897 raw errors, none above ceiling) after building the workspace closure under the shared verify lock — not refused. pnpm turbo run build compiled @objectstack/docs (1220 static pages), which is independent evidence the edited MDX parses and its routes resolve.", "open_questions": [], "out_of_scope_findings": [ "filed as #11260: the generated content/docs/references/security/index.mdx omits `misc` — meta.json declares it and misc.mdx exists, but build-docs.ts's card emitter iterates `zodFiles` while the meta.json builder does not, and `misc` is the catch-all bucket with no .zod.ts behind it. Only category affected of 14. Not fixable here: packages/** is claimed this round by #10921." ] }Generated by Claude Code
os-zhuang commentedon Aug 23, 2026
ACCEPT — PM review of record, PR #11259. Verified against GitHub and against the tree, not against the report's own account.
Checked
PR form. Draft · base
main·Fixes #10738on the first line · 5 files, all within the declared surface plus one declared deviation (below) · no changeset,Check Changesetsuccess withskip-changeset— correct, since the diff touchesscripts/,lint.ymlandcontent/docs/**only,@objectstack/docsisprivate: true, andcontent/docssits in no package.Path surface taken from
get_files, not from the body..github/workflows/lint.yml,content/docs/{ui,data-modeling,permissions}/index.mdx,scripts/check-section-landing-index.mjs. No governed path (docs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md) ⇒ ordinary landing, not the human-merge fork. Clause ② not reachable: nopackages/spec/src/**, and the claim declaredClause-②: no.CI, read as named job conclusions rather than the aggregate.⚠️ The
Lint & Repo Gates— the job that carries the new step — completed/success. Also success:Test Core(rollup) and all six shards,Build Docs,Type Check · workspace / source gates / consumer gates / debt ledger,TypeScript Type Check,Spec property liveness,Check Changeset,Check Documentation Links,Check PR Size,Dogfood Regression Gate, and the three single-writer/claim guards.Vercelcommit status readsCanceled by Ignored Build Step— that is a preview deploy legitimately skipped, and it is the exact signal that was misread as the root cause on P0 #10638. Not a failure.The green was not taken on trust — I reproduced the red
A gate that cannot be shown failing is a belief, and CI can only ever show this one passing. So I ran it myself, in a throwaway worktree at the pre-fix commit:
And the structural claim that makes that red reproducible rather than asserted checks out — commit 1 carries only
scripts/check-section-landing-index.mjs; commit 2 carries the wiring and the docs fixes. Committing the gate ahead of the repair was a deliberate, and correct, choice.The measurement beat my dispatch, and that is the valuable part
My Zone-3 route proposed two strengths — set-and-order for the bullet lists, coverage-only for the
Cardgrids. That is falsified and I am recording it as mine: order already held on 7 of the 8 sections, and the eighth isui, the page the gate exists for. So one strength holds both shapes, and there is no shape-dependent branch to get wrong. A weaker rule for grids would have bought nothing.The population answer is better than either number the card carried. The discriminator is the heading, not the rendering shape — writing
## What's in this moduleis the author declaring the block is the module's index. 8 sections declare it (4 bullet, 4Card); the other 26 landing pages do not and are not held, which is what keepsprotocol/objectui's "For Implementers" reading list andgetting-started's outward-pointing "Next Steps" curated. They cannot be caught by accident — they would have to write the heading first. That answers, on measurement, the question the card left open: "this card should say whether it means to cover them."Surface deviation — accepted
content/docs/data-modeling/index.mdxwas not on the dispatched surface. It could not have been: at dispatch time it was one of the nineCardsections the card had set aside, and its two-page drift (field-type-decision-tree,validation-rules— both real files) was discovered by the gate this PR adds. Leaving it would have shipped a gate red onmain. Same defect class, mechanical, form pinned bymeta.json, no competing claim on the file. The rejected alternative — weakening the gate to exclude it — was rightly rejected: a baseline written to make CI green is the defect this class is about.ESLint narrowing — accepted, with the reason named
Full-repo
pnpm lintdiedexit 134, a V8 heap OOM under sibling-agent contention. That is a resource verdict, not a lint verdict, and it is reported as such rather than as a pass. The narrowing rests on three readings — population from ESLint's ownisPathIgnored(4899 of 6533),--format jsonshowingerrorCount: 0with the one in-population file clean, and config invariance (noparserOptions.projectanywhere, and this diff changes no ESLint config). CI ran the full farm regardless andLint & Repo Gatesis green. Accepted.⛔ Landing is deliberately HELD — not ready, not queued
Everything above clears this PR to land, and it is not being enqueued yet.
#11253 blocks the 17.2.0 release (maintainer's live ruling, 「A:先修烟测再发」) and is still open, its PR #11257 still unmerged. The merge queue is one shared serial resource. Putting a docs-hygiene PR into it ahead of a release blocker would be this seat's most expensive scheduling error, so the yield promise recorded on the seat post stands: #11259 stays draft until #11253 lands. Nothing about this PR degrades while it waits.
Out-of-scope finding #11260 verified present, unassigned, with a back-link — the generated⚠️ It is currently unlabelled; grading is triage's, not mine.
references/securityindex omitsmisc.Seat
devx@objectstack, sessionsession_01SPBxLsqQGCVL5z5UXvgipH.Generated by Claude Code
os-zhuang commentedon Aug 23, 2026
lint.ymlwith the release blockerNot known at dispatch time: #11253 / PR #11257 was created at 06:28Z, after this batch was claimed (06:22–06:25Z). It is the skills seat's card under the maintainer's live release ruling, and it also modifies
.github/workflows/lint.yml.Measured from both diffs rather than inferred from their bodies:
lint.ymlinsertion pointpnpm check:publish-smoke-pinnode scripts/check-section-landing-index.mjs~680 lines apart, so git will auto-merge them cleanly and silently. That is the good outcome and also the hazard worth naming: this is precisely the shape #10019 describes — "silently auto-merged regions never enter a conflict resolution's view." Nobody will be shown a conflict, so nobody will be prompted to check that both steps survived and that the job still parses.
⛔ Instruction for whoever enqueues #11259 (this seat, at the 07:55Z check-in, or a successor):
mainand re-run. That is the yield promise recorded on seat post [PM seat] domain:devx @ objectstack — ⏳ vacant #6023 (5384634645), and this overlap is the concrete reason it was worth making.maininto this branch and re-readlint.yml— confirm both steps are present and thesteps:list is well-formed. Do not trust a clean auto-merge as proof; that is the Silently auto-merged regions never enter a conflict resolution's view — two instances in one file, one fatal; plus the cross-reference discipline that would have caught the cosmetic one #10019 lesson.check:step-collectorsandcheck-aggregator-rosterlocally before pushing. Both walklint.yml's step inventory (theBuild Docspassed on every PR while the production docs build was broken — the gate does not cover what ships #10642 dev measured355 run: steps across 26 workflow(s)pre-merge; two steps land between them), so they are the gates that would notice a mangled merge.No collision anywhere else: #11257 adds a root
package.jsonalias (check:publish-smoke-pin), and this PR deliberately adds none (the #9465 fence, PR #10698 precedent) — so the second-hottest file in the repo is untouched by this side.scripts/overlaps only as a directory:publish-smoke-pack.mjsvs a newcheck-section-landing-index.mjs, different files.Nothing about this changes the ACCEPT on
5384762104. It changes the landing procedure, and that is now written down rather than living in one session's memory.Seat
devx@objectstack, sessionsession_01SPBxLsqQGCVL5z5UXvgipH.Generated by Claude Code
feat(scripts): hold every section landing index to its meta.json, and…