Skip to content

feat(spec)+fix(approvals): approver value sources on the wire, author-facing type order, no more silent dead slots (#3508 / #3807) - #3817

Merged
os-zhuang merged 3 commits into
mainfrom
claude/approver-value-lookup-fix-t9hr0r
Jul 28, 2026
Merged

feat(spec)+fix(approvals): approver value sources on the wire, author-facing type order, no more silent dead slots (#3508 / #3807)#3817
os-zhuang merged 3 commits into
mainfrom
claude/approver-value-lookup-fix-t9hr0r

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3508 真机验收里发现的四条尾巴,一次收掉。框架侧完整;设计器侧(消费本 PR 新增的 xRef.sources)另开 objectui PR。

1|APPROVER_VALUE_SOURCES —— 设计器不必再自己猜候选从哪来(spec)

xRef.map 只说了渲染哪种 picker'team'),从没说这个 picker 的候选存在哪。于是设计器只能自带一份数据契约副本,而第一版副本恰恰是错的:所有目录类型都接到了 GET /api/v1/meta/:type(元数据注册表),而 sys_user / sys_team / sys_business_unit / sys_position记录根本不在注册表里 —— 候选恒空,控件退化成手填,这就是 #3508

现在把绑定投影到发布出去的 JSON schema 上,作为 xRef.sources

"xRef": {
  "kindFrom": "type",
  "map": { "department": "department",  },
  "sources": {
    "department": { "source": "data", "object": "sys_business_unit", "valueField": "id" },
    "position":   { "source": "data", "object": "sys_position",      "valueField": "name" },
    "org_membership_level": { "source": "enum", "values": ["owner","admin","member"] },
    "manager": { "source": "auto" }, "queue": { "source": "unsupported" }, 
  }
}

派生自 APPROVER_VALUE_BINDINGS,不是第二份真相:两者不可能漂移,且继承了原有的 satisfies 完备性 —— 新增 ApproverType 成员却不声明来源仍然是编译错误source: 'data' 这个词是刻意的,它对应的正是设计器过去打错的那个 meta

表现层(显示哪个字段、要不要弹 PeoplePicker、副标题放什么)仍归渲染方自己决定,spec 只出数据契约。

2|ApproverType 的声明顺序就是给作者的推荐(spec)

objectui#2834 主张「间接绑定优先、user 垫底」,但把顺序写在了它自己的 options 数组里 —— 而 Studio 的节点检查器根本不读那个数组,它是从发布的 JSON schema 里按本枚举(减去 xEnumDeprecated)派生的。所以那个引导从来没生效过,实测下拉仍是 User 排第一。

顺序只有写进枚举本身才算数,现在写进去了:

manager, position, department, team, field, expression, org_membership_level, user

绑定某个具体的人是作者能做的最不可移植的选择 —— 换环境那个 id 不存在,人一走审批就悄悄发给了不该看的人;manager / position / department / team 两样都扛得住。废弃的 role / queue 仍可 parse,靠 xEnumDeprecated 继续不出现在任何 picker 里,放在枚举何处都不影响。

3|图展开落空不再是静默的(plugin-approvals)

queue 早就有告警(#3508),但其余每一种图类型 —— team / department / position / org_membership_level / manager —— 查不到人时都落到同一个没人能操作的 type:value 字面量,且一个字都不说

这正是 #3807 能藏这么久的原因:请求照常打开、名单为空、日志无声,第一个症状是一条永远卡住的审批(#3424 就是它的下游形态)。

兜底字面量保留(15.x 的存量槽位和子串匹配的 fixture 依赖它),只是不再隐身:现在会带上 type、value、organizationId 记一条 warn。user / field 保持安静 —— 它们拿到 id 就用,压根没有「展开到空」这个状态。

4|plugin-sharing 那份同形代码,用测试钉住(仅测试)

BusinessUnitGraphService.orgScope 有与 #3807 完全同形的严格等值。今天不可达(实测所有物化的 sys_sharing_rule 都是 null-org,过滤器整个被跳过),而它是授权路径 —— 决定谁能看到记录 —— 在没有可复现失败的前提下不该跟着放宽。

所以不改行为,改成可执行的事实:新增 6 个用例,既锁住今天真正跑到的路径,也把「与 approvals 的分歧」本身写成一条名为 [divergence] 的测试。将来若平台统一认定 null-org = env-wide,那就是对一条有名字的测试做一次有意的修改,而不是一次静默的行为变化。

测试

结果
@objectstack/spec 6742 通过(新增 6:sources 完备性 + 上了 wire、枚举顺序、非授权拼写不出现)
@objectstack/plugin-approvals 258 通过(新增 6:四种图类型各自告警并带 type/value/org;能解析出人时保持安静;user 保持安静)
@objectstack/plugin-sharing 107 通过(新增 6 个 BU 图用例)
@objectstack/lint 471 通过
仓库 pnpm lint 干净

pnpm gen:schema 已重跑确认 xRef.sources 与新枚举顺序确实进了产物(packages/spec/json-schema/ 是 gitignore 的构建产物,故 diff 中不含)。

后续(不在本 PR)

objectui 侧消费 xRef.sources 替掉手写的 KIND_TO_RECORD_LOOKUP 镜像常量,并清掉 flow-node-config.ts 里那份不生效的 options 顺序。

🤖 Generated with Claude Code

https://claude.ai/code/session_01BVRVgSvmyCwmDfTmKmCZoH


Generated by Claude Code

…type enum for authors, surface dead approver slots (#3508 / #3807)

Four follow-ups from browser-verifying the #3508 approver work end to end.

1. `APPROVER_VALUE_SOURCES` (spec) — `xRef.map` named a picker KIND but never
   where its rows come from, so the designer kept its own copy of the data
   contract and the first copy pointed every directory kind at the metadata
   REGISTRY, which cannot list `sys_user` / `sys_team` / `sys_business_unit` /
   `sys_position` rows (#3508). The binding now ships on the published JSON
   schema as `xRef.sources`, derived from `APPROVER_VALUE_BINDINGS` so the two
   cannot drift and a new `ApproverType` with no declared source stays a
   compile error. Presentation stays a renderer decision.

2. `ApproverType` order (spec) — the Studio picker derives from this enum via
   the published schema, not from objectui's own options array, so the
   indirect-bindings-first order objectui#2834 argued for never took effect.
   The enum now carries it: manager, position, department, team, field,
   expression, org_membership_level, user. Deprecated `role`/`queue` still
   parse and stay out of every picker via `xEnumDeprecated`.

3. Dead approver slots (plugin-approvals) — `queue` already warned; every other
   graph type (`team`, `department`, `position`, `org_membership_level`,
   `manager`) fell back to the same unactionable `type:value` literal in total
   silence. That is what let #3807 hide: an empty slate, no log line, and a
   permanently stuck approval as the first symptom (#3424). The fallback stays;
   it now names the type, value and organization that produced it. `user` and
   `field` stay quiet — they never had an "expanded to nobody" state.

4. plugin-sharing's identical org scope (tests only) — `BusinessUnitGraphService`
   .orgScope carries the same strict equality #3807 fixed in approvals. It is
   unreachable today (every materialized `sys_sharing_rule` is null-org, so the
   filter is skipped) and it is an authorization path, so it is pinned by tests
   rather than widened blind: the reachable paths and the divergence itself are
   now executable facts.

Tests: spec 6742 pass (+6 new: sources exhaustive + on the wire, enum order,
non-authorable spellings); plugin-approvals 258 pass (+6: each graph type warns
with type/value/org, resolvable expansions and `user` stay quiet); plugin-sharing
107 pass (+6 new BU-graph cases); lint 471 pass; repo lint clean.

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

vercel Bot commented Jul 28, 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 Jul 28, 2026 7:31am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/plugin-approvals, @objectstack/plugin-sharing, @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/plugin-approvals, packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-sharing, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-sharing, @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-approvals, @objectstack/plugin-sharing, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/plugins/plugin-sharing, packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-approvals, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-approvals, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

…prover changes

`check:docs` and `check:api-surface` are both generated-artifact gates:

- `content/docs/references/automation/approval.mdx` — the `ApproverType` table
  and its Allowed Values list follow the enum's declaration order, which this
  branch reordered. Also reflowed the enum's own TSDoc: the doc generator emits
  one paragraph per source line, so the long ordering rationale now lives in
  `//` comments above the block and the JSDoc keeps a two-line summary.
- `packages/spec/api-surface.json` — picks up the new `APPROVER_VALUE_SOURCES`
  export.

NOTE: regenerating the API surface also absorbs drift that PREDATES this branch
— `MEMBERSHIP_ROLE_NAME_MIN_LENGTH` / `MEMBERSHIP_ROLE_NAME_PATTERN`,
`AppTranslationBundle*`, `ObjectTranslationNode*` added, and
`LEGACY_OBJECT_FIRST_KEYS` / `LegacyObjectFirstKey` / `TranslationItem*` /
`defineTranslation` removed. Those come from already-merged PRs that did not
re-run the generator; `check:api-surface` fails on main without any of this
branch's changes (verified by stashing them and re-running the gate). The
snapshot exists to mirror the real export surface, so it is brought fully in
sync rather than hand-patched to only this branch's line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVRVgSvmyCwmDfTmKmCZoH
@github-actions github-actions Bot added size/l and removed size/m labels Jul 28, 2026
…new export

The previous commit regenerated the snapshot against a `dist/` that turbo had
restored from another checkout's cache, so it recorded exports this branch's
source does not have (`AppTranslationBundle*`, `ObjectTranslationNode*`,
`MEMBERSHIP_ROLE_NAME_*`) and dropped ones it does (`defineTranslation`,
`TranslationItem*`, `LEGACY_OBJECT_FIRST_KEYS`). The generator reads the BUILT
`.d.ts` from the exports map, so a stale dist silently produces a wrong
snapshot — and it read as 8 breaking removals, which is exactly the alarm the
gate exists to raise.

`rm -rf packages/spec/dist && pnpm --filter @objectstack/spec build` first, then
regenerate: the snapshot now differs from main by the single line this branch
actually adds, `APPROVER_VALUE_SOURCES (const)`, and `check:api-surface`
reports the surface unchanged. Every other spec gate (docs, skill-refs,
skill-docs, react-blocks, spec-changes, upgrade-guide) re-verified green off
the clean build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVRVgSvmyCwmDfTmKmCZoH
@github-actions github-actions Bot added size/m and removed size/l labels Jul 28, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 07:45
@os-zhuang
os-zhuang merged commit 0f8ad09 into main Jul 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/approver-value-lookup-fix-t9hr0r branch July 28, 2026 07:45
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.

2 participants