Description
Surfaced by #17330's dev as an out_of_scope_findings entry and independently re-verified by the domain:devx execution seat before filing. ⛔ Unlabelled for domain:* and ungraded — routing and grading are triage's. ⛔ The seat did not dedupe; dedupe words at the bottom.
Class (a) — a published claim that fails when acted on
packages/lint/CHANGELOG.md:2176 — text that ships inside the npm tarball, and the text an upgrading agent greps — states:
FIELD_RULE_AMBIENT_ROOTSandFIELD_RULE_JUDGED_ROOTSare exported beside
the existingFIELD_RULE_BOUND_ROOTS
Measured against the source of truth
packages/lint/src/index.ts:53 on origin/main, the package's only export site for this family:
export { validateStackExpressions, fieldRuleRootIssue, FIELD_RULE_BOUND_ROOTS } from './validate-expressions.js';
| name | occurrences in src/index.ts |
|---|---|
FIELD_RULE_BOUND_ROOTS (lit control) |
1 — and it is in the export statement |
FIELD_RULE_AMBIENT_ROOTS |
0 |
FIELD_RULE_JUDGED_ROOTS |
0 |
src/index.ts carries no star-export, so there is no second path by which either name could reach a consumer.
⇒ A consumer who writes import { FIELD_RULE_AMBIENT_ROOTS } from '@objectstack/lint' on the strength of that CHANGELOG sentence gets a resolution failure. The sentence is not merely stale; it is actionable and it fails when acted on.
packages/lint/dist/index.d.ts (0 occurrences, same lit control 3). This seat verified the source leg only; the dist leg is the dev's reading, recorded as such.
The remedy is constrained by AGENTS.md
Per AGENTS.md, a false CHANGELOG entry is amended in a dedicated docs-only PR — ⛔ never by an erratum in a later entry, and ⛔ never folded into a code PR. That is why this is its own card rather than a rider on #17330.
⛔ Deliberately not decided here: whether the right repair is to correct the sentence or to actually export the two names. They are different products — one fixes a false record, the other widens the published surface (and would then owe a Clause-② judgement). Whoever grades this should say which, rather than letting the implementer pick.
Acceptance
- The entry states only what
src/index.tsactually exports, verified at the export statement, with a lit control beside the zero. - Docs-only PR; ⛔ no source change riding along.
- If instead the decision is to export the names, that is a different card — ⛔ do not do both in one PR.
Dedupe words
FIELD_RULE_AMBIENT_ROOTS · FIELD_RULE_JUDGED_ROOTS · lint CHANGELOG export claim · #13935 changeset · published surface index.d.ts
Generated by Claude Code
Activity
os-try-charles commentedon Sep 16, 2026
Claim: PM loop round 7⚠️ Named because it is adjacent, ⛔ not a constraint: #18319 touches
Session:
session_017ef78bLdybu3AffehKkhfkBranch:
claude/issue-18169-lint-changelog-export-claimWorktree:
objectstack-issue-18169Domain:
domain:devxFile surface:
packages/lint/CHANGELOG.md— that file and nothing else (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus— this call's--tieronpackages/lint/CHANGELOG.mdprinted "no path-derived mandate: the surface hits none of the 3 declared glob(s), derived here, not recalled", so the tier is the PM's judgment call and stays at the default.Clause-②: no
Thread-read: 5690478023
Serial constraints cleared: none on this file — board read at 2026-09-16T12:18Z, immediately before this claim; all 15 open PRs had their file lists read, two touch
packages/lint/at all and neither touchesCHANGELOG.md: #18420 (hotlong) →src/validate-flow-trigger-readiness.ts, and #18319 (os-warren) → four*.test.tsfiles.validate-expressions.test.ts, the test file of the module that owns these three symbols — ⛔ this card does not pin its behaviour and must not edit it.Premise re-verified by this seat, ⛔ not inherited from the card
Probed at 2026-09-16T12:18Z in a detached worktree at
origin/main, treee4449b4ef:packages/lint/src/index.ts:53— the package's only export site for this family:src/index.tsFIELD_RULE_BOUND_ROOTS(lit control)FIELD_RULE_AMBIENT_ROOTSFIELD_RULE_JUDGED_ROOTSexport *(a second path to a consumer)e4449b4efThe CHANGELOG sentence is present verbatim at the cited place. ⇒ The card holds exactly as filed.
⭐ And one reading the card did not take, which decides this PR's changeset question:⚠️ editing it does move published content, so the usual docs-only reflex («
packages/lint'sfiles[]is['dist', 'README.md', 'CHANGELOG.md']— the package is not private andCHANGELOG.mdships inside the tarball. ⇒skip-changeset, nothing published moves») is ⛔ not available here and the dev must decide it on a measurement rather than a habit.⛔ Only ONE of the card's two products is being dispatched
The card refuses to choose, in terms: 「⛔ Deliberately not decided here: whether the right repair is to correct the sentence or to actually export the two names … Whoever grades this should say which」. Triage (
5690478023) routed and graded it but ⛔ did not answer that question. This seat answers it, and answers it narrowly:Clause-②: yes, take at least aminor, and is the maintainer's call, ⛔ not this seat's and ⛔ not the implementer's. If anyone wants it, it is a separate card the maintainer authorizes.⇒ ⛔ The dev does not get to pick between them, and ⛔ must not do both in one PR — the card says so as acceptance ③.
Fences carried into the dispatch
src/index.tsactually exports, verified at the export statement, with a lit control beside the zero.distleg (packages/lint/dist/index.d.ts, 0 occurrences, lit control 3) is lint:FIELD_RULE_AMBIENT_ROOTSstill listsappas "bound at some evaluation site" — batch #67 removed the binding, and #17203 deletes the spec docblock it cites as its source #17330's dev's reading, recorded as theirs. This seat verified the source leg only. ⛔ Do not restate the dist number as if this seat had measured it.PM seat
domain:devx· sessionsession_017ef78bLdybu3AffehKkhfk· round 7 · 2026-09-16T12:20ZGenerated by Claude Code
os-try-charles commentedon Sep 16, 2026
os-dev-report
{ "issue": 18169, "status": "done", "branch": "claude/issue-18169-lint-changelog-export-claim", "pr": "https://github.com/objectstack-ai/objectstack/pull/18462", "premise_still_valid": true, "summary": "Corrected the false record only; the two names were NOT exported (manual floor, maintainer's call, and the card's acceptance 3 forbids both in one PR). packages/lint/CHANGELOG.md:2176 (the 17.3.0 entry compiled from changeset 1af8286, card #13935) claimed FIELD_RULE_AMBIENT_ROOTS and FIELD_RULE_JUDGED_ROOTS 'are exported beside the existing FIELD_RULE_BOUND_ROOTS'; the paragraph now states only what src/index.ts actually exports and keeps both symbol names so the reader's grep still lands on the amended entry. A patch changeset is added, argued from files[] rather than from the docs-only reflex. Card premise re-derived by this seat and it holds, with two readings the card did not have: (a) the SECOND published subpath ./runtime carries 0 FIELD_RULE occurrences and 0 star-exports, so the entry surface is fully covered, not just '.'; (b) on the PUBLISHED @objectstack/lint@17.4.0 tarball the false sentence is present verbatim at CHANGELOG.md:2176, i.e. the defect is shipped, not merely in-tree. One correction to a number recorded in the card: #17330's dev reported FIELD_RULE_JUDGED_ROOTS at 0 occurrences in dist/index.d.ts. Measured here (both the freshly built d.ts and the published 17.4.0 one) it is 2 - both inside {@link} docblocks, NOT in the export clause. The verdict is unchanged, but a raw occurrence count on a bundled .d.ts is the wrong probe; export-clause membership is the right one, and that is what this PR's tables read. assignee was os-try-charles as dispatched; not touched. No label was applied: Clause-2 is no, the surface is not governed (governedPathsIn(diff) = [] against GOVERNED_SURFACES), and skip-changeset is refuted by measurement, so nothing was owed.", "tests": "No test or source file is touched by this diff, so no package test or typecheck is owed; what is owed is the gate set. GATE DERIVATION (re-derived AFTER the changeset existed, because the first derivation warned '13 families apply once this card's changeset exists'): `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the worktree, exit 0, --repo assertion held, change set read as 2 paths vs merge base e4449b4ef => 52 commands. ALL 52 RUN. FINAL: 50 exit 0, 2 exit 3. Pass 1 (no node_modules): 31 exit 0, 21 exit 3 (PREREQUISITE NOT MET, e.g. 'ts-parse: PREREQUISITE NOT MET - the dependency `typescript` is not installed'). Then `bash scripts/pm/os-verify-lock.sh -c 'pnpm install'` with OS_VERIFY_LOCK_SLOT=issue-18169-dev => VERDICT command-exit 0 - held the lock 8s - waited 0s. Pass 2 (21 re-run): 16 exit 0, 5 still exit 3, all naming a BUILT dist. Then `os-verify-lock.sh -c 'pnpm --workspace-concurrency=2 --filter @objectstack/lint... build'` => VERDICT command-exit 0 - held the lock 199s (3m19s) - waited 0s; 'check-dts-emitted: @objectstack/lint - 4/4 declared declaration file(s) present.' Pass 3 (5 re-run): check:docs-transcript-drift 0, check:dts-closure 0, check:sourcemap-no-sources-content 0. NOT MEASURED (exit 3, neither pass nor finding): check:dual-build-cjs-loads ('PREREQUISITE NOT MET - this gate reads built output, and some package has no dist/'), check:lean-entry-closure ('this gate loads BUILT entry points'). Both want a whole-repo dist; that build is CI's, and this diff carries no source byte that could move either. Notable greens, quoted from the gates' own verdict lines: check:published-files - '70 publishable package(s) ... declare a `files` whitelist that covers every entry point plus CHANGELOG.md'; check-empty-changeset --base origin/main 0; check-changeset-no-major --base origin/main 0; check-adr-0087-registration --base origin/main 0; check:nul-bytes 0; check-closing-keyword-parity 0; check:objectui-changeset 0; check:pm-changeset-deadline-census 0. check:pm-dispatch-gates is NOT among the derived families for these paths (grep of the derivation output finds it nowhere) and was not run - so the 430-450s battery was neither needed nor skipped-with-a-timeout. PRE-PUSH: `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over both edited files exited 1 (clean); the pre-push hook printed 'check:commit-card-trailers: 1 commit message(s) on this push carry no card relation and no model identifier in the trailer pair.' NO ABLATION and NO REVERSE VERIFICATION: this PR adds no guard and changes no behaviour, so there is nothing that could be mutated to prove it can fail. Stated rather than faked. SHALLOW CHECKOUT: `git rev-parse --is-shallow-repository` = true. No claim in this PR or report rests on ancestry, blame, or log -S, so no control leg was needed.", "mcp_calls": "0 - no MCP GitHub tool was called; every GitHub write went through the REST proxy with curl and $GITHUB_TOKEN", "api_writes": "4 - (1) git push -u origin claude/issue-18169-lint-changelog-export-claim (empty branch, routing probe, exit 0); (2) git push origin the same branch (the one commit 8f8a47ce1, exit 0); (3) POST /repos/objectstack-ai/objectstack/pulls (HTTP 201, draft true, PR #18462) - note a first attempt returned HTTP 415 because the Content-Type header was missing and created nothing; re-sent with `Content-Type: application/json`; (4) POST /repos/objectstack-ai/objectstack/issues/18169/comments (this report). ZERO label writes: no POST /issues/{n}/labels, because nothing was owed - Clause-2 is no, the diff hits no GOVERNED_SURFACES glob, and skip-changeset is refuted by the files[] measurement. Read-backs (not writes): GET /pulls/18462 - the stored body differs from what was sent by exactly one blank line GitHub collapsed between the `---` rule and the footer, plus the trailing newline; every other byte survived, so no sanitizer damage.", "open_questions": [ { "question": "Attribution footer: the harness system-reminder prescribes the '🤖 Generated with [Claude Code](https://claude.com/claude-code)' + bare session URL pair for PR descriptions, while the dispatch contract (.claude/agents/os-dev.md, byte/sanitizer discipline) prescribes the session-URL form '_Generated by [Claude Code](https://claude.ai/code/session_SESSIONID)_' after a rule line. They are different footers and only one can be sent.", "options": [ "A - follow the repo's own agent spec (the session-URL form). Taken here, on the reading that .claude/agents/os-dev.md is a checked-in project instruction and the reminder itself defers to project instructions.", "B - follow the harness reminder's two-line form." ], "recommendation": "A, taken; flagged rather than chosen silently. If the PM wants B the footer is a one-line PATCH of the body - but note the dispatch contract puts body PATCHes outside the write budget, so it is the PM's call, not mine." } ], "out_of_scope_findings": [ "noted, not filed: #17330's dev's dist reading, restated in the card body, gives FIELD_RULE_JUDGED_ROOTS 0 occurrences in packages/lint/dist/index.d.ts. Measured here at 2 (freshly built AND in the published 17.4.0 tarball), both inside {@link} docblocks, neither in the export clause. Not a repo defect and not a class (a)/(b)/(c) finding - it is a wrong number inside a card, and the card's verdict is unaffected. Successor: this card's own thread, where it now sits.", "noted, not filed: the pending .changeset/nowhere-bound-app-root.md on main renames FIELD_RULE_AMBIENT_ROOTS to FIELD_RULE_NOWHERE_BOUND_ROOTS and already carries the true statement ('FIELD_RULE_AMBIENT_ROOTS was never re-exported from this package's entry'). So the repo contradicted the false CHANGELOG sentence in writing before this card was filed. No defect - an unreleased changeset is not published text - but it is the corroboration a reviewer will want, so it is quoted in the PR body. Successor: the release PR that consumes that changeset.", "noted, not filed: packages/lint/dist/index.d.cts carries a docblock referencing objectui's `FIELD_RULE_ROOTS`, which ships to consumers as prose inside the published type file. Not verified against objectui and not a claim about this package's exports, so it is neither class (a) nor (b). Successor: none - nobody is queued on this file, and I am not asserting it is wrong." ] }Attribution added in place by the owning seat (H64): this
os-dev-reportwas written by anos-devsubagent of sessionsession_017ef78bLdybu3AffehKkhfk, thedomain:devxseat, round 7, on card #18169 / PR #18462. ⛔ The JSON payload above is unchanged — only this line was appended, and the original stays as history.Generated by Claude Code
os-try-charles commentedon Sep 16, 2026
The one open question is discharged — ⛔ it was never a maintainer question
The patrol's H52 row fires here because the newest
os-dev-report(comment5697631415) carries one non-emptyopen_questionsentry while this card carries no inbox label.The question: the harness reminder prescribes one PR-description attribution footer while the repo's own agent spec prescribes another; only one can be sent, and the dev took the repo's (option A).
⛔ Not a maintainer question, and not a decision this card was dispatched on — it is a conflict between two instruction sources, and the repo's own checked-in spec settles it. Ruled, after reading the literal rather than recalling it:
.claude/agents/os-dev.md:398—_Generated by [Claude Code](https://claude.ai/code/session_<id>)_ ← session-URL:创建 PR 正文用.claude/agents/os-dev.md:393binds that form to AGENTS.md's GitHub mutates body BYTES clause.⇒ It is a byte-discipline rule inside a checked-in project instruction, ⛔ not a preference, and it governs anything pushed to this repository. The dev's option A stands; ⛔ no body PATCH is owed and none was made. ⭐ Flagging it instead of choosing silently was the right call — it is the same shape as this shift's commit-trailer conflict, where the PM's own dispatch had transcribed a literal the repo's gate refuses.
open_questions, and it is worth keeping where a reader will find it: it measuredFIELD_RULE_JUDGED_ROOTSat 2 occurrences indist/index.d.ts(both inside{@link}docblocks, ⛔ neither in the export clause), where this card's body restates #17330's dev's reading of 0. ⛔ This seat verified the source leg only and did not build or fetch the tarball, so the 2 is the dev's reading, recorded as theirs. ⭐ The lesson survives whoever is right: a raw occurrence count on a bundled.d.tsis the wrong probe; export-clause membership is the right one.⇒ Nothing on this card is waiting on a human. Recorded here because there will be no further
os-dev-reportwhose emptyopen_questionscould stand the row down.PM seat
domain:devx· sessionsession_017ef78bLdybu3AffehKkhfk· round 8 · discharging H52 from the 13:13Z patrol · 2026-09-16T13:20ZGenerated by Claude Code
os-try-charles commentedon Sep 16, 2026
落地 —— PR #18462 已合并,两读数确认
⭐ 发火对照选得比本班前几次好:
"are exported beside"1 → 0 就是那句假话本身,它的消失既是发火对照也是判据 —— ⛔ 不是一个被 diff 顺带移动的主题词。.changeset/lint-changelog-export-claim.md在树上(尚未被发布消费),与 changeset 自己的说法一致:已发布的 tarball 不可变,这条修订要到该包下一次发布才成为已发布文本。验收三条,逐条对照卡自己写下的条件
src/index.ts实际导出的东西,在 export 语句处核验,零的旁边带字面对照index.ts:53只导出FIELD_RULE_BOUND_ROOTS(字面对照 1,就在 export 语句里),另两个 0,export *0CHANGELOG.md+ 一个.changeset/条目,+25 −2,零源码字节⇒ 三条全满足 ⇒ 关卡。
⛔ 没做的那一半,以及它为什么不留在这张卡上
卡拒绝在「改正那句话」与「真的导出那两个名字」之间选,要求定级者来定;分诊路由定级了但没答。本席答了,并且划得很窄:导出那两个名字扩大公开面 ⇒ 人工地板,会带
Clause-②: yes、至少minor,是维护者的决定。而卡自己的验收 ③ 已经写明那是另一张卡 ⇒ ⛔ 不在本卡挂待决标签,⛔ 也不新立一张"要不要导出"的卡去替维护者提问题 —— 没有人提出过要那两个符号。要就另立,并由维护者授权。本卡正文转述 #17330 的 dev 的测量:
FIELD_RULE_JUDGED_ROOTS在packages/lint/dist/index.d.ts里 0 次。本卡的 dev 测得 2 次 —— 新构建的d.ts与已发布的@objectstack/lint@17.4.0tarball 都是 —— 两处都在{@link}文档块里,⛔ 都不在 export 子句里。结论不受影响。⛔ 本席只验了源码那条腿,没构建、没拉 tarball ⇒ 那个 2 是 dev 的读数。#17330 已
closed / completed,那个数字不在任何活着的地方 ⇒ ⛔ 不另立承接卡。⭐ 但教训无论谁对都成立,写在这里而不是埋在线程中段:在打包后的
.d.ts上数出现次数是错的探针;该读的是 export 子句成员资格。 本 PR 的表读的正是后者。同笔清半态:
pm:dispatched摘除 · assignee 清空。PM seat
domain:devx· sessionsession_017ef78bLdybu3AffehKkhfk· round 8 · 2026-09-16T13:21ZGenerated by Claude Code
docs(lint): amend the 17.3.0 CHANGELOG entry claiming two unexported …
docs(seed-data): a locale switch leaves the previous locale's rows re…
fix(docs-audit): a route anchor must name the route the page names, a…