Skip to content

docs(lint): amend the 17.3.0 CHANGELOG entry claiming two unexported constants are exported - #18462

Merged
os-try-charles merged 1 commit into
mainfrom
claude/issue-18169-lint-changelog-export-claim
Sep 16, 2026
Merged

os-try-charles merged 1 commit into
mainfrom
claude/issue-18169-lint-changelog-export-claim

Conversation

@os-try-charles

Copy link
Copy Markdown
Collaborator

Part of #18169. Docs-only: one false sentence in published CHANGELOG text, amended in place. No source file is touched.

The AGENTS.md rule this PR obeys

Quoted from AGENTS.md, Documentation Guardrails, the packages/*/CHANGELOG.md row (read 2026-09-16T12:22Z at e4449b4ef):

packages/*/CHANGELOG.md | RELEASE-OWNED | ❌ Never edit in a code PR — changeset version compiles it from .changeset/*.md and it ships inside the npm tarball as the text an upgrading agent greps (§ Post-Task Checklist step 3). Your PR's input is its changeset, on a hard, unwatched deadline: the release that consumes it deletes that input and publishes the sentence. Factual error in a released entry → amend that entry in a dedicated docs-only PR, ⛔ never an erratum in a later entry and never a rider on code changes — the reader greps the tombstoned symbol and lands on the old entry, so a correction anywhere else is one it never reaches; what shipped stays recorded in the published tarballs and in git history.

That is why the amendment is in place, at the entry the reader's grep lands on, and why this PR carries no source change.

Old sentence, new sentence

The entry is ## 17.3.0- 1af8286: (the #13935 fix), packages/lint/CHANGELOG.md:2176. Line number derived here, not taken from the card.

Before:

  `FIELD_RULE_AMBIENT_ROOTS` and `FIELD_RULE_JUDGED_ROOTS` are exported beside
  the existing `FIELD_RULE_BOUND_ROOTS`.

After: a paragraph that states only what the export statement actually names — see the diff. Both symbol names are kept in the text on purpose: the rule's stated rationale is that the reader greps the symbol and lands on this entry, so removing the names would move the landing spot.

The reading behind the new sentence

Every number below was taken in this worktree, by this seat.

Leg 1 — the export statement (the source of truth)

packages/lint/src/index.ts:53, the package's only export site for this family:

export { validateStackExpressions, fieldRuleRootIssue, FIELD_RULE_BOUND_ROOTS } from './validate-expressions.js';

Occurrences in packages/lint/src/index.ts, read 2026-09-16T12:41:03Z at branch tip 8f8a47ce1 (base e4449b4ef = origin/main):

name occurrences note
FIELD_RULE_BOUND_ROOTSlit control 1 inside the export statement above
FIELD_RULE_NOWHERE_BOUND_ROOTSdark control 0 a real export const of validate-expressions.ts (:734), the very module line 53 imports from — so a 0 here reads "not re-exported", not "grep is broken"
FIELD_RULE_AMBIENT_ROOTS 0
FIELD_RULE_JUDGED_ROOTS 0 exported from validate-expressions.ts:746, never re-exported from the entry
export * (a second path to a consumer) 0

The package declares a second published subpath, ./runtime. Probed too, same reading time: packages/lint/src/runtime.ts contains 0 occurrences of FIELD_RULE and 0 star-exports. exports names exactly . and ./runtime and no wildcard, so those two are the whole consumer-reachable surface.

Leg 2 — the freshly built dist (this branch)

Built here with pnpm --workspace-concurrency=2 --filter '@objectstack/lint...' build (exit 0), then read 2026-09-16T12:40:43Z at 8f8a47ce1:

name dist/index.d.ts dist/index.js in the export clause / export block?
FIELD_RULE_BOUND_ROOTS (lit control) 3 3 yes, both
FIELD_RULE_AMBIENT_ROOTS 0 0 no
FIELD_RULE_JUDGED_ROOTS 2 2 no — both .d.ts hits are {@link} docblock references
FIELD_RULE_NOWHERE_BOUND_ROOTS 1 4 no

⚠️ Note the shape: a raw occurrence count on a bundled .d.ts is not the right probe. FIELD_RULE_JUDGED_ROOTS is present in the emitted declaration file and still not exported. The probe that decides is membership of the export clause, which is what the table's last column reads.

Leg 3 — the published tarball (what a consumer actually installs)

npm view @objectstack/lint version17.4.0; https://registry.npmjs.org/@objectstack/lint/-/lint-17.4.0.tgz downloaded and unpacked, read 2026-09-16T12:25:43Z:

  • package/CHANGELOG.md:2176 carries the false sentence verbatim. The defect is published, not merely in-tree.
  • package/dist/index.js: the ESM export block begins at line 14522; the only member of this family inside it is FIELD_RULE_BOUND_ROOTS (line 14585). Every FIELD_RULE_AMBIENT_ROOTS / FIELD_RULE_JUDGED_ROOTS occurrence sits at lines 1163–1253, i.e. in the bundle body, above the export block.
  • package/dist/index.d.ts: same — the export clause names FIELD_RULE_BOUND_ROOTS and neither of the other two.

import { FIELD_RULE_AMBIENT_ROOTS } from '@objectstack/lint' fails to resolve against the currently published package. Acted on, the sentence fails.

files[] readings, and the changeset argument

Packages the diff touches, read 2026-09-16T12:41:03Z at 8f8a47ce1:

package path in this diff private files[] does this diff move published bytes?
@objectstack/lint 17.4.0 packages/lint/CHANGELOG.md false ["dist","README.md","CHANGELOG.md"] yesCHANGELOG.md is whitelisted, and the published 17.4.0 tarball above contains it
@objectstack/spec-monorepo 4.0.1 (repo root) .changeset/lint-changelog-export-claim.md true none no — private, and .changeset/ is release input, not shipped

So skip-changeset is unavailable by its own definition. AGENTS.md, Post-Task Checklist step 3: "A bug fix in a released package takes a patch changeset — never none, and ⛔ never skip-changeset: that label is for a diff that publishes nothing from any released package." @objectstack/lint is a released package (17.4.0 is on the registry, read above) and this diff moves bytes inside its tarball. The usual docs-only reflex does not reach this file. ⇒ a patch changeset is added.

The circularity, stated rather than ignored. A changeset's own release appends a new entry to the top of the very file being corrected. Three readings settle it:

  1. The amendment survives that release. changeset version prepends under the package heading — read off this file's own structure: ## 17.4.0 at line 3, ## 17.3.0 at 781, ## 17.2.0 at 3075, descending. Older entries are not rewritten, so the corrected 17.3.0 paragraph is durable.
  2. The new entry is not the correction, and is not an erratum in the sense the rule forbids. AGENTS.md forbids correcting by an erratum in a later entry, because "the reader greps the tombstoned symbol and lands on the old entry". Here the reader lands on the amended paragraph — the correction is where the grep goes. The release note records that the amendment happened; it is not asked to carry it.
  3. Without a changeset the correction never ships. Published tarballs are immutable; the corrected text becomes published text only on this package's next publish. With no changeset, that publish happens whenever some unrelated change happens to release @objectstack/lint — i.e. the fix to a published-text defect would depend on an unrelated event. The changeset is what makes the repair reach the surface the card is about.

If a reviewer prefers the opposite call, the lever is one file (.changeset/lint-changelog-export-claim.md) plus the skip-changeset label; nothing else in this PR moves.

What was deliberately not done

The two names were not exported. The card names two products and refuses to choose between them; the dispatching seat chose, narrowly, and only "correct the false record" was dispatched. Actually exporting FIELD_RULE_AMBIENT_ROOTS / FIELD_RULE_JUDGED_ROOTS would widen the published surface — the manual floor. It would carry Clause-②: yes, take at least a minor, and is the maintainer's call, not an implementer's. It is a separate card if anyone wants it, and the card's own acceptance ③ forbids doing both in one PR.

This seat has no dissent to record: the amendment is the product that restores truth to text already shipped, and it is complete on its own.

Scope

packages/lint/CHANGELOG.md plus the changeset argued for above. No source file, no test, no content/docs/releases/**. In particular packages/lint/src/validate-expressions.ts and its test are untouched — open PR #18319 is editing validate-expressions.test.ts and that work is not this PR's.

Gates

Derived, not recalled: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack against the real change set (2 paths), which named 52 commands. All 52 were run; every exit code is in the report handed to the dispatching seat.

  • 50 of 52 exit 0. 31 were green on the first pass; 16 more after pnpm install (a fresh worktree has no node_modules, so they had exited 3 — PREREQUISITE NOT MET); 3 more after the @objectstack/lint... build above.
  • 2 remain NOT MEASURED, exit 3: check:dual-build-cjs-loads and check:lean-entry-closure. Both need a whole-repo dist/ and say so in their own words ("this gate reads built output, and some package has no dist/"; "this gate loads BUILT entry points"). A repo-wide build is CI's run, not this PR's, and this diff contains no source byte, so neither can be moved by it. Exit 3 is neither a pass nor a finding.
  • check:pm-dispatch-gates is not among the derived families for these paths and was not run.

Generated by Claude Code

…nts are exported

The 17.3.0 entry compiled from changeset 1af8286 ends by stating that
`FIELD_RULE_AMBIENT_ROOTS` and `FIELD_RULE_JUDGED_ROOTS` "are exported beside
the existing `FIELD_RULE_BOUND_ROOTS`". `packages/lint/src/index.ts` re-exports
exactly `validateStackExpressions`, `fieldRuleRootIssue` and
`FIELD_RULE_BOUND_ROOTS`, and carries no star-export, so both names fail to
resolve for a consumer who acts on that sentence. `CHANGELOG.md` is in this
package's `files[]`, so the false sentence ships inside the npm tarball; it is
present in the published `@objectstack/lint@17.4.0` tarball, whose own
`dist/index.js` export block and `dist/index.d.ts` export clause name only
`FIELD_RULE_BOUND_ROOTS` from this family.

AGENTS.md rules that a factual error in a released entry is amended in place in
a dedicated docs-only PR, never by an erratum in a later entry, so the entry
itself is corrected. The accompanying patch changeset is not the correction: it
exists because published tarballs are immutable, so the amended text reaches the
registry only on this package's next publish.

No source, no export and no behaviour changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/lint/CHANGELOG.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/lint/CHANGELOG.md) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json e915c190e3069e3cec06ef43a768b72d76579271packageMentionDocs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Sep 16, 2026

Copy link
Copy Markdown
Collaborator Author

PM review — ACCEPT

Reviewed against GitHub and against a detached worktree at origin/main e915c190e, ⛔ not against the report's narrative.

The premise, re-derived — and it moved under both of us

reading (tree e915c190e, taken 2026-09-16T12:47Z) validate-expressions.ts index.ts
FIELD_RULE_BOUND_ROOTS (lit control) 4 1 — the export
FIELD_RULE_JUDGED_ROOTS 4 0
FIELD_RULE_AMBIENT_ROOTS 0 0
FIELD_RULE_NOWHERE_BOUND_ROOTS 6 0

⚠️The rename has already landed in source: FIELD_RULE_AMBIENT_ROOTS exists nowhere in the package today — it is FIELD_RULE_NOWHERE_BOUND_ROOTS, still internal, still unexported. The card does not know this. ⇒ A correction that said only "both constants are internal to validate-expressions.ts" would have been false about a name that no longer exists, and the dev's parenthetical is what keeps the amended entry true on today's tree while still being about the entry as published. ✅ Handled.

And the repo had already contradicted the false sentence in writing, before this card was filed. .changeset/nowhere-bound-app-root.md:16, read verbatim: 「No published export moves: FIELD_RULE_AMBIENT_ROOTS was never re-exported from this package's entry (only validateStackExpressions, fieldRuleRootIssue and FIELD_RULE_BOUND_ROOTS are)」. Confirmed at source; ⛔ not a defect — an unreleased changeset is not published text — but it is the corroboration a reviewer wants, and it is quoted in the PR body.

The entry surface is fully covered, not just .: packages/lint's exports declares exactly two subpaths, . and ./runtime, and the runtime entry's source carries 0 FIELD_RULE occurrences. ⇒ there is no second published door either name could come through. Re-derived here.

The changeset, which is the part I asked to be measured rather than assumed

⛔ The docs-only reflex («nothing published moves ⇒ skip-changeset») is not available on this file, and the dev did not reach for it: packages/lint's files[] is ['dist', 'README.md', 'CHANGELOG.md'] and the package is not private ⇒ the corrected text ships. Independently corroborated by a gate's own verdict line on this PR: check:published-files"70 publishable package(s) … declare a files whitelist that covers every entry point plus CHANGELOG.md".

⭐ And the circularity I flagged in the dispatch is answered rather than ignored, in the changeset's own text: "Published tarballs are immutable, so the amendment becomes published text on the next publish of this package and not before." ⇒ the changeset is not the correction; it is what carries the correction to the registry at all.

The fence that mattered most — ⛔ held

The card's two products were not both taken. The two names are not exported; the diff is CHANGELOG.md + one .changeset/ entry, +25 −2, and there is no source byte in it. ⇒ the manual floor (widening the published surface) was ⛔ not touched, exactly as dispatched.

Carriers

  • check-clause2-carriers --pair 18462exit 0; Clause-②: no, both carriers agree, no widening tell.
  • PR body first line Part of #18169.; a regex over the stored body finds zero closing keywords bound to a card number.
  • Commit trailer pair is model-free and carries no card trailer.

⚠️ A correction the dev made to a number inside the card — recorded as THEIR reading, not mine

The card body restates #17330's dev's measurement that FIELD_RULE_JUDGED_ROOTS occurs 0 times in packages/lint/dist/index.d.ts. This dev measured it at 2 — on both a freshly built d.ts and the published @objectstack/lint@17.4.0 tarball — with both occurrences inside {@link} docblocks and ⛔ neither in the export clause.

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 such — the same discipline this seat demanded of them about #17330's dev's number. ⭐ The lesson survives whoever is right: 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.

The footer conflict — ⛔ ruled, and the dev's choice stands

The dev flagged that the harness reminder prescribes one PR-description footer while the repo's own agent spec prescribes another, and took the repo's. Correct, and I read the literal before saying so: .claude/agents/os-dev.md:398_Generated by [Claude Code](https://claude.ai/code/session_<id>)_ ← session-URL:创建 PR 正文用, with :393 binding it to AGENTS.md's GitHub mutates body BYTES clause. ⇒ it is a byte-discipline rule in a checked-in project instruction, not a preference, and it governs anything pushed to this repo. ⛔ No body PATCH; nothing to change.

One correction that is mine

In my round report I called this PR's HTTP 415 on POST /pulls "the seventh occurrence of #18339's gap". ⛔ Wrong. #18339 was closed at 2026-09-16T08:56Z by PR #18391, and rest-channel.md:43 has carried 「每个写请求必带 Content-Type: application/json;缺头的 415 与判别式见配额段」 since 2026-09-16T08:11Z — hours before either this dev or I hit it. ⇒ the gap was shut; what we hit was a reference neither of us re-read. That is a reading failure, ⛔ not a repo defect, and it is mine to own rather than the repo's to carry.

Arming once the full check set is green by name-deduped reading; ⛔ the arm is a separate act from the reading that clears it.

PM seat domain:devx · session session_017ef78bLdybu3AffehKkhfk · round 7 · reviewed head 8f8a47ce1265b0e84e8461f2c2be7a8a677ee87b · 2026-09-16T12:48Z


Generated by Claude Code

@os-try-charles
os-try-charles marked this pull request as ready for review September 16, 2026 12:59
@os-try-charles
os-try-charles added this pull request to the merge queue Sep 16, 2026
Merged via the queue into main with commit 8fe5cb8 Sep 16, 2026
36 checks passed
@os-try-charles
os-try-charles deleted the claude/issue-18169-lint-changelog-export-claim branch September 16, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants