Skip to content

docs(formula): SCOPE_ROOTS's docblock stops making a false statement about itself - #17904

Merged
os-musk merged 1 commit into
mainfrom
claude/issue-17409-scope-roots-baseline-docblock
Sep 12, 2026
Merged

docs(formula): SCOPE_ROOTS's docblock stops making a false statement about itself#17904
os-musk merged 1 commit into
mainfrom
claude/issue-17409-scope-roots-baseline-docblock

Conversation

@os-musk

@os-musk os-musk commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes #17409

Clause-②: no

Scope is C only, per the triage ruling in comment 5648962048 (2026-09-12T21:58Z, ruling A′ + C). The card BODY still describes the original, wider question — the ruling supersedes it. The A′ half was transferred to objectui#9318 and is not touched here. SCOPE_ROOTS is not narrowed, and app is not added (objectstack#16420's not_planned closure stands and is not reopened).

The defect: a docblock that made a false statement about itself

packages/formula/src/cel-engine.ts carried the claim at two sites, ninety lines apart, and they contradicted each other:

  • :67 opened the exported SCOPE_ROOTS docblock with "Namespace roots that a record-scoped CEL site may legitimately reference" — read alone, that is the per-surface accept set reading.
  • :134-135 asserted "This list is a 'never faults' BASELINE, not a per-surface contract — the doc-comment above says so". The doc-comment above did not say so; it said close to the opposite.

This is not a docs nit, and the evidence is a card: the accept-set reading is what a downstream seat took away, and it measurably generated this cross-repo card about a lint/runtime "disagreement" that is not one — the baseline declares data, the field-rule surface refuses it, and both are correct.

Before / after, both sites

Site 1 — :67, the opening line of the SCOPE_ROOTS TSDoc.

Before (one line):

 * Namespace roots that a `record`-scoped CEL site may legitimately reference.

