Skip to content

fix(lint): 删除 validateOrgAxisRedLines 里 spec 合法 stack 到不了的四条分支 (#5009) - #5018

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-5009-org-axis-dead-branches
Aug 4, 2026
Merged

fix(lint): 删除 validateOrgAxisRedLines 里 spec 合法 stack 到不了的四条分支 (#5009)#5018
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-5009-org-axis-dead-branches

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #5009

validate-org-axis-red-lines.ts 注册为 input: 'parsed' —— 它看到的是 ObjectStackSchema 解析后的产物。#4984 修掉了 sharing rule 字段那一层的 ?? 别名读法,同一文件里还留着四条同形分支(议题点名三条,核对时又发现第四条),读的键 spec 都不声明。

逐条核实与处置

每一条都用真实 schema 的 .shapesafeParse 实测核过,不是靠读注释。

原读法 spec 事实(实测) 处置
cfg.permissions ?? cfg.permissionSets ObjectStackSchema.shapepermissionSets。stack 根 strip 未声明键:safeParse({ manifest, permissionSets: [...] }) 成功,但返回的 data 里没有该键 收敛为 cfg.permissions
cfg.sharingRules ?? cfg.sharing(两处) 同上,sharing 也被 strip 收敛为 cfg.sharingRules
str(rule.object ?? rule.objectName)(议题未列,同形第四条) SharingRuleSchema.strict(),按名拒绝:Unrecognized key(s) on this sharing rule: objectName``;而 object必填,解析过的规则上不可能缺 收敛为 str(rule.object)
asArray(object.rowLevelSecurity ?? object.rls) 整段(约 20 行) ObjectSchema.shape 两个键都没有,且 ObjectSchema.strict() —— 带对象级 RLS 的 stack 在 os validate / os build整包拒绝:Unrecognized key(s) on this object: rowLevelSecurity`` 删除

对象级 RLS 从来不是可授权面:authorable-surface.json 里唯一一条是 security/PermissionSet:rowLevelSecurity。删除依据(schema 名 + 该键不存在的证明)已写进 commit message 和规则自己的 ## Scope 段。

危险不在死代码,在误导

这四条对任何 spec 合法 stack 都不执行,所以漏报为零。真正的代价是:那段死代码连 objects[N].rowLevelSecurity[M].using 的诊断 path 都写好了,足以让下一位作者(人或 AI)相信对象级 RLS 是真实授权面并照着写更多代码 —— #5008 的 dev 差点就照它复制了一条同样惰性的遍历。

别名容忍属于 producer 的拒绝,不属于 consumer(Prime Directive #12)。它还把一条指名道姓的 schema 拒绝,悄悄换成了一条永不触发的 gate。

反向验证

  • 删除的分支:把新测试跑在改动前的实现上 —— 29 条由 safeParse fixture 驱动的断言全绿,8 条转红的全部是 (a) 扫源码的 meta-guard 或 (b) 喂非 spec 合法 stack 的新钉子测试。即:删除前后对全部 spec 合法 fixture 行为完全一致,它本来就死。
  • canonical 化的三条:这里要如实说明,与议题预设的 "before 判绿 / after 判红" 不同 —— canonical 键本来就是 ?? 的第一顺位,spec 合法的违规 stack 改动前已经判红。别名 limb 只是额外在 schema 会拒绝的形状上可达。所以正确的反向验证是反过来的:改动前对 permissionSets / objectName 拼法的非法 stack 会判红,改动后判绿,并由钉子测试证明 schema 才是那道门。(validate-expressions.ts:205 那条就没这么幸运 —— canonical 排第三顺位,见 validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017。)
  • 变异验证:四条分支各自加回去跑一遍,每条都至少让两条测试转红。

meta-guard 全覆盖(#4992 模式)

留下的每条遍历分支都要有过 safeParse 的 fixture 能走到;走不到的不允许存在。

  1. declared-key guard —— 规则源码里从 stack / permission set / RLS policy / object / sharing rule 上读的每一个键,必须出现在对应 schema 自己的 Zod .shape 里。扫源码而不是扫行为是刻意的:不可达分支根本没有行为可断言,这正是问题本身。计算键(policy[clause])另用一条断言把 ['using', 'check'] 钉在 RowLevelSecurityPolicySchema 上。
  2. reachability guard(议题提到的"覆盖率式结构性断言",本 PR 落地了)—— 从源码里抽出每个 findings.push 调用点的 rule:path: 模板,断言现存三个点各自被至少一条过 safeParse 的 fixture 触达;并反向断言没有 fixture 产出源码未声明的 path。加回删掉的第四个点会同时触发数量断言和可达性断言。
  3. 规则 ① 的 fixture 现在也走 PermissionSetSchema.safeParse(此前只有 sharing rule 和 object fixture 有这层保护)—— 顺带暴露出旧 fixture 缺 objects / operation,并不 spec 合法。

真实元数据零新红

  • examples/default-permission-sets.ts没有 parent_organization_id,规则 ① 无从触发;
  • examples 里唯一的 rowLevelSecurity 用法在 permission set 上(app-showcase/src/security/permission-sets.ts:57),正是保留的那条分支;
  • 出现的 sharing: 都在 view 定义内(inquiry.view.ts / lead.view.ts),不是 stack 根键,本规则从不读它。

改动纯粹是减法,findings 集合单调不增,不可能新增红。

顺带核对(不在本 PR)

议题建议 3 要求核对邻居规则。validate-expressions.ts(三处)与 validate-security-posture.ts(两处)有同形别名读法,其中 validate-expressions.ts:205/421 更糟 —— canonical 的 condition 排在两个被拒别名之后。已按 Prime Directive #10 记为 #5017(未认领),不在本 PR 修。

验证

pnpm --filter @objectstack/lint test     → 55 files, 1029 passed | 4 skipped
pnpm --filter @objectstack/lint typecheck → tsc --noEmit, clean
npx eslint <两个改动文件>                  → clean

Changeset:.changeset/org-axis-dead-alias-branches.md(@objectstack/lint patch)。改动限于 packages/lint


Generated by Claude Code

该规则注册为 `input: 'parsed'`,看到的是 `ObjectStackSchema` 解析后的产物。
#4984 修掉了 sharing rule 字段层的 `??` 别名读法,同一文件里还留着四条同形
分支,读的键 spec 都不声明 —— 逐条对着 schema 的 `.shape` 与 `safeParse`
实测核过:

- `cfg.permissions ?? cfg.permissionSets` → `cfg.permissions`。
  `ObjectStackSchema.shape` 无 `permissionSets`;stack 根 strip 未声明键,
  实测 `safeParse({ manifest, permissionSets: [...] })` 成功但 `data` 里
  没有该键 —— 规则看到 stack 之前它已经不存在。
- `cfg.sharingRules ?? cfg.sharing`(两处)→ `cfg.sharingRules`。同上。
- `str(rule.object ?? rule.objectName)` → `str(rule.object)`。
  `SharingRuleSchema` 是 `.strict()`,`objectName` 被按名拒绝
  ("Unrecognized key(s) on this sharing rule: `objectName`");`object`
  又是必填,解析过的规则上不可能缺。
- `asArray(object.rowLevelSecurity ?? object.rls)` 整段遍历(约 20 行)
  **删除**。依据:`ObjectSchema.shape` 两个键都没有(实测键表里只有
  `sharingModel` / `access` / `tenancy` 等,无 `rowLevelSecurity`、无 `rls`),
  且 `ObjectSchema` 是 `.strict()` —— 带对象级 RLS 的 stack 在
  `os validate` / `os build` 被整包拒绝,报 "Unrecognized key(s) on this
  object: `rowLevelSecurity`"。对象级 RLS 从来不是可授权面
  (`authorable-surface.json` 里只有 `security/PermissionSet:rowLevelSecurity`)。

对任何 spec 合法的 stack,判定结果不变 —— 反向验证:新测试跑在改动前的
实现上,29 条由 `safeParse` fixture 驱动的断言全绿,8 条转红的全部是
(a) 扫源码的 meta-guard,或 (b) 喂非 spec 合法 stack 的新钉子测试。

代价从来不是漏报,是误导:那段死代码连 `objects[N].rowLevelSecurity[M].using`
的诊断 path 都写好了,足以让下一位作者相信对象级 RLS 是真实授权面并照着写
(#5008 差点如此)。别名容忍属于 producer 的拒绝,不属于 consumer(Prime
Directive #12)。

meta-guard(#4992 模式),让下一条死分支在 review 前就红:

- declared-key guard:规则源码里从 stack / permission set / RLS policy /
  object / sharing rule 上读的每个键,必须出现在对应 schema 自己的 `.shape`
  里。扫源码而非行为是刻意的 —— 不可达分支没有行为可断言。
- reachability guard:每个 `findings.push` 调用点都必须被至少一条过
  `safeParse` 的 fixture 触达(现存三个点,全覆盖)。
- 规则 ① 的 fixture 现在也走 `PermissionSetSchema.safeParse`。

四条分支各自做过变异验证:加回任意一条,至少两条测试转红。

真实元数据零新红:examples/ 与 default-permission-sets 中无
`parent_organization_id`,唯一的 `rowLevelSecurity` 用法在 permission set
(保留的那条分支)上,`sharing:` 出现在 view 定义内而非 stack 根。

邻居规则同形别名读法已另行记账为 #5017(未认领),不在本 PR 范围。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 8:42pm

Request Review

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

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.

@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 3, 2026 20:44
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 3, 2026
Merged via the queue into main with commit b821b29 Aug 4, 2026
24 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-5009-org-axis-dead-branches branch August 4, 2026 00:04
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/l tests tooling

Projects

None yet

2 participants