Skip to content

fix(platform-objects, lint): the Business Unit page and the readScope hint say what needs the enterprise edition - #30622

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-pkx5by
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-pkx5by

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:server#6 · os-bill · session_01Utp6CXCS98uhsFQYk64epE (dev: card thread session_01Utp6CYwoPbRRkiyph2ufCw)

维护者速读

  • 改了什么:业务单元页面说明改成「开源版里共享规则可以按业务单元共享、成员离开即失去;按业务单元读取全部记录需要企业版」;业务单元时区帮助不再声称日期按它计算;os validate 的 security-private-no-readscope 提示在项目没声明 hierarchy-security 时只建议 org,并写明另外三个范围需要企业包。
  • 为什么改:管理员照页面和提示去配,结果开源版编译直接拒绝(Setup and tools: say which organization features need the enterprise edition, and remove fields that do nothing #30210 第 1、2 行);时区帮助说的事运行时根本没人读。
  • 风险与回滚:只改说明文字和一条 info 级提示的 hint,编译拒绝、规则触发条件与级别都不变;回滚即 revert 本 PR。
  • 先看:packages/lint/src/validate-security-posture.ts(hierarchyScopesCompile 那段)、packages/platform-objects/src/identity/sys-business-unit.object.ts(description 与 timezone)、zh-CN.objects.generated.ts 两处中文。
  • 两分钟验证:pnpm --filter @objectstack/lint exec vitest run src/validate-security-posture.test.ts -t hierarchy-security,预期 2 条通过;无 requires 时 hint 只建议 'org' 并点名 @objectstack/security-enterprise。
  • 需要你做的:无,席位落地。

Refs #30210 (rows 1, 2)

Clause-②: no
Metadata-protocol: none
Skills-docs: none (git grep of skills/** for business unit, readScope and security-private-no-readscope: 6 hits in skills/objectstack-data — SKILL.md lines 73 and 388, rules/security.md lines 178 and 252, one eval; none states a sentence this diff makes false)

What changes

  • Row 1 — Business Unit page. sys_business_unit.description (the page text) said units are "used to scope data and reports". It now says what a unit does in the open edition (a sharing rule can share records with a unit, or a unit and the units under it; members get that access and lose it when they leave — measured on the card) and that letting a permission set read every record of a unit (read scope unit) needs the enterprise edition. The compile refusal (validateHierarchyScopeCapability in stack.zod.ts) is unchanged, as the card asks. zh-CN value hand-written; en regenerated by pnpm i18n:extract.
  • Seat amendment 6662593558 — Business unit Timezone help. It said date boundaries are computed in this zone; nothing reads sys_business_unit.timezone (same reading as sys_organization.timezone, corrected in One organization shows three names (Branding, the Organization record, the product chrome) and two timezones (Edit Organization, Localization) — one place for each, or say which wins (hotcrm#2194 T2-U20, R2-T2-U10) #25347). The help now says it changes no date calculation and names Settings → Localization → Default timezone, mirroring the organization's help. en + zh-CN. No field change.
  • Row 2 — security-private-no-readscope hint (packages/lint, run by os validate / os lint). The rule now reads the stack's requires — the same declaration defineStack reads before refusing a hierarchy scope. With hierarchy-security declared, the hint is unchanged. Without it, the hint offers readScope: 'org' or widening sharingModel, and says 'own_and_reports', 'unit' and 'unit_and_below' need requires: ['hierarchy-security'], which @objectstack/security-enterprise provides. A stack with no requires (the runtime metadata door hands none) gets the requirement named too. Same grants fire, same info severity.

Not built here: row 3 (Timezone on Edit organization) is measured only, per the order — removing a sys_organization field narrows an accept set and is the owning lane's ADR-0049 removal. Row 4 (hide Parent Organization / Sort Order on one organization) stopped as needs_decision: field and field-group visibleWhen bind only record / previous / parent at authoring (FIELD_RULE_BOUND_ROOTS, packages/lint/src/validate-expressions.ts), and fields carry no requiresFeature, so hiding needs a new metadata capability. Both readings and the row 4 options are in the os-dev-report on the card.

Tests

  • packages/platform-objects/src/identity/sys-business-unit-edition-copy.test.ts (new): the description names sharing rules and the enterprise edition and drops the scoping claim; the zh-CN description says 共享规则 / 企业版; the Timezone help (en, zh-CN) says it changes no date calculation and names Localization's Default timezone.
  • packages/lint/src/validate-security-posture.test.ts: two cases in the existing security-private-no-readscope block — without hierarchy-security, the hint offers 'org' before any hierarchy scope and names the requirement in the same sentence as all three; with it, every scope is offered and the enterprise package is not named. The validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017 key-surface meta-test now lists requires among the keys read off stack (declared by ObjectStackSchema).
Red on main 2ffdb64, green on the fix
red (main 2ffdb649, tests only):
  lint            × without hierarchy-security, offers org and names the requirement beside the hierarchy scopes   Tests 1 failed | 1 passed
  platform-objects × all 4 cases   Tests 4 failed (4)
green (fe3d2a1e):
  @objectstack/lint test              Test Files 195 passed (195)  Tests 8105 passed (8105)
  @objectstack/platform-objects test  Test Files 114 passed (114)  Tests 1567 passed (1567)
  both typechecks exit 0
Ablation (once): the requires read replaced by true
ablation-replace: ok mutation landed: anchor 1 -> 0, blob d87715223f07 -> 6eded5d4c050
  × without hierarchy-security, offers org and names the requirement beside the hierarchy scopes
  × every key read off 'stack' is declared by 'ObjectStackSchema'
restored: git hash-object == HEAD blob d87715223f07, git diff HEAD empty

Acceptance notes

  • The order's file surface named packages/cli/src/** for the advisory; it lives in packages/lint. The seat's amendment 6662593558 added packages/lint/src/validate-security-posture.ts and its tests.
  • pnpm i18n:extract rewrote only en; ja-JP / es-ES kept their existing values (their source-hash ledgers do not cover these keys), and check:i18n / check:i18n-stale-fill are green.
  • sys_business_unit's docblock lists a bu: approver prefix in the approval engine; plugin-approvals has no such prefix on main. A code comment, not admin-facing — left alone.

Verification

All local readings on head fe3d2a1 (after merging origin/main bdbaf80, which carries #30551; the delta vs main is exactly the 7 files of this PR). pnpm i18n:extract after the merge wrote no change.

dispatch-gates: 59 derived families, 59 run, 0 NOT-MEASURED; plus both packages' test and typecheck and pnpm lint — 64/64 exit 0
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-stale-fill :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
pnpm --filter @objectstack/lint test :: exit 0
pnpm --filter @objectstack/lint typecheck :: exit 0
pnpm --filter @objectstack/platform-objects test :: exit 0
pnpm --filter @objectstack/platform-objects typecheck :: exit 0
pnpm lint :: exit 0

dispatch-gates --ran: 59 derived famil(ies) accounted for — 59 run, 0 NOT-MEASURED

CI families are read from this PR's check-runs, not re-run here.


Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/platform-objects, touching 5 documentable anchor(s).

14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/approvals.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects))
  • content/docs/kernel/runtime-services/sharing-service.mdx (via own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/access-recipes.mdx (via own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/administrator-guide.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/attachments-access.mdx (via unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/authorization.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects), validateSecurityPosture (symbol, a top-level function))
  • content/docs/permissions/capabilities.mdx (via own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/delegated-administration.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects))
  • content/docs/permissions/index.mdx (via own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/permission-sets.mdx (via own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/permissions-matrix.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects), own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/positions.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects), own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/permissions/sharing-rules.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects), own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/protocol/objectql/security.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects))

⛔ 6 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v13.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects), validateSecurityPosture (symbol, a top-level function))
  • content/docs/releases/v15.mdx (via unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/releases/v16.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects))
  • content/docs/releases/v17/17-4.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects))
  • content/docs/releases/v18/18-2.mdx (via own_and_reports (literal, a string literal in validateSecurityPosture), unit_and_below (literal, a string literal in validateSecurityPosture))
  • content/docs/releases/v18/18-3.mdx (via sys_business_unit (symbol, a field of const object enObjects; a field of const object zhCNObjects))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 103 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 6 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 1c5cee6736518291d86148cd69124542dfc0d021 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 55fb1cd9814020de03bc77895a563c3333932b7e — the merge of head fe3d2a1ed33cdd09dad25b51e40d784b01ed6670 into base 1c5cee6736518291d86148cd69124542dfc0d021, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 55fb1cd9814020de03bc77895a563c3333932b7e && git checkout 55fb1cd9814020de03bc77895a563c3333932b7e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1c5cee6736518291d86148cd69124542dfc0d021 fe3d2a1ed33cdd09dad25b51e40d784b01ed6670 && git checkout -B drift-repro 1c5cee6736518291d86148cd69124542dfc0d021 && git merge --no-ff fe3d2a1ed33cdd09dad25b51e40d784b01ed6670

node scripts/docs-audit/affected-docs.mjs --json 1c5cee6736518291d86148cd69124542dfc0d021

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 1c5cee6736518291d86148cd69124542dfc0d021 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#6 · os-bill · session_01Utp6CXCS98uhsFQYk64epE

席位记录 · 2026-11-05T18:44Z · Head-sha fe3d2a1e · ACCEPT

维护者速读(席位按 diff 校正)

  • 改了什么:业务单元页面的说明改成两点:开源版里共享规则可以按业务单元共享,成员离开即失去;按业务单元读取全部记录(读取范围 unit)需要企业版。业务单元「时区」的帮助不再声称日期按它计算,改为指向 设置 → 本地化 → 默认时区。os validate 的 security-private-no-readscope 提示在项目没声明 hierarchy-security 时只建议 'org',并写明另外三个范围需要企业包。
  • 为什么改:Setup and tools: say which organization features need the enterprise edition, and remove fields that do nothing #30210 第 1、2 行,外加席位修订 6662593558。管理员照页面和提示去配,开源版编译直接拒绝;时区帮助说的事运行时没有任何代码在读。
  • 风险与回滚:只改说明文字和一条 info 级提示的 hint。编译拒绝、规则触发条件和级别都不变。回滚即 revert 本 PR。
  • 先看:packages/lint/src/validate-security-posture.ts(hierarchyScopesCompile)、sys-business-unit.object.ts 的 description 与 timezone。
  • 两分钟验证:pnpm --filter @objectstack/lint exec vitest run src/validate-security-posture.test.ts -t hierarchy-security,2 条通过。
  • 需要你做的:无。非受管面、非 BREAKING、Clause-②: no,不碰四个复核面,按技能由 CI 加席位自读,席位落地。第 4 行要你裁定,交接给席位 2。

席位意见:同意落地。提示读的是 requires,和 defineStack 拒绝层级范围时读的是同一个声明,所以只会建议这个项目编得过的范围。时区帮助的改法和 #25347 改组织时区时一致。

席位核对

  1. 形态:draft→ready,目标 main,Refs #30210 (rows 1, 2),无闭合关键词;作者 objectstack-fleet[bot](中继)。
  2. 前提实测:报告 6665712637 显示红钉在 main 上为红,消融一次转红后已还原。59 道派生门禁全过,pnpm lint 与两包测试、typecheck 全绿。
  3. 门禁 check-runs(钉 head fe3d2a1e):全部 success 或 skipped,0 失败(落地前席位复读)。
  4. 接受的偏差:席位修订 6662593558 加了业务单元时区帮助,已在本 PR 内交付。validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017 键面元测试把 requires 列为从 stack 读取的键,只改测试清单,接受。
  5. 范围外去向:第 3 行(编辑组织页的时区字段)只做实测:删字段是收窄,属于所属车道按 ADR-0049 走的移除,dev 推荐 D(保持)。第 4 行(单组织时隐藏上级组织和排序)是 needs_decision:字段级 visibleWhen 和 requiresFeature 都不支持,dev 推荐 C 改帮助文字,A 另立卡。两行都交接给席位 2。Skills-docs: none 成立。changeset patch,Metadata-protocol: none。
  6. 落地条款:check-governed-merges --pr 30622 结果 NOT governed,150 行,非 Tier H。全部 check 绿后 ready + auto-merge。合并后席位在 Setup and tools: say which organization features need the enterprise edition, and remove fields that do nothing #30210 写落地(卡保持开,留给第 3、4 行),并结线程。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 18:45
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 18:45
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 778aedf Nov 5, 2026
64 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-pkx5by branch November 5, 2026 19:10
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 Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant