Skip to content

fix(lint): 字段公式校验读回声明的 expression —— 激活一条从未跑过的检查 (#5026) - #5118

Merged
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-5026-field-formula-activate
Aug 4, 2026
Merged

fix(lint): 字段公式校验读回声明的 expression —— 激活一条从未跑过的检查 (#5026)#5118
xuyushun441-sys merged 2 commits into
mainfrom
claude/issue-5026-field-formula-activate

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes#5026

这一单和 #4984 / #5009 / #5017 的差别

同族的前三轮都是删死代码:别名 limb 在链里,canonical limb 也在,删掉别名对任何能解析的 stack 零行为变化。这一条不是。

validate-expressions.ts 的字段公式校验读 f.formula,而 FieldSchema 声明的是 expression —— formula 恰恰是 field.zod.ts:333按名拒绝的别名:

aliases: { formula: 'expression', calculation: 'expression', compute: 'expression' }

规则以 input: 'parsed' 注册(authoring-rules.ts),compile/build/validate 路径上它看到的是 ObjectStackSchema 的解析产物,所以 f.formula恒为 undefined。代码里根本没有f.expression 的读法。结论:整段检查对任何 spec 合法 stack 从未执行过一次。

所以把读法收敛为 f.expression启用一条从未跑过的检查,是覆盖面扩大 —— 不是删死代码。这也是它没跟着 #5017 顺手带上、而是单独一单的原因。

真实元数据实测:零新红

激活是覆盖面扩大,可能对现有元数据判红 —— 那是本单的目的而非事故,所以逐条跑过、逐条定性。用激活后的规则在全部真实元数据上实测(先 ObjectStackSchema.safeParse,再喂 validateStackExpressions,和 compile 路径完全一致):

元数据解析本次激活新访问的 expression新判决
examples/app-showcase30
examples/app-crm50
examples/app-todo00
packages/platform-objects0(无公式字段)0
plugin-security default-permission-sets0(无公式字段)0
skills/ 公式样例(3 处)30(已是 canonical)

八个真实公式槽全部判绿。这不是"门没开"——反向验证里坏 expression 确实判红(见下),所以绿是判别的结果,不是不可达的结果。

顺带扫到的两处裸引用样例在文档/博客里(content/docs/data-modeling/fields.mdx:230'quantity * price * …'content/blog/context-window-is-the-constraint.mdx:108cel`amount * probability`),两条都实测判红。它们是 markdown 散文、不在本规则的消费半径内,按 Prime Directive #10 另开 #5116 记账,没有在本 PR 里改(本 PR 限于 packages/lint)。

双向证明

新增 validateStackExpressions — the field-formula check now actually runs (#5026),四条,全部执行而非描述:

  1. spec 合法 stack 上坏 expression判红且点名 —— unknown field 'no_such_field',定位到 field 'expected_revenue' expression;1b 语法坏的同样判红;
  2. 正确的 expression判绿 —— 门会判别,不是一律亮;
  3. formula: 拼法交给 schema 按名拒绝(Did you mean 'formula' → 'expression'?),本规则不再对同一个键给第二套说法;
  4. 变异:把源码里的读法改回 f.formula,fix(lint): 收敛 validate-expressions / validate-security-posture 的 spec 不声明键 ?? 别名读法 (#5017) #5046 的 declared-key meta-guard 用同一段 source scan 判红并点名 formula —— 接住了,已验证。

另有 reachability 一条:fields[].expression 的判决从一个真正 ObjectStackSchema.parse的 stack 上产生 —— 这句话在本 PR 之前从来不成立。

TRACKED_UNDECLARED_READS 清账

那份"只缩不长"的清单唯一一条就是 { receiver: 'f', key: 'formula', issue: 5026 }。本单清空到 0:规则读的每一个键都是 spec 声明的键。断言从"恰好等于这一条 debt"改成 toEqual([]),再加一条独立断言把"清单为空"钉住,任何新增条目都成为一次刻意行为。

消费半径扫描(#5046 返工的教训)

全仓 grep formula:字段谓词键的测试 fixture:

其他改动

  • 诊断定位串 … field 'Y' formula… field 'Y' expression。理由写在代码注释里:说 "formula" 正是错拼法的传播路径 —— 下一位作者读了诊断就去写 formula:,然后被 schema 拒。消息要点名作者要改的那个键
  • validate-null-guards.ts 的 surface ledger 把该行从 Field.formula 正名为 field expression(Field.formula({ expression: … }) 写入的槽),并写明:排除的只是 null-guard 这一条判决(公式是 value 角色、天然可空,guard ? value : null 是祝福写法),语法 / 字段存在性 / 裸引用判决从此生效。对应的那条测试也补了注释 —— 它现在零 issue 是"gate 确实被排除",而不是过去的"surface 压根不可达"。
  • changeset:@objectstack/lint minor。

验证

pnpm --filter @objectstack/lint test → 57 files / 1169 tests passed
pnpm --filter @objectstack/lint typecheck → tsc --noEmit, clean
eslint (三个改动文件) → exit 0
pnpm --filter @objectstack/cli test → 68 files / 600 tests passed

已 merge origin/main(5 个新 commit,均不触及 packages/{lint,spec,formula},无冲突),在合并后的树上重跑 lint 测试 + typecheck 仍全绿。


Generated by Claude Code

…ating a pass that never ran (#5026)
`validate-expressions.ts`'s field-formula pass read `f.formula`. `FieldSchema`
declares the computed slot as `expression`; `formula` is one of the names
`field.zod.ts:333` rejects BY NAME (`aliases: { formula: 'expression', … }`).
The rule is registered `input: 'parsed'`, so on the compile path it sees
`ObjectStackSchema`'s output, where `f.formula` is always `undefined` — the
whole branch had never executed against a stack an author can ship.
Converging the read onto `expression` therefore ACTIVATES a check rather than
deleting a dead branch: field formulas now carry the ADR-0032 §1a/1b verdicts
(CEL syntax, `record.<field>` existence, the #1928 bare-reference and
type-soundness tiers). Bare refs in a formula silently evaluate to null, and
they are the single most common defect in AI-authored formula slots, which is
what #1928 built this check for.
Coverage widened, zero new findings on real metadata. Swept every stack in the
repo with the activated check: examples/app-showcase (3 `expression` slots),
examples/app-crm (5), examples/app-todo (0) all parse and stay green;
platform-objects and plugin-security's default-permission-sets declare no
formula fields; the `skills/` samples are already canonical. Two bare-ref
samples in `content/docs` + `content/blog` are out of this rule's consumer
radius and filed as #5116 rather than fixed here.
- `TRACKED_UNDECLARED_READS` in the meta-guard drops to EMPTY — every key the
rule reads is now one the spec declares.
- Diagnostic locator renamed `… field 'Y' formula` → `… field 'Y' expression`,
so the message names the key the author edits instead of propagating the
spelling the schema refuses.
- The 7 field-predicate fixtures spelled `formula:` move to `expression:`; a
`fields[].formula → expression` case joins the rejected-alias table (schema
refuses by name, this rule stays silent) and a reachability case proves the
pass fires from a stack that really parses.
- New reverse verification: broken `expression` red, correct one green,
`formula:` left to the schema, and the read mutated back to `f.formula`
caught by the declared-key guard — executed, not asserted.
- `validate-null-guards.ts`'s surface ledger renames the row to field
`expression` and states that only the NULL-GUARD verdict stays excluded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
@vercel

vercelBot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 4, 2026 5:23am

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/m labels Aug 4, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx(via @objectstack/lint)
  • content/docs/permissions/authorization.mdx(via @objectstack/lint)
  • content/docs/releases/v17.mdx(via @objectstack/lint)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validate-expressions.ts 的 field-formula 校验读 f.formula —— spec 声明的是 expression,这段从未对任何 spec 合法 stack 跑过

2 participants

@xuyushun441-sys@claude