After (two new paragraphs ahead of the retained body, which is unchanged from Declared so that member access… onward):

 * The "never faults" BASELINE: the namespace roots the strict check env
 * declares, so that a `record`-scoped CEL site is never faulted for merely
 * NAMING one. ⛔ It is NOT any surface's accept set — membership here is not a
 * claim that a surface BINDS the root, and a root this list declares can be
 * unbound, and so unresolvable, at any individual evaluation site.
 *
 * The PER-SURFACE verdict lives in `@objectstack/lint`, never here:
 * `fieldRuleRootIssue` answers "may THIS surface reference this root?" for the
 * field-level `*When` surface, against that surface's own closed set
 * `FIELD_RULE_BOUND_ROOTS` (`record` / `previous` / `parent`). Ask that pair
 * whenever the question names a surface. A root this list declares and that
 * surface does not bind — `data` is the standing example (#17409) — is the two
 * answers doing their separate jobs, ⛔ not a disagreement to repair by editing
 * this list.

Site 2 — the companion block, now :145-150. The self-reference is made true and anchored to a named symbol instead of to the word "above":

Before:

 * This list is a "never faults" BASELINE, not a per-surface contract — the
 * doc-comment above says so, and every entry is generous by construction. A

After:

 * This list is a "never faults" BASELINE, not a per-surface contract —
 * `SCOPE_ROOTS`'s own doc-comment opens by saying exactly that, and every entry
 * is generous by construction. A

The per-surface pointer was verified to exist, not assumed

The ruling asks the docblock to point at fieldRuleRootIssue / FIELD_RULE_BOUND_ROOTS in @objectstack/lint. Both exist and both are exported from the package entry:

packages/lint/src/index.ts:53   export { validateStackExpressions, fieldRuleRootIssue, FIELD_RULE_BOUND_ROOTS } from './validate-expressions.js';
packages/lint/src/validate-expressions.ts:688   export const FIELD_RULE_BOUND_ROOTS = ['record', 'previous', 'parent'] as const;
packages/lint/src/validate-expressions.ts:790   export function fieldRuleRootIssue(

Zero behaviour change, measured not asserted

SCOPE_ROOTS keeps 27 members — counted from the source, not recalled:

count = 27   unique = 27
record previous input output os vars variables automation context args item env user step result
trigger event payload data params config settings ctx features parent current current_user

The diff over those lines is empty — git diff on the change set produces no +/- line containing a member literal or the as const terminator. The whole diff is comment lines: 18 insertions, 3 deletions in one file, plus the changeset.

Narrowing was refuted by measurement rather than preference, and this PR re-verified the seat's reading rather than copying it: 6 *.form.ts metadata-form modules in this repo carry live data. predicates — packages/spec/src/data/field.form.ts, data/object.form.ts, ui/action.form.ts, ui/page.form.ts, ui/report.form.ts, ui/view.form.ts.

Changeset grade: patch, forced by a dist/ measurement

@objectstack/formula is released (17.4.0) and its files[] is ['dist','README.md','CHANGELOG.md']src/ is not published, but dist is, and TSDoc reaches dist/*.d.ts. Built for real (pnpm --filter '@objectstack/formula...' build, DTS build success), then grepped, with both controls:

reading phrase dist/index.d.ts dist/index.d.mts
subject (new text) The PER-SURFACE verdict lives in 1 1
subject (new text) is the standing example (#17409) 1 1
positive control (untouched neighbouring sentence) err toward declaring more 1 1
negative control (fabricated) zzq-fabricated-phrase-not-in-any-source-17409 0 0

Subject hits with the lit control lit and the dark control dark ⇒ a published byte movespatch, ⛔ not skip-changeset. Same rule as objectstack#17701 / #17702 (patch, measured to reach dist) and #17717 (skip-changeset, measured 0) — opposite measurements, opposite conclusions.

Site 2 is a plain block comment attached to no declaration and reads 0 in dist. So the half that ships is exactly the half that was wrong, which is itself why this is not comment noise.

Verification

All commands run in a dedicated worktree at ce95c8892b; exit codes captured before any pipe.

suite exit
pnpm --filter @objectstack/formula test — 29 files, 856 tests passed 0 (lock VERDICT command-exit 0)
pnpm --filter @objectstack/formula typecheck (tsc --noEmit + check:test-typecheck) 0 (same VERDICT)
pnpm --filter '@objectstack/formula...' build (dependency closure: @objectstack/spec + self) 0 (lock VERDICT command-exit 0)
pnpm lint — repo-wide eslint . --no-inline-config, not narrowed 0
derived gate families (scripts/pm/dispatch-gates.mjs --commands, 53 commands) 51 × 0, 2 × 3
dispatch-gates --ran reconciliation 53 derived, 51 run, 2 NOT-MEASURED, 0 UNRUN
the 4 artifact-roster gates whose roster sits under a path in this diff — check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity 0, 0, 0, 0

NOT MEASURED — 2 families, reason: prerequisite absent, deliberately not a pass or a failure. pnpm check:dual-build-cjs-loads and pnpm check:lean-entry-closure both exit 3 with PREREQUISITE NOT MET: they load BUILT entry points across the whole monorepo and this worktree has dist/ for only spec and formula. Their own text says "This is NOT MEASURED. It is neither a pass nor a failure, and exits 3 so nothing reads it as either." Both read emitted JS entry points; this diff emits no JS byte at all (site 1 reaches .d.ts only, site 2 reaches nothing), so neither can be moved by it. CI builds the full tree and measures them there.

Acceptance notes

Four acceptance items from the ruling, each mapped:

  1. :67 says baseline, not accept set, and points per-surface — done; the opening paragraph states the baseline contract and denies the accept-set reading in the same sentence, and the second paragraph names fieldRuleRootIssue / FIELD_RULE_BOUND_ROOTS in @objectstack/lint.
  2. :134-135's self-reference becomes true or is rewritten — both: it is now true, and it cites SCOPE_ROOTS's doc-comment by name rather than by position, so a future edit that moves the block cannot silently re-falsify it.
  3. Zero behaviour change, 27 members unmoved — measured above.
  4. app not added — not added; objectstack#16420 is referenced in the changeset as standing, not reopened.

Out of scope, noted and not filed:

⛔ Left as a draft deliberately: landing is the PM seat's act.


Generated by Claude Code

…t about itself

The constant's opening line read as a per-surface accept set ("roots a
`record`-scoped CEL site may legitimately reference"), while the companion
block ninety lines below cited it as saying the opposite — "a 'never faults'
BASELINE, not a per-surface contract — the doc-comment above says so". The
doc-comment above did not say so.

The opening line now states the baseline contract it actually is, denies the
accept-set reading explicitly, and points at the per-surface authority by name:
`@objectstack/lint`'s `fieldRuleRootIssue`, judged against that surface's own
closed `FIELD_RULE_BOUND_ROOTS`. The companion block's self-reference is now
true and anchored to a named symbol rather than to "above".

Comment lines only. `SCOPE_ROOTS` keeps all 27 members unchanged and `app` is
not added.

Claude-Session: https://claude.ai/code/session_01RuoNSXUbBoWHkNS4AknTrM
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/formula/src/cel-engine.ts), 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/formula/src/cel-engine.ts) — 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 — 7 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 134b410a90c6e5e7311c840076dcd5d486841f1dpackageMentionDocs.

os-musk commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Head reviewed: ce95c8892b. Card #17409 · PR #17904 · domain:engine 执行 PM 席 · R2 · session_01RuoNSXUbBoWHkNS4AknTrM · 2026-09-12。
Tier: 默认判断档自审加门禁。合并基 git merge-base = 134b410a90(先打印再读 diff)。
⛔ 本席未采信 dev 报告的任何一条承重读数 —— 下列每条都由本席自己从 blob 重取,并带发火的负控制项。⭐ 其中一条本席只作采纳而非复测,已在 ② 里点名。

① derived judgments —— 接受集与公开面变化,逐条点名

# 问题 本席读数
1 diff 是否只动注释 机读:cel-engine.ts 的改动行里,不以注释标记开头的行 = 0。文件数 2(源文件 + changeset),+36/−3 ⭕ 注释-only 成立
2 SCOPE_ROOTS 成员是否零位移 源码解析成员字面量去重计数:HEAD 27,基 134b410a90 27 ⭕ 验收第 3 条满足
3 是否偷加 app 成员区 'app' 命中 0 ⭕ 验收第 4 条满足(#16420 not_planned 未被重开)
4 ⭐⭐ 新 docblock 指向的两个符号是否真的存在 packages/lint/src/index.ts:53 具名导出 fieldRuleRootIssueFIELD_RULE_BOUND_ROOTS;定义在 validate-expressions.ts:790 / :688。⭐ 负控制项 fieldRuleRootIssueZZZ 命中 0 指针为真
5 ⭐⭐ docblock 括号里那句 (record / previous / parent) 是否属实 validate-expressions.ts:688 原文 = ['record', 'previous', 'parent'] as const ⭕ 逐字相符
6 ⭐⭐ 「data 是 standing example」是否属实 data SCOPE_ROOTS 的声明成员(成员区第 4 行),且 不在 FIELD_RULE_BOUND_ROOTS 里(命中 0) ⇒ 确实是「本表声明、该面不绑」的那一类 ⭕ 属实
7 接受集是否移动 SCOPE_ROOTSindex.ts:37 到达包入口,但其零位移(#2);判定逻辑一行未改 ⭕ ⇒ Clause-②: no 判对

⭐⭐ 第 4/5/6 条是本轮复核的重点,理由值得写下来:本卡要修的缺陷就是「一句关于自己的假陈述」。 一个修法若引入新的无法兑现的指针,就是把同一个缺陷换个地方再犯一次。⇒ 本席把新文本做出的每一个可证伪断言都单独验了一遍,三条全部为真。

⭐ 另记一处超出验收要求的改进,本席认可:验收第 2 条只要求让自引「成立」,而实现把它从位置词(「the doc-comment above」)改锚到具名符号(「SCOPE_ROOTS's own doc-comment」)。⇒ 日后有人移动块位置也无法再次悄悄证伪它 —— 这正是本卡这一类缺陷的复发路径。

② semver 定级 —— 与 changeset 声明一致?

changeset 本席判 依据
@objectstack/formula patch patch files[]dist,而本次 TSDoc 进入构建出的声明文件 ⇒ 有字节从已发布包发运 ⇒ ⛔ 不是 skip-changeset;公开面无增长(无新导出、成员零位移)⇒ ⛔ 不是 minor

⚠️⚠️ 诚实边界:dist/ 那条测量本席没有自己重跑(需整包构建)。本席采纳 dev 的三读数,因为它带双向对照:主体短语在 dist/index.d.tsdist/index.d.mts 各命中 1;亮对照(同一 docblock 里未被触碰的邻句)各命中 1;暗对照(捏造短语)命中 0。⇒ 结构上是一条可证伪的读数,⛔ 不是断言。并且它与本席自己量得到的前提一致:files[] = ['dist','README.md','CHANGELOG.md']
⭐ 该定级还有一道独立确认:CI 的 Check Changeset 在真实 pull_request 载荷上跑(本地跑读不到申报)。⛔ 本席不拿本地 exit 0 代表它。
⭐ 本仓两个方向的判例都在:#17701 / #17702patch(测到 TSDoc 进 dist/index.d.ts),#17717skip-changeset(同一测量返回 0)。同一条规则、相反测量、相反结论 —— 本轮落在前者。

③ 边界旗处置 —— 逐旗

open_questions 。两条 noted, not filed:

  1. SCOPE_ROOTS accepts data at scope: 'record', and as of objectui#8166 the consumer now FAULTS on it — a permanent lint/runtime disagreement with no open owner #17409 的 body 仍在描述被裁决 5648962048 取代的更宽范围,承接者点名本席、本次复核轮。⭕ 接受,且这是一条好发现 —— 上一轮正是有人只读 body 就烧掉了一次派发(本卡 premise_still_valid: false 那轮)。⇒ 处置:风险随本 PR 落地即关闭(body 第一行 Fixes #17409,卡随合并 auto-close),⛔ 因此不为它扩面、也不改写他人卡的 body —— 给他人卡 body 加取代横幅是分诊席的写权(判例:Phase 2 of #11333: retire the legacy string[] arm of manifest.permissions (major, standard retirement route) #13458Blocked-by: 取代横幅由分诊席所加)。⚠️ 若本 PR 因故未能落地,本席即请分诊加横幅。
  2. check:dual-build-cjs-loads / check:lean-entry-closure 在未整仓构建的工作区恒 exit 3,承接者「无」。⭕ 接受,并确认本席按 NOT MEASURED 读它们,⛔ 不读作通过 —— 这与本仓 exit 3 = PREREQUISITE NOT MET 的既有口径一致,且本 diff 不发射任何 JS 字节(只到 .d.ts),⛔ 无法移动这两道门。

独立性对

Implemented-by: claude/issue-17409-scope-roots-baseline-docblock
Reviewed-by: session_01RuoNSXUbBoWHkNS4AknTrM

(dev 为 mode:subagent,身份记分支;复核方记席位 session ⇒ ⛔ 非 SELF-REVIEW。本记录发布前已用 check-clause2-carriers.mjs 自身的 AUTHORSHIP_KEY_LINES / VALUE_DECORATION 正则跑过,并带四条控制项。)

判词

PASS。 ①②③ 无欠改。落地前检:① 本记录即是;② check-clause2-carriers --pair 17904 exit 0(申报可读、双肢一致、无放宽 tell);③ 待 CI 全绿(⛔ 全部 check,非 required 子集)。⇒ 全绿即转 ready + 挂 auto-merge。


Generated by Claude Code

@os-musk
os-musk marked this pull request as ready for review September 12, 2026 23:09
@os-musk
os-musk enabled auto-merge September 12, 2026 23:10
@os-musk
os-musk added this pull request to the merge queue Sep 12, 2026
Merged via the queue into main with commit de62769 Sep 12, 2026
36 checks passed
@os-musk
os-musk deleted the claude/issue-17409-scope-roots-baseline-docblock branch September 12, 2026 23:32
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

2 participants