Skip to content

fix(lint): org-axis 红线读 spec 声明的 sharing-rule 键 —— ADR-0105 D6 两条门禁此前从不触发 (#4984) - #4992

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4984-org-axis-redlines-dead-keys
Aug 3, 2026
Merged

fix(lint): org-axis 红线读 spec 声明的 sharing-rule 键 —— ADR-0105 D6 两条门禁此前从不触发 (#4984)#4992
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-4984-org-axis-redlines-dead-keys

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #4984

死态复现(before 证据,最新 main)

validateOrgAxisRedLines 注册为 input: 'parsed',即跑在 Zod parse 之后。它取 rule.criteria ?? rule.filterrule.sharedTo ?? rule.recipient,而 SharingRuleSchema.strict() 的、声明的键是 condition / sharedWith —— 那四个名字只作为被拒别名出现在 sharingRuleUnknownKeyError 里(那是给报错信息用的处方,不是被接受的键)。

c87ef7034(本分支的基点)上实测:

=== 1. spec-valid 且违反 D6 ① 的 sharing rule(真实键名) ===
safeParse ok: true
  {"name":"hq_sees_children","object":"work_order","active":true,"accessLevel":"read",
   "sharedWith":{"type":"team","value":"hq"},"type":"criteria",
   "condition":{"dialect":"cel","source":"record.parent_organization_id == 'org_hq'"}}
findings on PARSED stack: []          ← 判绿
findings on RAW (pre-parse) stack: [] ← 判绿

=== 2. 现有测试 fixture 用的死键,过不了 schema ===
name,object,criteria  => REJECTED: Unrecognized key(s) on this sharing rule: `criteria`.
name,object,sharedTo  => REJECTED: Unrecognized key(s) on this sharing rule: `sharedTo`.

=== 3. 平台级对象上的 BU 授权(真实键名,D6 ②) ===
safeParse ok: true
findings: []                          ← 判绿

即:一条声明为 error、以 ADR-0105 D6 红线为依据的门禁,在 sharing-rule 这条路径上对任何 spec 合法的 stack 永不触发;而它自己的测试 fixture 用的正是那些被拒的死键,所以测试全绿、规则全死

改了什么

1. 规则改读 canonical 键。 rule.condition / rule.sharedWith,不再有 ?? 别名兜底。别名已经在 sharingRuleUnknownKeyError 里有明确处方并被 parse 拒收 —— consumer 不该容忍 producer 契约拒绝的东西(Prime Directive #12)。新增一条测试把这个决定钉住:写 criteria / filter / sharedTo / recipient 的规则判红,交给 spec 的拒收。

2. 语义按 D6 原文逐条搬,不是只换名字。 别名与真键的形状确实不同:

死键 真键 形状差异
谓词 criteria / filter condition ExpressionInput —— 裸串(作者态)/ { dialect, source }(parse 后)/ { dialect, ast }(compile 后),三种都要能取到文本,新增 expressionText()
收件人 sharedTo / recipient sharedWith .strict(){ type, value };id 也是被拒别名

expressionText() 刻意不读 meta.rationale —— 那段散文完全可能正当地提到它正在解释「本规则不使用」的那个字段,error 级门禁不能被注释误伤。

path 从笼统的 sharingRules[i] 细化到 sharingRules[i].condition / sharingRules[i].sharedWith,两个槽位各自点名。

3. fixture/schema 漂移守卫(本单的结构性修复)。 只改键名的话,同样的漂移下次还来。现在每个 sharing-rule fixture 都过 sharingRule(),它先用真的 SharingRuleSchema parse 再交给 lint;object fixture 同理走 objectFixture()ObjectSchema。fixture 一旦与 spec 漂移就在 fixture 处判红,而不是去测一个作者写不出来的形状。元测试本身也自证不死:把 #4984 之前的两个 fixture 原样喂进去,断言它们抛错

反向验证

场景 结果
真实键名的违规 stack(修复后) 判红并点名 sharingRules[0].condition / .sharedWith
合法 stack(membership / BU / 非 BU 收件人) 判绿
把规则改回读死键,跑新测试 7 failed —— 元测试与行为测试同时判红

改回死键时的失败清单:

× flags a spec-valid sharing rule whose `condition` walks the org parent
× flags the pre-parse `condition` shape too (`os lint` runs on the normalized stack)
× flags the compiled `{ dialect, ast }` condition shape
× flags a recipient that reaches for the org parent
× does NOT resurrect the schema-rejected aliases — parse is that gate
× flags a business-unit grant on a `tenancy.enabled: false` object
× flags the `systemFields.tenant: false` spelling of the same opt-out
 Tests  7 failed | 11 passed (18)

真实元数据上是否有新红

没有。 D6 是 error 级,所以逐条核过:

  • examples/app-showcase / examples/app-crm 的全部 sharing rule:没有任何 conditionsharedWith 提到 parent_organization_id;
  • 全仓没有对象声明 tenancy.enabled: falsesystemFields.tenant: false,所以 D6 ② 也无处触发;
  • default-permission-sets.tsexamples/app-showcase/src/security/permission-sets.ts 的 RLS using/check:只有 assignee == current_user.email / owner == current_user.email 一类,无 org 轴引用。

验证

$ pnpm --filter @objectstack/lint test
 Test Files  54 passed (54)
      Tests  987 passed | 4 skipped (991)

$ pnpm --filter @objectstack/lint typecheck
(clean)

$ npx eslint packages/lint/src/validate-org-axis-red-lines{,.test}.ts --no-inline-config
(clean)

顺手发现,已单独建单(未随本 PR 修)


Generated by Claude Code

`validateOrgAxisRedLines` 取 `rule.criteria ?? rule.filter` 与
`rule.sharedTo ?? rule.recipient`,而 `SharingRuleSchema` 是 `.strict()` 的、
声明的键是 `condition` / `sharedWith` —— 那四个名字只作为**被拒别名**存在于
`sharingRuleUnknownKeyError` 里。规则跑在 parse 之后(`input: 'parsed'`),
所以对任何 spec 合法的 stack 这四个属性恒为 undefined,
`parent_organization_id` 的判定恒为 false:一条 error 级红线在 sharing-rule
路径上从不触发。

改为只读 canonical 键。别名不在 consumer 侧用 `??` 容忍 —— schema 的拒收
信息已经给出处方,parse 就是那道门。`condition` 是 `ExpressionInput`,
三种形状(裸串 / `{dialect,source}` / 编译后 `{dialect,ast}`)都要能扫到。

结构性的一半在 fixture:原测试用的正是那些被拒别名,所以**测试全绿而规则全死**。
现在每个 sharing-rule fixture 先过 `SharingRuleSchema`、每个 object fixture 先过
`ObjectSchema` —— fixture 与 spec 漂移即在 fixture 处判红,而不是去测一个
作者写不出来的形状。

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 6:57pm

Request Review

@github-actions github-actions Bot added 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.

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/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validateOrgAxisRedLines 读的 sharing-rule 键是 spec 拒收的:ADR-0105 D6 ① 在 criteria 路径上从不触发

2 participants