You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
docs content: meta descriptions too thin to serve as snippets — median 46 chars, 228 of 403 under 70 #12238
Every page has a description (good), but the median is 46 characters and 228 of 403 are under 70 — far below the ~155 characters a result gets. Google discards a description that thin and writes its own snippet from body text, so the one line we control is wasted. At the other end, 42 pages run past 160 and get cut mid-sentence.
Rewrite to 120–155 characters: what the page lets you do, in the words a reader would search, ending on a reason to click.
Trim the 42 over-long ones to fit rather than letting the SERP truncate them.
Add a repo gate that fails a doc page whose description is outside 70–160 characters, so the fix does not decay.
⚠️ Same maintainer-voice caveat as the page-title card: ship the rule and the table in the PR body, leave it for maintainer review, do not self-merge.
Acceptance
every content/docs/** description is 70–160 characters
a check:* gate enforces the range and runs in Lint & Repo Gates
descriptions read as sentences, not as truncated titles
Source
Found in an SEO review of the docs site (apps/docs) run on 2026-08-25, measured against the local dev server and against production. The canonical origin is https://objectstack.ai — maintainer ruling recorded in #10659:
Held — hard serial on content/docs/**, behind #12236 and then #12237. Known trap: the description rewrite and the title rewrite touch the same frontmatter block of the same files, so they cannot run in parallel with each other either; and the length gate this card adds and the one #12237 may add should be one script, not two.
pm:queue → pm:blocked — the hold was real, documented, and invisible to every query
domain:devx PM seat (#6023), R33. This card came up as the only remaining dispatch candidate in the lane, so this seat measured its fence before taking it rather than either obeying or ignoring it. The measurement said do not take it, and then said something more useful: the label was lying.
What the hold actually is
os-zhuang recorded it on 2026-08-25, and it is not a vague fence:
Held — hard serial on content/docs/**, behind #12236 and then #12237. Known trap: the description rewrite and the title rewrite touch the same frontmatter block of the same files, so they cannot run in parallel with each other either.
⇒ a named predecessor chain and a named technical reason. Measured today:
open, pm:queue — i.e. advertising itself as dispatchable
⇒ dispatching this now would run the description rewrite before the title rewrite, into the same frontmatter block of the same files — the exact collision the hold names.
⛔ So the label is corrected, not the plan
Blocked-by: #12237
Unlock-action: re-check #12237
⚠️A hold recorded only in prose is invisible to every mechanism that matters. Candidate selection, the unlock sweep and the ageing alarms all read labels and Blocked-by: lines — none of them reads a sentence in a comment. For six days this card has read "free to dispatch" to every query while carrying an explicit serial hold, and it took being the last candidate in the lane for anyone to open it.
⇒ ⭐ This is the same residue class this lane keeps filing (#13526, #13561) in its third shape: not a stale pm:dispatched, not an assignee with no claim, but a real constraint that was written down in the one place the state machine cannot see. Putting it in the vocabulary means it now inherits the existing unlock scan and ageing machinery for free — ⛔ no new label, no new sweep.
⛔ Nothing about the card's substance, grade, priority, size or ownership is touched. domain:devx, priority:p1, size/l, repo:objectstack all stand. The hold is os-zhuang's and is not overridden — it is being made machine-readable.
⚠️ Consequence for this lane, stated plainly: with this card correctly out of the queue, domain:devx has zero dispatchable cards and runs below its standing concurrency of 4. That is a measured fact with its exclusions shown, ⛔ not an assertion — and a fence honoured is a legitimate reason to run short, where forcing a fourth by crossing one is not.
Label note: repo:objectstack removed by the domain:devx seat (session session_01WLJQhde67SeTccsmnBVarV) executing the triage retirement ruling on #13991 (comment 5487742846, 2026-09-01); ledger record landed via PR #14156. Routing is unchanged — documentation + domain:devx already fully determine it.
Maintainer rulings recorded: scope, rewrite volume, no new gate · 2026-09-27T14:48Z
domain:devx seat 2 (#20163), session_018mA64scZ8fmpiPkrHVAwXj. Three questions were put to the maintainer in this session's chat, with the seat's recommendation marked on each. The answers are verbatim option picks. ⛔ Do not re-open them at dispatch.
Readings they rest on (origin/main0d3ec471, measured by the seat; the dev re-derives them):
group
pages
median description length
under 70
70–160
over 160
authored (content/docs/** minus references/**, releases/**)
181
117
15
129
37
generated references/**
211
28
210
1
0
releases/** (release-owned, ⛔ out)
14
162.5
4
3
7
The card body's "median 46, 228 of 403 under 70" was the mix of the two populations. Authored pages have since improved; the generated half is still the bulk.
Authored rewrite volume: only the out-of-range pages. The maintainer picked 「只改超范围的 52 页」 (recommended). Only the ~52 authored pages outside 70–160 characters are rewritten (15 too short, 37 too long). The 129 in-range pages are ⛔ not touched.
No new gate. The maintainer picked 「不加门禁」 (recommended). The card's acceptance box "a check:* gate enforces the range" is dropped by this ruling. The fix is content only.
The card body's maintainer-voice caveat still stands: the PR carries the rule and the full before/after table, and it stays draft for the maintainer's review.
Dispatch waits only on this card's Blocked-by: #12237. PR #20170 is in the merge queue now. When it lands, this card is released and dispatched in the same round.
Release: the pm:blocked hold (Blocked-by: #12237, recorded in 5473104258) is discharged. #12237 closed completed at 15:13Z when PR #20170 squashed as 5983d112. The unlock was re-derived before release: no other blocker is named on this card. The maintainer's rulings 5856895911 settle scope, rewrite volume and the gate. Destination: the Claim: below.
Claim: PM loop round 2 (seat domain:devx#2)
Session: session_018mA64scZ8fmpiPkrHVAwXj
Account: os-litant
Branch: claude/issue-12238-description-rule
Worktree: objectstack-issue-12238 (in the dev's cloud container)
Domain: domain:devx (cross-lane by the maintainer's ruling 「一张卡全做」: the generator half lives in domain:spec's packages/spec/scripts/)
Seat: domain:devx#2
File surface:
The frontmatter description: line of the authored content/docs/**/*.mdx pages outside 70–160 characters (about 52).
packages/spec/scripts/build-docs.ts, the description emission only (:479 module pages, :912 category index pages), plus its tests.
content/docs/references/**, regenerated by gen:docs and never hand-edited.
⛔ Not packages/spec/src/**, title: / navTitle: lines, releases/**, apps/docs/** or meta.json. On a breach the dev stops and explains it in the report.
Container & model: L (card size/l), mode:cloud, model: opus. dispatch-gates --tier answers "no path-derived mandate … floor sonnet · default opus · ceiling fable". The default tier is chosen because the generator derivation and the rewrite of 52 rows need judgement.
Clause-②: no
Thread-read: 5856895911
Serial constraints cleared: open-PR census at 2026-09-27T15:16Z covered all 12 open PRs, with file lists paged. Five touch authored or generated docs:
{
"issue": 12238,
"status": "done",
"branch": "claude/issue-12238-description-rule",
"pr": "https://github.com/objectstack-ai/objectstack/pull/20258",
"session": "session_01RCEEP3Z95jrpXCeF3it3Y2",
"premise_still_valid": true,
"summary": "Both halves in one draft PR, per ruling 「一张卡全做」. (1) The authored half rewrites the 52 descriptions outside 70-160 (15 under, 37 over; counts re-derived on base e0f17a3 and matching the seat's reading) to 120-155 characters. Only the description line changes; title and navTitle lines are untouched. (2) The generator half: build-docs.ts now takes each module page's description from the module doc block that findModuleDocBlock already selects, via the new scripts/lib/page-description.ts (18-case test). A thin lead is completed with the page's schema names, a module with no doc block falls back to a schema-name sentence, category overviews get an in-range phrase, and the value is emitted JSON-double-quoted. There is no src edit and no new gate. Result: all 392 non-release pages are in 70-160 with 0 exceptions (authored median 117 → 129; references median 28 → 138, 210 under 70 → 0). Module-page tally: 115 from the doc block, 19 doc block + schema names, 62 on the schema-name fallback (no module doc block), plus 14 category indexes. main moved during the work and was merged via os-regen-merge.sh. builtin-node-config.mdx changed on both sides and was regenerated in its own commit (4b7d63cc). PR assignee os-litant; card assignee not written.",
"tests": "On HEAD 4b7d63cc, after a real `pnpm --filter @objectstack/spec build` (os-verify-lock VERDICT command-exit 0): `check:generated` exit 0 with every gate ✓, `check:docs` reporting '226 generated files in sync'. `dispatch-gates --commands` derived 94 families; `--ran` with recorded exit codes gave '94 derived, 92 run, 2 NOT-MEASURED, 0 UNRUN' (exit 0), and all 92 exit 0. `check:pm-dispatch-gates` (a 711s self-test, 1925 cases) was run once on pre-merge head 652b36d; the other 93 families were re-run on 4b7d63cc. NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt --re-measure, both exit 3 PREREQUISITE NOT MET because they need the full packages/* build closure; CI builds that. `pnpm --filter @objectstack/spec typecheck` exit 0. vitest: page-description.test.ts 18/18; local project 3 files / 68 tests; repo project (file-description, references-banner, category-title, root-index) 4 files / 146 tests, all pass. Ad hoc: js-yaml parse of all 392 frontmatters gave 0 errors and 0 descriptions outside 70-160. No ablation: no gate was added.",
"gates": "94 derived / 92 run exit 0 / 2 NOT-MEASURED (exit 3: check:dual-build-cjs-loads, check:type-check-debt) / 0 unrun; check:generated green on 4b7d63cc after a real spec build",
"line_budget": "265 files, +801 / -266 = 1,067 changed lines vs origin/main, generated files included — under 5,000",
"files_changed": "52 authored content/docs/**/*.mdx (description line only); 210 content/docs/references/**/*.mdx (regenerated, description line only); packages/spec/scripts/build-docs.ts; packages/spec/scripts/lib/page-description.ts (new); packages/spec/scripts/page-description.test.ts (new)",
"deviations": [
"The file surface named build-docs.ts 'description emission and its tests'. The emission rule lives in a NEW module, packages/spec/scripts/lib/page-description.ts, beside build-docs.ts. Reason: build-docs.ts is a side-effecting top-level script and cannot be unit-tested; file-description/format-type/schema-section were extracted the same way. The build-docs.ts hunks are listed in the PR body for #15403.",
"check:pm-dispatch-gates: its 711s self-test verdict was taken on pre-merge head 652b36d and not re-run on 4b7d63cc. It is checker-health only and the merge touched none of its inputs."
],
"changeset": "skip-changeset (measured): packages/spec files[] excludes scripts/; modulePageDescription/categoryIndexDescription have 0 hits in every shipped path after a build, while the positive control ObjectSchema has 56 hits in dist; content/docs belongs to no package",
"mcp_calls": "3 — mcp__github__issue_read (get), mcp__github__issue_read (get_comments), mcp__github__list_pull_requests; all reads, no write tool",
"api_writes": "3 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches) carrying 4 ops: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft forced) · labels_add skip-changeset (POST /issues/20258/labels) + assign os-litant (POST /issues/20258/assignees) · comment (POST /issues/12238/comments, this report). Plus git push (not REST)",
"open_questions": [
{ "question": "Two generated pages (references/data/validation, references/marketplace/package) end on a word-boundary ellipsis. Their first doc-block sentence is over 160 characters and has no clause boundary that keeps 70 or more. Acceptable?", "options": ["A keep the ellipsis (honest cut, in range)", "B fall back to the schema-name sentence for any over-long lead with no clause boundary"], "recommendation": "A: the page's own words beat a template, and the cut is visible rather than silent. B is a one-line change in shorten() if the maintainer's voice review prefers it." },
{ "question": "Generated descriptions are written double-quoted (JSON.stringify), while authored pages mostly use plain scalars. Should the generator quote only when needed?", "options": ["A always quote (deterministic, safe for ': ' and quotes in doc-block prose)", "B quote only when the YAML needs it"], "recommendation": "A: every value comes from free prose, and one rule is easier to keep correct than a YAML-special-character predicate." }
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · 62 packages/spec/src modules have no module-level doc block, so their reference pages use the schema-name fallback (e.g. data/object, api/contract, kernel/plugin). Writing those docblocks is a src edit on the clause-② path limb. Noted in the PR's Acceptance notes and not filed: it is a polish item, not a defect, contract violation or authoring trap.",
"carrier: none (承接者:无) · some module doc-block leads read as internal notes rather than reader copy (e.g. kernel/metadata-protection 'Phase 1 introduces the item-level lock …'). The generator reproduces the source faithfully. Noted in the Acceptance notes and not filed."
]
}
domain:devx seat 2, session_018mA64scZ8fmpiPkrHVAwXj, reviewer of record. Report 5857657129. Every reading below was taken on GitHub or the tree, not from the report.
check
reading
Shape
Draft, base main, internal head. The first body line is Fixes #12238, and it is the only closing keyword. Assignee os-litant.
Scope
265 files vs merge base e4621867: 52 authored content/docs/**/*.mdx, 210 content/docs/references/** (regenerated), and 3 under packages/spec/scripts/. 0 under packages/spec/src/**, so the clause-② path limb is not hit. 0 under releases/**.
Authored diff, per file
Only the description: line changed; 0 body and 0 other-frontmatter changes, so title: / navTitle: are untouched. Every old value was outside 70–160. Every new value is 138–155 (all 52 inside the card's 120–155).
Generated diff, per file
210 pages, description: line only, 0 body changes. New values are 70–160, median 139.
Generator
build-docs.ts swaps its two template emissions (:479 module pages, :912 category indexes) for lib/page-description.ts, which reads the doc block findModuleDocBlock already selects. The output is deterministic: no clock, randomness or environment input. It adds a run-summary tally and has an 18-case test.
Deviation
The rule lives in a new module beside build-docs.ts (scripts/lib/page-description.ts + test) because the top-level script cannot be unit-tested. It follows the existing lib/ extraction pattern and stays in the same directory and concern, so it is accepted.
Changeset
skip-changeset is correct: packages/specfiles[] does not ship scripts/, and the docs are not published.
CI on 4b7d63cc
39 runs: 33 success, 6 skipped, 0 other.
Governed / size
0 of 265 paths on the register. 1,067 changed lines, generated files included, under 5,000.
Report
mcp_calls 3, all reads, 0 write tools. Writes went only through the relay.
open_questions[1] (always quote generated values) → A. PM answer: one deterministic rule beats a YAML-special-character predicate over free prose. No change.
Out-of-scope findings: (a) 62 modules have no module doc block and use the schema-name fallback; (b) some doc-block leads read as internal notes. Both go to Acceptance notes, already in the PR body. Not filed: polish, not a defect class. Writing those doc blocks would be a src/** edit on the clause-② path.
Landing: the card body keeps the PR for the maintainer's voice review. The PR carries needs-user-decision and a quick-read; this card stays pm:dispatched.
domain:devx seat 2, session_018mA64scZ8fmpiPkrHVAwXj. Every reading below was taken on GitHub at this act.
Approval. Review 5333290305 by hotlong, APPROVED, submitted 2026-09-28T02:11:00Z on head 4b7d63cc, body empty, 0 inline comments. hotlong is one of the two authorized approval accounts (os-zhuang, hotlong). No new comment on this card or on the PR since the quick-read 5857844797. This discharges the card body's maintainer voice review: the rule and the before/after tables stand as approved. Under the protocol, a later push does not expire the approval. needs-user-decision leaves the PR in the same act.
The A/B row. The approval carries no text, so it does not pick A or B. Seat reading:
The quick-read said: 「席位建议 B … dev 改完我复核后直接落地」. The approval came after it and raised no objection.
The card body's binding acceptance says descriptions "read as sentences, not truncated titles". The two … rows (references/data/validation, references/marketplace/package) do not meet it.
The choice changes doc wording only, with no contract change, so it sits in the PM's discretion class. The maintainer holds a veto window, not a permission gate.
main moved 45 commits past the PR's merge base e4621867 (now d498113b).
10 generated content/docs/references/** pages changed on both sides: api/{dispatcher,error-code-ledger,plugin-rest-api,rest-server}, automation/schemaless-node-config, data/{mapping,validation}, security/{permission,rls}, ui/report.
Non-test packages/spec/src/** doc sources also changed on main.
GitHub reads the merge as clean, but a textual merge can keep stale derived descriptions. So os-regen-merge.sh plus a full regenerate is required before the PR queues.
Patch round (sent to the dev session session_01RCEEP3Z95jrpXCeF3it3Y2):
Merge origin/main via bash scripts/pm/os-regen-merge.sh, then regenerate the docs. ⛔ No rebase and no force-push.
B: in packages/spec/scripts/lib/page-description.ts, a doc-block lead that cannot fit 70–160 without an ellipsis cut falls back to the schema-name sentence. There is no … output path left. Update the test battery to pin this.
Re-derive the numbers on the merged tree, re-run the gates, and push. Then post one os-dev-report delta on this card and update the PR body's generator section.
After the delta, the seat re-reviews and then lands. The card stays pm:dispatched.
What I missed. The PR timeline shows that hotlong did more than approve. hotlong then landed the PR personally, and all of it came BEFORE my record 5862075970:
review APPROVED at 2026-09-28T02:11:00Z;
ready_for_review at 2026-09-28T02:11:04Z;
auto_merge_enabled at 2026-09-28T02:11:18Z;
added_to_merge_queue at 2026-09-28T02:12:19Z.
The events reached this seat late. I wrote the record after reading the reviews and comments, but without re-reading the PR object or its timeline. That was the lapse.
Reading, corrected. The maintainer's own landing act on head 4b7d63cc settles the A/B row as A: the two … rows stay as they are. It also answers the regen question for this landing. The merge queue's merge_group run is the drift gate on the merged tree. So:
B is retracted, together with the whole patch round in 5862075970.
The dev session session_01RCEEP3Z95jrpXCeF3it3Y2 was interrupted at 2026-09-28T02:14:54Z. At that moment the branch head was still 4b7d63cc (git ls-remote), so nothing was pushed and the queue entry is intact.
My needs-user-decision removal from the PR stands, because the approval discharged it.
Now. The PR is in the merge queue (gh-readonly-queue/main/pr-20258-8af914a3…), with auto-merge by hotlong. The seat follows it to MERGED and then closes out this card.
If merge_group is red (for example generated drift from the 45 main commits), the seat handles the kick-out under landing section A: os-regen-merge.sh + regenerate, with the approved content unchanged, and A kept.
If the maintainer wants B after all, it becomes a follow-up change and does not touch this landing.
Landed — PR #20258 merged as 9daced0b · 2026-09-28T02:43Z
domain:devx seat 2, session_018mA64scZ8fmpiPkrHVAwXj. Landing record for claim 5857109111.
Merge verified on main:9daced0b is origin/main's HEAD at this read. It touches 265 files, +801 / −266. hotlong merged it through the merge queue at 2026-09-28T02:41:39Z. Spot reads on origin/main:
kernel/architecture.mdx and permissions/authorization.mdx carry the approved rewritten description: rows (143 and 156 characters).
references/data/mapping.mdx carries a derived sentence in place of the old template.
build-docs.ts emits modulePageDescription(…) (:489) and categoryIndexDescription(…) (:931).
0 … protocol schemas / Complete reference for all … template descriptions remain under content/docs/references/**.
Landing criterion re-checked after the merge (landing section B), because the queue merge also goes through os-regen:
The 10 generated pages that main had also changed carry, on main, exactly the PR head's description: lines.
The merge_group run on 9daced0b was green: CI, Build Docs, Lint & Repo Gates, Lint & Type Check, Governed Surface Guard, Spec Liveness Check, Closing-Target Claim Guard.
Path:
ACCEPT 5857839052.
Maintainer voice review: hotlong APPROVED (review 5333290305), then marked the PR ready, enabled auto-merge and enqueued it at 02:11–02:12Z, keeping the 2 ellipsis rows as they are (A).
The seat's B reading 5862075970 was retracted in 5862098826 before any push.
Activity
os-zhuang commentedon Aug 25, 2026
Held — hard serial on
content/docs/**, behind #12236 and then #12237. Known trap: the description rewrite and the title rewrite touch the same frontmatter block of the same files, so they cannot run in parallel with each other either; and the length gate this card adds and the one #12237 may add should be one script, not two.claude commentedon Aug 31, 2026
pm:queue→pm:blocked— the hold was real, documented, and invisible to every querydomain:devxPM seat (#6023), R33. This card came up as the only remaining dispatch candidate in the lane, so this seat measured its fence before taking it rather than either obeying or ignoring it. The measurement said do not take it, and then said something more useful: the label was lying.What the hold actually is
os-zhuang recorded it on 2026-08-25, and it is not a vague fence:
⇒ a named predecessor chain and a named technical reason. Measured today:
<h1>)pm:blocked⇐ this card's immediate predecessor, has not runpm:queue— i.e. advertising itself as dispatchable⇒ dispatching this now would run the description rewrite before the title rewrite, into the same frontmatter block of the same files — the exact collision the hold names.
⛔ So the label is corrected, not the plan
Blocked-by:lines — none of them reads a sentence in a comment. For six days this card has read "free to dispatch" to every query while carrying an explicit serial hold, and it took being the last candidate in the lane for anyone to open it.⇒ ⭐ This is the same residue class this lane keeps filing (#13526, #13561) in its third shape: not a stale
pm:dispatched, not an assignee with no claim, but a real constraint that was written down in the one place the state machine cannot see. Putting it in the vocabulary means it now inherits the existing unlock scan and ageing machinery for free — ⛔ no new label, no new sweep.⛔ Nothing about the card's substance, grade, priority, size or ownership is touched.
domain:devx,priority:p1,size/l,repo:objectstackall stand. The hold is os-zhuang's and is not overridden — it is being made machine-readable.domain:devxhas zero dispatchable cards and runs below its standing concurrency of 4. That is a measured fact with its exclusions shown, ⛔ not an assertion — and a fence honoured is a legitimate reason to run short, where forcing a fourth by crossing one is not.Generated by Claude Code
claude commentedon Sep 1, 2026
Label note:
repo:objectstackremoved by the domain:devx seat (sessionsession_01WLJQhde67SeTccsmnBVarV) executing the triage retirement ruling on #13991 (comment 5487742846, 2026-09-01); ledger record landed via PR #14156. Routing is unchanged —documentation+domain:devxalready fully determine it.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Maintainer rulings recorded: scope, rewrite volume, no new gate · 2026-09-27T14:48Z
domain:devxseat 2 (#20163),session_018mA64scZ8fmpiPkrHVAwXj. Three questions were put to the maintainer in this session's chat, with the seat's recommendation marked on each. The answers are verbatim option picks. ⛔ Do not re-open them at dispatch.Readings they rest on (
origin/main0d3ec471, measured by the seat; the dev re-derives them):descriptionlengthcontent/docs/**minusreferences/**,releases/**)references/**releases/**(release-owned, ⛔ out)The card body's "median 46, 228 of 403 under 70" was the mix of the two populations. Authored pages have since improved; the generated half is still the bulk.
references/**. The generator lives underpackages/spec/scripts/(domain:spec), which makes this a cross-lane card by the maintainer's order. The claim will declare both halves' file surface, and the spec seats get a notice.check:*gate enforces the range" is dropped by this ruling. The fix is content only.The card body's maintainer-voice caveat still stands: the PR carries the rule and the full before/after table, and it stays draft for the maintainer's review.
Dispatch waits only on this card's
Blocked-by: #12237. PR #20170 is in the merge queue now. When it lands, this card is released and dispatched in the same round.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Release: the
pm:blockedhold (Blocked-by: #12237, recorded in5473104258) is discharged. #12237 closedcompletedat 15:13Z when PR #20170 squashed as5983d112. The unlock was re-derived before release: no other blocker is named on this card. The maintainer's rulings5856895911settle scope, rewrite volume and the gate. Destination: theClaim:below.Claim: PM loop round 2 (seat
domain:devx#2)Session:
session_018mA64scZ8fmpiPkrHVAwXjAccount:
os-litantBranch:
claude/issue-12238-description-ruleWorktree:
objectstack-issue-12238(in the dev's cloud container)Domain:
domain:devx(cross-lane by the maintainer's ruling 「一张卡全做」: the generator half lives indomain:spec'spackages/spec/scripts/)Seat:
domain:devx#2File surface:
description:line of the authoredcontent/docs/**/*.mdxpages outside 70–160 characters (about 52).packages/spec/scripts/build-docs.ts, the description emission only (:479module pages,:912category index pages), plus its tests.content/docs/references/**, regenerated bygen:docsand never hand-edited.packages/spec/src/**,title:/navTitle:lines,releases/**,apps/docs/**ormeta.json. On a breach the dev stops and explains it in the report.Container & model: L (card
size/l),mode:cloud,model: opus.dispatch-gates --tieranswers "no path-derived mandate … floor sonnet · default opus · ceiling fable". The default tier is chosen because the generator derivation and the rewrite of 52 rows need judgement.Clause-②: no
Thread-read: 5856895911
Serial constraints cleared: open-PR census at 2026-09-27T15:16Z covered all 12 open PRs, with file lists paged. Five touch authored or generated docs:
references/**pages. That is os-regen: resolved by regeneration on merge, withos-regen-merge.sh.os migrate meta --stored, not silence (#17321) #20244 (deployment/cli.mdx).None claims
build-docs.ts. LatestClaim:on the openpm:dispatchedcards: [finding] a joined report'schart— container andblocks[].chart— parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161, import mapping: no fieldMapping target can build a compound (address) value from separate CSV columns — a dotted target passes validate and dry-run, then fails every row on commit #20149, [finding] After PR #19962 rewrites the RLScheckdescribe, three texts still quote the old "defaults to USING clause if not specified" contract, and theusingtext never says it is the insert check when nocheckis declared #19967, [finding]os generateprojects ObjectStackDefinitionSchema through THREE override-lessz.toJSONSchemacalls — the schema an author's editor validates against is wider than the runtime, at 7 measured sites #19098 and [Deferred by ruling] Unify flow field-expression assignment onto the CEL engine — the B half of the #11060 ruling, awaiting maintainer appetite #11182 name authored pages for body edits. This card changes only onedescription:line per page, so this is ordinary concurrency. docs(spec): carry the page-title rule into the docs GENERATOR — 225 of 405 pages are emitted, so a hand edit is reverted (split (b) of #12237) #15403 (domain:spec) will editbuild-docs.ts:478, the title emission, adjacent to this card's:479. It is not in flight; a serial note is left on it.Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
os-dev-report
{ "issue": 12238, "status": "done", "branch": "claude/issue-12238-description-rule", "pr": "https://github.com/objectstack-ai/objectstack/pull/20258", "session": "session_01RCEEP3Z95jrpXCeF3it3Y2", "premise_still_valid": true, "summary": "Both halves in one draft PR, per ruling 「一张卡全做」. (1) The authored half rewrites the 52 descriptions outside 70-160 (15 under, 37 over; counts re-derived on base e0f17a3 and matching the seat's reading) to 120-155 characters. Only the description line changes; title and navTitle lines are untouched. (2) The generator half: build-docs.ts now takes each module page's description from the module doc block that findModuleDocBlock already selects, via the new scripts/lib/page-description.ts (18-case test). A thin lead is completed with the page's schema names, a module with no doc block falls back to a schema-name sentence, category overviews get an in-range phrase, and the value is emitted JSON-double-quoted. There is no src edit and no new gate. Result: all 392 non-release pages are in 70-160 with 0 exceptions (authored median 117 → 129; references median 28 → 138, 210 under 70 → 0). Module-page tally: 115 from the doc block, 19 doc block + schema names, 62 on the schema-name fallback (no module doc block), plus 14 category indexes. main moved during the work and was merged via os-regen-merge.sh. builtin-node-config.mdx changed on both sides and was regenerated in its own commit (4b7d63cc). PR assignee os-litant; card assignee not written.", "tests": "On HEAD 4b7d63cc, after a real `pnpm --filter @objectstack/spec build` (os-verify-lock VERDICT command-exit 0): `check:generated` exit 0 with every gate ✓, `check:docs` reporting '226 generated files in sync'. `dispatch-gates --commands` derived 94 families; `--ran` with recorded exit codes gave '94 derived, 92 run, 2 NOT-MEASURED, 0 UNRUN' (exit 0), and all 92 exit 0. `check:pm-dispatch-gates` (a 711s self-test, 1925 cases) was run once on pre-merge head 652b36d; the other 93 families were re-run on 4b7d63cc. NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt --re-measure, both exit 3 PREREQUISITE NOT MET because they need the full packages/* build closure; CI builds that. `pnpm --filter @objectstack/spec typecheck` exit 0. vitest: page-description.test.ts 18/18; local project 3 files / 68 tests; repo project (file-description, references-banner, category-title, root-index) 4 files / 146 tests, all pass. Ad hoc: js-yaml parse of all 392 frontmatters gave 0 errors and 0 descriptions outside 70-160. No ablation: no gate was added.", "gates": "94 derived / 92 run exit 0 / 2 NOT-MEASURED (exit 3: check:dual-build-cjs-loads, check:type-check-debt) / 0 unrun; check:generated green on 4b7d63cc after a real spec build", "line_budget": "265 files, +801 / -266 = 1,067 changed lines vs origin/main, generated files included — under 5,000", "files_changed": "52 authored content/docs/**/*.mdx (description line only); 210 content/docs/references/**/*.mdx (regenerated, description line only); packages/spec/scripts/build-docs.ts; packages/spec/scripts/lib/page-description.ts (new); packages/spec/scripts/page-description.test.ts (new)", "deviations": [ "The file surface named build-docs.ts 'description emission and its tests'. The emission rule lives in a NEW module, packages/spec/scripts/lib/page-description.ts, beside build-docs.ts. Reason: build-docs.ts is a side-effecting top-level script and cannot be unit-tested; file-description/format-type/schema-section were extracted the same way. The build-docs.ts hunks are listed in the PR body for #15403.", "check:pm-dispatch-gates: its 711s self-test verdict was taken on pre-merge head 652b36d and not re-run on 4b7d63cc. It is checker-health only and the merge touched none of its inputs." ], "changeset": "skip-changeset (measured): packages/spec files[] excludes scripts/; modulePageDescription/categoryIndexDescription have 0 hits in every shipped path after a build, while the positive control ObjectSchema has 56 hits in dist; content/docs belongs to no package", "mcp_calls": "3 — mcp__github__issue_read (get), mcp__github__issue_read (get_comments), mcp__github__list_pull_requests; all reads, no write tool", "api_writes": "3 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches) carrying 4 ops: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft forced) · labels_add skip-changeset (POST /issues/20258/labels) + assign os-litant (POST /issues/20258/assignees) · comment (POST /issues/12238/comments, this report). Plus git push (not REST)", "open_questions": [ { "question": "Two generated pages (references/data/validation, references/marketplace/package) end on a word-boundary ellipsis. Their first doc-block sentence is over 160 characters and has no clause boundary that keeps 70 or more. Acceptable?", "options": ["A keep the ellipsis (honest cut, in range)", "B fall back to the schema-name sentence for any over-long lead with no clause boundary"], "recommendation": "A: the page's own words beat a template, and the cut is visible rather than silent. B is a one-line change in shorten() if the maintainer's voice review prefers it." }, { "question": "Generated descriptions are written double-quoted (JSON.stringify), while authored pages mostly use plain scalars. Should the generator quote only when needed?", "options": ["A always quote (deterministic, safe for ': ' and quotes in doc-block prose)", "B quote only when the YAML needs it"], "recommendation": "A: every value comes from free prose, and one rule is easier to keep correct than a YAML-special-character predicate." } ], "out_of_scope_findings": [ "carrier: none (承接者:无) · 62 packages/spec/src modules have no module-level doc block, so their reference pages use the schema-name fallback (e.g. data/object, api/contract, kernel/plugin). Writing those docblocks is a src edit on the clause-② path limb. Noted in the PR's Acceptance notes and not filed: it is a polish item, not a defect, contract violation or authoring trap.", "carrier: none (承接者:无) · some module doc-block leads read as internal notes rather than reader copy (e.g. kernel/metadata-protection 'Phase 1 introduces the item-level lock …'). The generator reproduces the source faithfully. Noted in the Acceptance notes and not filed." ] }Generated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
PM review — PR #20258: ACCEPT (head
4b7d63cc) · 2026-09-27T16:51Zdomain:devxseat 2,session_018mA64scZ8fmpiPkrHVAwXj, reviewer of record. Report5857657129. Every reading below was taken on GitHub or the tree, not from the report.main, internal head. The first body line isFixes #12238, and it is the only closing keyword. Assigneeos-litant.e4621867: 52 authoredcontent/docs/**/*.mdx, 210content/docs/references/**(regenerated), and 3 underpackages/spec/scripts/. 0 underpackages/spec/src/**, so the clause-② path limb is not hit. 0 underreleases/**.description:line changed; 0 body and 0 other-frontmatter changes, sotitle:/navTitle:are untouched. Every old value was outside 70–160. Every new value is 138–155 (all 52 inside the card's 120–155).description:line only, 0 body changes. New values are 70–160, median 139.build-docs.tsswaps its two template emissions (:479module pages,:912category indexes) forlib/page-description.ts, which reads the doc blockfindModuleDocBlockalready selects. The output is deterministic: no clock, randomness or environment input. It adds a run-summary tally and has an 18-case test.build-docs.ts(scripts/lib/page-description.ts+ test) because the top-level script cannot be unit-tested. It follows the existinglib/extraction pattern and stays in the same directory and concern, so it is accepted.skip-changesetis correct:packages/specfiles[]does not shipscripts/, and the docs are not published.4b7d63ccmcp_calls3, all reads, 0 write tools. Writes went only through the relay.references/data/validation,references/marketplace/package) is copy, and the card says descriptions "read as sentences". It goes to the maintainer with the voice review, on PR docs(content,spec): search-ready page descriptions — 52 authored rewrites and derived reference descriptions #20258.src/**edit on the clause-② path.needs-user-decisionand a quick-read; this card stayspm:dispatched.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Maintainer approval recorded — PR #20258 · patch round ordered · 2026-09-28T02:13Z
domain:devxseat 2,session_018mA64scZ8fmpiPkrHVAwXj. Every reading below was taken on GitHub at this act.Approval. Review
5333290305byhotlong,APPROVED, submitted 2026-09-28T02:11:00Z on head4b7d63cc, body empty, 0 inline comments.hotlongis one of the two authorized approval accounts (os-zhuang,hotlong). No new comment on this card or on the PR since the quick-read5857844797. This discharges the card body's maintainer voice review: the rule and the before/after tables stand as approved. Under the protocol, a later push does not expire the approval.needs-user-decisionleaves the PR in the same act.The A/B row. The approval carries no text, so it does not pick A or B. Seat reading:
…rows (references/data/validation,references/marketplace/package) do not meet it.Why a push is owed anyway (landing section A).
mainmoved 45 commits past the PR's merge basee4621867(nowd498113b).content/docs/references/**pages changed on both sides:api/{dispatcher,error-code-ledger,plugin-rest-api,rest-server},automation/schemaless-node-config,data/{mapping,validation},security/{permission,rls},ui/report.packages/spec/src/**doc sources also changed onmain.os-regen-merge.shplus a full regenerate is required before the PR queues.Patch round (sent to the dev session
session_01RCEEP3Z95jrpXCeF3it3Y2):origin/mainviabash scripts/pm/os-regen-merge.sh, then regenerate the docs. ⛔ No rebase and no force-push.packages/spec/scripts/lib/page-description.ts, a doc-block lead that cannot fit 70–160 without an ellipsis cut falls back to the schema-name sentence. There is no…output path left. Update the test battery to pin this.os-dev-reportdelta on this card and update the PR body's generator section.After the delta, the seat re-reviews and then lands. The card stays
pm:dispatched.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Correction to
5862075970— B and the patch round RETRACTED · the maintainer landed the PR as it stands · 2026-09-28T02:15Zdomain:devxseat 2,session_018mA64scZ8fmpiPkrHVAwXj.What I missed. The PR timeline shows that
hotlongdid more than approve.hotlongthen landed the PR personally, and all of it came BEFORE my record5862075970:APPROVEDat 2026-09-28T02:11:00Z;ready_for_reviewat 2026-09-28T02:11:04Z;auto_merge_enabledat 2026-09-28T02:11:18Z;added_to_merge_queueat 2026-09-28T02:12:19Z.The events reached this seat late. I wrote the record after reading the reviews and comments, but without re-reading the PR object or its timeline. That was the lapse.
Reading, corrected. The maintainer's own landing act on head
4b7d63ccsettles the A/B row as A: the two…rows stay as they are. It also answers the regen question for this landing. The merge queue'smerge_grouprun is the drift gate on the merged tree. So:5862075970.session_01RCEEP3Z95jrpXCeF3it3Y2was interrupted at 2026-09-28T02:14:54Z. At that moment the branch head was still4b7d63cc(git ls-remote), so nothing was pushed and the queue entry is intact.needs-user-decisionremoval from the PR stands, because the approval discharged it.Now. The PR is in the merge queue (
gh-readonly-queue/main/pr-20258-8af914a3…), with auto-merge byhotlong. The seat follows it to MERGED and then closes out this card.merge_groupis red (for example generated drift from the 45maincommits), the seat handles the kick-out under landing section A:os-regen-merge.sh+ regenerate, with the approved content unchanged, and A kept.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Landed — PR #20258 merged as
9daced0b· 2026-09-28T02:43Zdomain:devxseat 2,session_018mA64scZ8fmpiPkrHVAwXj. Landing record for claim5857109111.main:9daced0bisorigin/main's HEAD at this read. It touches 265 files, +801 / −266.hotlongmerged it through the merge queue at 2026-09-28T02:41:39Z. Spot reads onorigin/main:kernel/architecture.mdxandpermissions/authorization.mdxcarry the approved rewrittendescription:rows (143 and 156 characters).references/data/mapping.mdxcarries a derived sentence in place of the old template.build-docs.tsemitsmodulePageDescription(…)(:489) andcategoryIndexDescription(…)(:931).… protocol schemas/Complete reference for all …template descriptions remain undercontent/docs/references/**.mainhad also changed carry, onmain, exactly the PR head'sdescription:lines.merge_grouprun on9daced0bwas green:CI,Build Docs,Lint & Repo Gates,Lint & Type Check,Governed Surface Guard,Spec Liveness Check,Closing-Target Claim Guard.5857839052.hotlongAPPROVED (review5333290305), then marked the PR ready, enabled auto-merge and enqueued it at 02:11–02:12Z, keeping the 2 ellipsis rows as they are (A).5862075970was retracted in5862098826before any push.5856895911: 「一张卡全做」 · 「只改超范围的 52 页」 · 「不加门禁」.completedbyFixes #12238.pm:dispatchedis removed in this act;domain:devx/priority:p1stay.domain:spec) can takebuild-docs.ts:478(title emission) with no serial wait. PR docs(content,spec): search-ready page descriptions — 52 authored rewrites and derived reference descriptions #20258's body names the description hunks.session_01RCEEP3Z95jrpXCeF3it3Y2is archived after this record.Generated by Claude Code
docs(content,spec): search-ready page descriptions — 52 authored rewr…