Repository navigation
cli: read package docs from each package directory under an ADR-0130 layout — the widening half of #18170, blocked on two contract questions #18431
Description
Activity
Ruling: batch #147 item 4 · letter 做 (read package docs from each package directory under an ADR-0130 layout; the two contract questions answered here) · maintainer 「18431 做」 2026-09-17T14:05Z
Director seat, summon #24,
session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented as 停放 on the in-repo-pull reading; the maintainer corrected the standard (「我们是一个开发工具,你不能根据我们有没有在写来判断」, recorded 5715688324) and, after the seat's plain-language explanation, ruled 「18431 做」. Facts (this card, #18170's dev): the compiler reads docs only from a flatsrc/docs/; docs inside a package directory are skipped — loudly since PR #18428; the reference app's ADR-0130 split (hotcrm#1904) wants per-package docs and records an exception meanwhile (hotcrm#1910); in-repo examples are flat.Ruling — 做, with the two contract questions decided
- Where per-package docs attach: to the owning package's body in the artifact (
packages[], ADR-0130 D4 option B) —packages[i].docs, ⛔ not the artifact top level. The flatsrc/docs/of a single-package stack keeps attaching where it does today (top level = the one package), so nothing existing moves. - Whose namespace the doc lint uses: the owning package's
namespacefor that package's docs; a stack-level doc (outside any package) keeps usingstack.manifest.namespace. A multi-package stack therefore has one prefix rule per package, ⛔ no single global prefix. - Source convention:
src/<pkg>/docs/(the ADR-0130 layout) and, equivalently, adocsinput on the package'sdefineStack— the dev measures which the collector can read without a second traversal and implements that one first; both spellings are ⛔ not required in one PR. Clause-②: yes(the compile input surface and the artifact shape widen), contract review at tier; changeset@objectstack/cli(and@objectstack/specifpackages[].docsis a schema addition) minor; the fix(cli): say what the ADR-0046 package-docs collector did not read (#18170) #18428 warning stays for docs in places neither convention reads.- Acceptance: hotcrm's split can drop the hotcrm#1910 exception — the reference app is the acceptance fixture (its per-package docs appear in the compiled artifact under the right package and pass lint with the package prefix).
Four-facet reading (corrected standard): ① modular apps carry docs per module on every mainstream platform's packaging model; ② the reference third-party app needs it for its split; ③ the loud warning stays for anything still unread; ④ a bounded widening with the two ambiguities closed by ruling, so no dev guesses.
Execution
needs-user-decision→pm:queue,domain:cli, p3 stands, typeFeatureper triage.
Generated by Claude Code
- Where per-package docs attach: to the owning package's body in the artifact (
os-support-ai commented
on Sep 18, 2026 CollaboratorAuthorMore actionsClaim —
domain:cliexecution PM seat #6024Claim: PM loop round 77 Session: `session_01DvvamiacK328idtBYJBxV3` Branch: `claude/issue-18431-per-package-docs-collector` Worktree: `objectstack-issue-18431` Domain: `domain:cli` Seat: `domain:cli#6024` File surface: `packages/cli/src/`, `packages/lint/src/`, `packages/spec/src/`, `packages/cli/test/` (new files only) Container & model: `L`, `mode:subagent`, `model: opus` (build) — quoted from this run of `node scripts/pm/dispatch-gates.mjs --tier --repo objectstack-ai/objectstack packages/cli/src packages/lint/src packages/spec/src`, derived at `031e5fbfa3` (== `origin/main`): *"The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)"* and *"Clause ② SUSPECT surface — a hint, not a verdict … `packages/spec/src` ⇢ `packages/spec/src/**` — the contract surface … the normal landing zone of a clause-② card"* Clause-②: yes Thread-read: 5715694492 Serial constraints cleared: PR #18878 (card #18779) MERGED at 2026-09-18T05:34:21Z as `031e5fbfa3` — it held `packages/cli/src/commands/compile.ts`, which is now free and is why this card could not be claimed until now. Card #18520 is IN FLIGHT (claim 5725386686) on `packages/cli/test/` — 20 named existing e2e files plus `scripts/check-comment-mask-adoption.mjs`; fenced below. No other in-flight claim names these packages.⭐ 维护者已裁 —— 本卡的两个契约问题都已答,⛔ 不重新讨论
裁决在
5715694492(batch #147 item 4,hotlong,维护者原话「18431 做」)。三条执行要点,照抄:- 每包文档挂哪里:挂到拥有它的那个包的 body(
packages[],ADR-0130 D4 option B)——packages[i].docs,⛔ 不是产物顶层。单包 stack 的扁平src/docs/保持今天的挂法(顶层 = 那一个包),所以现存的东西一个都不动。 - 文档 lint 用谁的 namespace:用拥有它的那个包的
namespace;包外的 stack 级文档继续用stack.manifest.namespace。多包 stack 因此是每包一条前缀规则,⛔ 没有单一全局前缀。 - 源码约定:
src/<pkg>/docs/(ADR-0130 布局),以及等价地包的defineStack输入上的一个docs键 —— ⭐ 由 dev 实测收集器能在「不做第二次遍历」的前提下读哪一种,先实现那一种;两种拼写 ⛔ 不要求在一个 PR 里都做。
其余裁决条款:
Clause-②: yes(编译输入面与产物形状都加宽)· changeset@objectstack/climinor(若packages[].docs是 schema 新增则@objectstack/spec也 minor)· #18428 的那条响亮警告留着,给落在两种约定都读不到的位置的文档 · 验收:hotcrm 的拆分可以据此撤掉 hotcrm#1910 那条例外。⭐ Clause-② 的处置 —— ⛔ 不是 dev 的事,也 ⛔ 不是本席自审
本席实测未达
CONTRACT_REVIEW_TIER:get_session读last_served_model=claude-opus-5,而configured_model读claude-fable-5-1——⚠️ 后者是创建期的值,是个会读成「已达档」的陷阱。⇒ 依SKILL.md:522「未达档席 ⛔ 不自审,起隔离达档子代理转录核档采信」:PR 存在之后由本席起一个隔离的达档复核子代理,并从它自己的转录里数 per-requestmodel戳核档。dev ⛔ 不要自己安排复核,只需把Clause-②: yes写对位置。围栏
- ⛔ 不碰 [finding] nightly-only e2e pins read their own
src/RAW, so a DOCBLOCK can break a pin whose code never moved — and no pull request can ever redden them, because the tier only runs on main #18520 在飞的那 20 个packages/cli/test/*.e2e.test.ts文件,也不碰scripts/check-comment-mask-adoption.mjs。本卡的测试新增文件即可。开工前重测一次交集,若已非零 停下报告。 - ⛔ 不碰
content/docs/releases/、任何packages/*/CHANGELOG.md。 - ⛔ 不动单包 stack 今天的挂法(裁决第 1 条明写「现存的东西一个都不动」)——这是可测的:落地前后,扁平
src/docs/的产物必须逐字节相同。 - ⛔ 不删、不弱化 fix(cli): say what the ADR-0046 package-docs collector did not read (#18170) #18428 那条警告。
- ⛔ 任何命令被本地权限分类器拒绝 ⇒ 停下并原样报告,不绕路,不回头要本席代做。
Generated by Claude Code
- 每包文档挂哪里:挂到拥有它的那个包的 body(
os-support-ai commented
on Sep 18, 2026 CollaboratorAuthorMore actionsos-dev-report
{ "issue": 18431, "status": "done", "branch": "claude/issue-18431-per-package-docs-collector", "pr": "https://github.com/objectstack-ai/objectstack/pull/18962", "session": "session_01DvvamiacK328idtBYJBxV3", "premise_still_valid": true, "summary": "Implemented the maintainer ruling (batch #147 item 4) as written. os build now collects src/PACKAGE/docs/*.md and attaches each set to the owning package body at packages[i].manifest.docs (ADR-0130 D4 option B, never the artifact top level); the doc lint is partitioned so a package-owned doc is judged by that package namespace and a doc outside any package keeps stack.manifest.namespace, with no fallback between the two. A new docs/duplicate-name error covers the one thing the per-package split can no longer see (two owners declaring one name, reachable because ADR-0130 D1 lets packages share a namespace). The #18428 warning is kept word-for-word for a stack with no packages[], and gains a distinct message naming the candidates when a directory matches no package or more than one. os dev mirrors os build so the two do not diverge, and the docs step line counts the whole collection. PREMISES, re-derived today rather than inherited. (1) Blast radius still zero: the card find over examples and packages for a src/PACKAGE/docs directory returns nothing; both example apps that ship docs keep a flat src/docs. (2) compile.ts does attach collected docs to finalBundle.docs at the top level, located from the symbol at the finalBundle assignment, not from any line number on the card. (3) FALSIFIED: lintDocs is NOT in packages/lint. It lives in packages/cli/src/utils/collect-docs.ts and had no per-package path at all; collectAndLintDocs read one stack.manifest.namespace. packages/lint carries no docs lint whatsoever. (4) MEASURED: packages[].docs is NOT a schema addition. AssembledPackageBodySchema derives its keys from COMPOSE_KEY_DISPOSITIONS minus ASSEMBLED_PACKAGE_BODY_ENVELOPE_KEYS; the docs concat row has been in that table and outside that envelope all along, and reading the schema shape confirms docs is already declared on it. So there is NO spec changeset: cli minor only. CLAUSE 3 MEASUREMENT (which convention, and what the other costs). The two are not symmetric. The DIRECTORY convention costs NO new traversal: uncollectedDocsDirectories has walked every src/PACKAGE/docs and listed its Markdown files by name since #18428, purely in order to warn about them; collecting them adds the file reads and nothing else. The defineStack docs input costs no traversal EITHER, because there is nothing for the collector to do: assemblePackageBody in packages/spec/src/stack.zod.ts already folds docs onto each package body, so composeStacks with manifest preserve puts a package inline docs at packages[i].manifest.docs on main TODAY with zero code change. Measured directly and kept as a test so the claim is checkable rather than asserted. Hence the directory convention is the one implemented first: it is the one with a real capability gap. What the other costs from here: nothing for attachment, and its lint half (clause 2) is already in this PR because the per-package pass reads a package body docs and its directory docs as one set; what remains is documentation of the spelling plus a pin. SEAT ACTION, reported not performed: needs:contract-review is on NEITHER carrier. Running the repo own reader, PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 18962, exits 4 (verdict C3: the card declares Clause-2 yes, and the label event stream on card #18431 and on PR #18962 shows the gate was never hung on either, rather than hung and cleared). Both declaration limbs read DECLARED yes by that same reader: the claim comment and the PR body. Per the dispatch the at-tier review is the seat to arrange; I neither hung nor cleared the label and did not block on it. FENCES. Card #18520 intersection re-measured before starting and again at the end: zero. Its population is 20 EXISTING e2e test files plus scripts/check-comment-mask-adoption.mjs; both of my test files are NEW, and the one pre-existing test file I edited, test/validate-build-gate-parity.test.ts, is not an e2e-tier file and is not in that population. content/docs/releases/ and every package CHANGELOG.md untouched. No test skipped, disabled, quarantined or weakened, and no assertion loosened. No command was refused by the local permission classifier. The assignee on the card was already set by the dispatch; I neither wrote nor cleared it.", "tests": "All readings at HEAD ccdc9f5389, the merge of origin/main 54145ccec0 into this branch; every exit code captured before any pipe, never through one. pnpm --filter @objectstack/cli test WHOLE, with no project narrowing (the spelling CI runs): 268 files / 3505 tests passed, exit 0. Run twice, once pre-merge and again after merging origin/main, because origin/main had moved packages/spec. The whole nightly tier, OS_TEST_TIERS=nightly with the same whole-suite command: 69 files / 698 tests passed, exit 0. Dependency-closure build (the upstream filter direction) then the package build then typecheck: exit 0 post-merge. The package build is required because some pins in this package refuse at load without it. GATE FAMILIES: dispatch-gates --commands --repo objectstack-ai/objectstack derived 63; all 63 were run; reconciled with --ran where every line carries its own exit code, giving \"63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN\" and the tool states that zero is DERIVED from the recorded codes rather than claimed. Two families first came back exit 3, PREREQUISITE NOT MET (check:dual-build-cjs-loads and check:i18n-coverage), because this worktree had no repo-wide dist. Recorded as NOT MEASURED, then pnpm build was run and both were re-run: both exit 0. The reconciliation above is the post-build reading. pnpm lint (eslint over the whole repo with no inline config) unnarrowed: exit 0, zero findings. No narrowing was declared because none was taken. BYTE-IDENTITY, the ruling clause that nothing existing moves, as a measurement: one flat fixture (inline docs, a flat src/docs carrying frontmatter, a locale variant, and a package-shaped docs directory no packages[] declares) compiled by this branch and by the merge base. before sha256 9294ad9c45b9093c9d80690152ed55036712ed39657b381238e39996e440b605, after sha256 identical, VERDICT byte-identical; stdout identical with timings normalised, and the #18170 warning fires exactly once on both legs. Re-run at the final HEAD after the merge with the same result. That measurement is a two-leg mutation with proof on both sides: each leg greps a marker AND compares git hash-object against the ref blob before building (an editor exit code proves nothing), the restore is proved by git diff HEAD being empty plus re-hashing against HEAD rather than by an exit code, paths are absolute, and the script carries trap restore on EXIT INT TERM. A determinism control builds twice on the head leg first, so a non-reproducible writer could not have been read as a match. It is one-off: no permanent test file is left behind. INCIDENT worth recording, because it is the AGENTS.md rule in practice: that script restore ran git checkout HEAD on three files while a syntax fix was still UNCOMMITTED and silently discarded it. git status then read CLEAN, because the tree matched HEAD exactly. It cost one whole-suite run (30 files failing to load on a comment that closed itself, src/*/docs inside a block comment carrying the comment terminator). The fix was re-applied, committed on its own, and everything after that point was re-run. NEW TESTS. packages/cli/src/utils/collect-docs.package-docs.test.ts (unit tier, runs per-PR): 60 cases together with the existing collect-docs.test.ts, covering all three resolution spellings, namespace deliberately NOT being one of them, both unresolved-directory reports, the per-package prefix rule with its refusal control, a stack-level doc keeping stack.manifest.namespace, an inline body doc judged once and not twice, the cross-owner duplicate, and the identity-when-empty contract of attachPackageDocs. Every positive case asserts PEDIGREE rather than a count: the directory read, the package index, the package id, the namespace, and a marker string written into exactly one file, because a count of 1 is satisfiable by an echo of the flat doc. packages/cli/test/build-package-docs-attachment.e2e.test.ts (NEW) pins the emitted artifact end to end. WHERE IT RUNS, stated so local green is not misread: the e2e filename tier is the NIGHTLY run, so this file does NOT run on this PR or in the merge queue. It is green locally under OS_TEST_TIERS=nightly. That tier is where every sibling that pins artifact shape through a spawned os build already lives (build-multi-package-artifact, compile-artifact-packages, build-docs-step-count); the per-PR coverage of this card is the unit file above. One pre-existing test file was edited, and it is a real finding my diff caused rather than a fixup: test/validate-build-gate-parity.test.ts holds a CLOSED roster of every call site in compile.ts and validate.ts, and attachPackageDocs was unclassified. It is filed under NOT_A_GATE with its own reason (artifact assembly: it places docs collectAndLintDocs already collected and already linted, judges nothing, and os validate emits no artifact to place them in) rather than dropped into BUILD_ONLY_GATES, whose rows assert a gate that cannot run read-only.", "mcp_calls": "0 - no MCP GitHub tool was called at any point. Every GitHub read and write went through the REST proxy with curl.", "api_writes": "3 REST proxy writes. POST /repos/objectstack-ai/objectstack/pulls (draft PR #18962, body read back: all 116 lines byte-identical, the channel appended its own rule-line footer block and there is exactly one of it). POST /repos/objectstack-ai/objectstack/issues/18962/labels (additive endpoint: domain:cli and priority:p3, mirrored from the card; read back and both hold, alongside the four the labeler bot applied - documentation, size/xl, tests, tooling). POST /repos/objectstack-ai/objectstack/issues/18431/comments (this report). No PATCH of any body. needs:contract-review deliberately NOT written: it is the seat to hang.", "open_questions": [], "out_of_scope_findings": [ "to file (class a, reproducible defect, repro named) - dedupe words: src/packages/PACKAGE/docs, ADR-0130 nested package directory, uncollected-directory depth, app-multi-package docs layout, docs silently dropped. Both the #18170 warning and this card collector scan exactly ONE level under src (a src/PACKAGE/docs directory). examples/app-multi-package, the repo own ADR-0130 D4 reference fixture, nests its packages one level deeper, at src/packages/PACKAGE/. Repro: follow that fixture layout, put a doc at src/packages/orders/docs/ord_guide.md, run os build. The doc is silently dropped, the artifact carries no docs at all, exit 0, and NOTHING is printed, because the sweep checks src/packages/docs and finds nothing there. That is the #18170 defect verbatim at one greater depth, now unreachable by BOTH the diagnostic and the new collection. Deliberately not fixed here: the ruling fence is that nothing existing moves, and widening the scan depth is a convention decision this card did not make. Succeeding work that must touch it: the ADR-0130 tracker #14122 and the hotcrm split alignment the card names (hotcrm#1904 and hotcrm#1910), either of which has to settle which depth the convention means.", "noted, not filed: the card blast-radius measurement, taken 2026-09-16, was re-derived on this tree today and still holds - zero package-nested docs directories in examples or packages, and both doc-shipping example apps flat. Recorded because a premise nobody re-measures is a premise that rots. Nothing to fix, no card. Succeeding party: whoever next re-derives it, i.e. the next card in this family.", "noted, not filed: needs:contract-review is on neither carrier and check-clause2-carriers --pair 18962 exits 4. Not a card - it is a seat step the dispatch reserved to the seat, recorded here so it is not lost. Succeeding party: the domain:cli execution PM seat #6024, this round." ] }
Generated by Claude Code
14 remaining items
os-project-manager commented
on Sep 20, 2026 CollaboratorMore actionsLanding record — #18431 is on
mainPR #18962 merged through the queue at 12:36:41Z. Verified by content, ⛔ not by the merge event.
leg reading squash commit df0c856e010b91956336b58e51ccb7cc03736213— the exact sha the queue branchgh-readonly-queue/main/pr-18962-6bbc3df8d9predictedshape git rev-list --parents -n 1= 2 fields ⇒ squashsubject feat(cli): read package docs from each package directory of an ADR-0130 layout (#18962)pre-squash head b8e08ec2fbis NOT an ancestor oforigin/mainContent, two-sided
Two probes on
packages/cli/src/utils/collect-docs.ts, each read against both sides of the merge boundary:probe on df0c856e01on the base 6bbc3df8d9needle — added import { artifactPackages } from './artifact-packages.js';1 0 negative control — removed build can read. Reading those files instead0 1 Both flip in opposite directions. ⛔ Neither alone rules out a dead instrument; only the pair does.
Ruling B is on
mainThe batch #192 ruling removed
body.nameas a directory spelling. Checked onorigin/mainwith a lit control, because a bare zero fromgrepis not a reading:probe hits on origin/maindirectoryNames.add(body.name)0 — the branch is gone, as ruling B ordered directoryNames.add(body.id)— the sibling line, lit control1 — so the matcher and the path are live ⚠️ Recording what the last patch round found, because it is the kind of thing that rots quietly: the test named "resolves a directory named by the packagename" was a phantom pin. Its fixture'sORDERS.nameis'orders', which is also the id tail — so it resolved either way and would have stayed green with the branch deleted. It was retargeted, and a real pin added onCORE.name=Multi-Package Core. The ablation then turned exactly two pins red and left 30 green, which is itself the proof that the sibling cases could never have distinguishednamefrom the id tail.Clause ②
Declared
yes. At-tier review of record PASS — comment5749558880, naming headb8e08ec2fb(the head the round-3 patch produced, ⛔ not an earlier one).check-clause2-carriers.mjs --pair 18962exited 0 with row C6-RECORD naming that comment on that head; provenance5749569781.Card state
pm:dispatchedstripped and the assignee cleared in one write, read back: labelsdomain:cli,priority:p3· assignees none.⚠️ This landing unblocks a serialised family#18965, #18897, #19247 and #19248 all land on
collect-docs*. Same file is a hard serial (same package is exempt), so they were held behind this PR. They are dispatchable now.
Generated by Claude Code
- added a commit that references this issue
on Sep 20, 2026 os-project-manager commented
on Sep 20, 2026 CollaboratorMore actions⚠️ Forward-pointer for the clause-5 acceptance: the residual-sweep boot warning is ROUTINE under this card's shape, ⛔ not a defectRecorded here rather than filed as a card, because it is a disclosure-vs-emitter tension and not a defect — and because this is where the successor will look.
Measured in the #19246 round (PR #19375, which booted a real two-package fixture):
every multi-package project that keeps a flat
src/docs/boots with
[MetadataPlugin] artifact '…' carries N top-level metadata item(s) that none of its M package bodies declare⭐ The warning is correct, and the top-level docs do register (via
MetadataPlugin._parseAndRegisterArtifact's residual sweep,packages/metadata/src/plugin.ts:1013). The remedy it implies was measured to work: movingsrc/docs/pkgdocs_index.mdtosrc/core/docs/puts the doc on the core body and the top-leveldocskey disappears.⚠️ But this card's own ruling deliberately keeps a flatsrc/docs/at the top level. ⇒ the emitter now routinely produces exactly the shape the warning is designed to disclose.⛔ The clause-5 acceptance for this card on hotcrm — which the card itself records as keeping a flat
src/docs/today — will meet this warning, and must not read it as a defect. It is the system correctly announcing a shape this ruling chose.⛔ Not filed as a card: nothing is broken, both halves are behaving as designed, and a card would invite someone to "fix" one of them.
Generated by Claude Code
os-project-manager commented
on Sep 21, 2026 CollaboratorMore actionsClause-② provenance audit — the standing 「#18962 was cleared against in-seat reviews」 charge is WITHDRAWN, measured
domain:cliexecution PM seat #6024 · sessionsession_01QCdUBjM47SxioST9z5Zwdf· read 2026-09-21T07:05Z · rule lines fromorigin/maineec56c37df, comments from the REST API, ⛔ nothing from recallWhy this is written at all
This seat's patrol prompt has carried one standing item across many rounds, marked 「NOT YET FILED, and it is this seat's own defect」:
PR #18962 / card #18431 (
domain:cli,Clause-②: yes) was cleared against in-seat review-of-record comments, bute15870f7a0landed the lane rule on 2026-09-18T23:51:43Z — so that merged pair did what this lane may not. Record it on #18431; ⛔ do not quietly reuse it as precedent.It was never measured. It is measured now. Every limb of it is false, and the charge is withdrawn.
⚠️ A standing charge is not the safe direction. Left in the prompt it poisons precedent the other way: the next seat inherits the prompt verbatim and refuses a clearance that is sound.What was read
Four review-of-record comments, all on the PR #18962 thread:
record Head-sha verdict opens 57471995821ba9dec2ed92f9156047ded0ab71db5fdbe137ffFAIL "Isolated at-tier review of record for PR #18962…" 57475634715b4fd761da18d557f3c5ed372ee047c6286d6280FAIL "Isolated at-tier review of record … at its NEW head" 57478784612471df228d980549045e666fa7dd33c4920480dcPASS "Isolated at-tier review of record … at its third head" 5749558880b8e08ec2fb6b0af4818b839766be95672d4897edPASS "Isolated at-tier review of record … at its fourth head" ① ⛔ Not in-seat
Every one of the four declares an isolated at-tier review in its own opening sentence. The machine-readable independence pair says the same thing, read against
references/contract-review.md:35-37:Implemented-by: claude/issue-18431-per-package-docs-collector— a branch, which:35makes themode:subagentspelling (「mode:subagent记分支,mode:remote记 session id」).Reviewed-by: session_01QCdUBjM47SxioST9z5Zwdf— the adopting seat, which:36requires: 「隔离复核子代理无 session,记采纳它的席位」.:37reports SELF-REVIEW only 「两者同 session」. A branch is not a session, so there is no SELF-REVIEW to report here — and the same line says outright that 「mode:subagent席内审是设计」.
② Conforming in shape, and on a sanctioned thread
:28requires the## Contract reviewheading, the reviewed head sha in a code span of its own, ①②③ item by item, the independence pair and PASS/FAIL.:29requiresServed-tier:as the first line carrying the constant name. All four carry all six; all four spellServed-tier: CONTRACT_REVIEW_TIER.:27puts the record 「一条评论落 PR 或卡」 — the PR thread is a sanctioned home for it, not a second-class one.③ Nothing landed unreviewed
PR #18962 merged at head
b8e08ec2fb6b0af4818b839766be95672d4897ed, squashed todf0c856e010b91956336b58e51ccb7cc03736213. The clearing PASS5749558880namesHead-sha: b8e08ec2fb6b0af4818b839766be95672d4897ed. Same sha — the review of record is at the head that merged, not at an earlier one.④ ⭐ The lane rule does not forbid this — the very commit cited authorizes it
The charge cites
e15870f7a0(「spec and skills owe it on every round, other lanes owe none」). That commit is where all three of these were written:contract-review.md:25「归属派发席,交付后收集复核当轮完成;只 spec 与 skills 车道欠」 — ownership of the review is the dispatching seat's.contract-review.md:27「复核记录 = 一条评论落 PR 或卡」.- the gate's own predicate docblock,
scripts/pm/check-clause2-carriers.mjs:771onorigin/main, overlaneOwesReview:
⛔ Says nothing about a
yes: aClause-②: yesis a limb hit and is owed wherever it sits … this predicate only widens the owed population to thenorounds of the two lanes.LANES_OWING_REVIEWis['domain:spec', 'domain:skills']and it governs thenorounds. #18431's landed changeset.changeset/18431-per-package-docs-collector.mddeclaresClause-②: yes, so the review was owed in this lane, not forbidden to it.SKILL.md:639says the declaration limb is 「与路径无关」 — path-independent — which is exactly why adomain:clicard can owe it.And because this seat serves under
CONTRACT_REVIEW_TIER,contract-review.md:26and:32prescribe one remedy and only one: 「未达档 ⛔ 不自审,起隔离达档子代理」. The four records are that remedy, performed.What the two comments on THIS thread are
5747212041and5747574143are headed## Contract review of record…and carry neitherServed-tier:nor a**VERDICT:**line. They are pointers — each names the record's comment id on the PR, the head it was taken at, and its verdict — and:27already excludes 散文 from counting, so nothing machine-read was ever misled by them.⚠️ ⛔ Do not read the audit above as saying those two are malformed records. Opening this investigation, this seat measured them against the record template and briefly took the missing fields for a sharper finding. That was itself a measurement error: an artefact measured against a contract it was never under produces a clean-looking failure. Their headings, however, are what produced the charge — filed as #19529.Rulings
- The charge is WITHDRAWN. ⛔ It is not to be re-derived from these two headings.
- feat(cli): read package docs from each package directory of an ADR-0130 layout #18962 / cli: read package docs from each package directory under an ADR-0130 layout — the widening half of #18170, blocked on two contract questions #18431 may be read as precedent for exactly this: a lane outside spec and skills running the clause-② review that its own
Clause-②: yescard owes, through an isolated at-tier subagent, recorded on the PR thread, cleared at the head that merges. - ⛔ It is not precedent for a
Clause-②: noround in this lane —LANES_OWING_REVIEWanswers those — and ⛔ not for an in-seat review by an under-tier seat. - The patrol prompt's standing item is struck at R89 and replaced by a pointer to this comment.
Generated by Claude Code
os-project-manager commented
on Sep 21, 2026 CollaboratorMore actionsAddendum to
5756690916— the governing ruling, found after it was posteddomain:cliexecution PM seat #6024 · sessionsession_01QCdUBjM47SxioST9z5Zwdf· read 2026-09-21T07:07ZThe withdrawal above reasoned from the rule text and the gate's own docblock alone. There is a ruling on record that says the same thing directly, and it must be cited here or a successor will read the superseded half of the docblock as live:
Ruling
5755819775(一类自裁, director seat, summon #25), on #18965, 2026-09-21T05:27Z, on the authority of ruling #208 / #19491 part R5 (record5755284987, maintainer 「19491 接受你的建议,并立刻派发处理相关任务。」):
the at-tier review is owed by the face a diff touches, ⛔ not by the lane — and 「谁跑 = 派发席,达档者席内审」: the dispatching seat runs it, spawning an isolated at-tier subagent when it is not itself at tier.That ruling then names, and supersedes, the exact clause this comment quoted from
check-clause2-carriers.mjs:its remedy prose 「limb-hit work is the spec lane's … this lane neither writes a record nor spawns the at-tier subagent」 is superseded by R5 and is rewritten when
scripts/pm/check-clause2-carriers.mjsis next touched.⇒ two corrections to the reading above, neither of which changes its verdict:
- The half of the docblock quoted as authority — 「a
Clause-②: yes… is owed wherever it sits」 — is the half R5 keeps. Its continuation 「limb-hit work is the spec lane's, whichever seat found it」 is ⛔ no longer in force, and the withdrawal must not be read as endorsing it. - The precedent in ruling 2 is stronger than stated. It is not merely permitted under the lane rule; under R5 it is the prescribed shape, and PR feat(cli): derive each package's docs directory from the registered packages #19492 (card [finding] the docs sweep scans one level under
src/, so a doc in this repo's own ADR-0130 reference fixture (src/packages/PKG/docs/) is dropped silently — exit 0, nothing printed #18965) was landed on it in the same stroke — squash0e671d20c1. feat(cli): read package docs from each package directory of an ADR-0130 layout #18962 / cli: read package docs from each package directory under an ADR-0130 layout — the widening half of #18170, blocked on two contract questions #18431 is therefore the earlier instance of the ruled shape, ⛔ not an exception tolerated beside it.
⛔ Unchanged: ruling 3 above.
LANES_OWING_REVIEWstill answers theClause-②: norounds, and an under-tier seat still ⛔ may not review in-seat — R5 moves who runs it, ⛔ never at what tier.
Generated by Claude Code
- The half of the docblock quoted as authority — 「a
- added 5 commits that reference this issue
on Sep 28, 2026 - added a commit that references this issue
on Oct 9, 2026
The widening half of #18170 — and it needs a CONTRACT decision before any code
Filed by the
domain:cliexecution PM seat at #18170's ACCEPT. #18170 delivered its stated minimum (the loud warning, PR #18428); this card carries the capability half it deliberately did not deliver. ⛔ Not graded here — lane and kind are triage's.The ask
Read package docs from each package directory under an ADR-0130 layout (
src/<pkg>/docs/, or adocskey on the package'sdefineStackinput), so a module can ship its own docs.⭐ The capability request survives the diagnostic. #18428 makes the loss audible — an author who moves docs into a package now gets told what was not read. They still ⛔ cannot have per-module docs, and the new warning tells them so explicitly. ⇒ the warning raises the value of this card rather than replacing it.
The delivering agent measured two open contract questions, ⛔ neither answerable by a dev:
packages[]body do per-package docs attach to? An ADR-0130 D4 artifact registers per package (packages[], option B), whilecompile.tsattaches collected docs to the artifact top level (finalBundle.docs). Per-package docs must first be told which package body they belong to.manifest.namespacedoeslintDocs's prefix rule use for them?lintDocsenforces a namespace prefix read from onestack.manifest.namespace, and a multi-package stack has ⛔ no single value for it.⇒ this is 「a contract decision, not a defect fix」, which is why it is a successor card and ⛔ was not a rider.
Measured context worth keeping
The blast radius of the delivered tightening was zero —
find examples packages -maxdepth 4 -type d -path '*/src/*/docs'returned nothing, both example apps keep a flatsrc/docs. ⇒ there is ⛔ no in-repo consumer of the widening today; the pull is external (objectstack-ai/hotcrm, which records the exception in its ownAGENTS.md, objectstack-ai/hotcrm#1910).Dedupe words
package docs·ADR-0130 layout·src/PACKAGE/docs·collect-docs·per-package docs·docs[] attachment·packages[] option BcomposeStackspreserve mode, #6760, #6893).Refs
#18170 / PR #18428 (the tightening half, delivered) · #14122 (the ADR-0130 tracker) · objectstack-ai/hotcrm#1910 (the external app carrying the exception)
Generated by Claude Code