Repository navigation
Description
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
The maintainer: 「全部立,继续测试验证」 (2026-11-04), on the seat's evaluation of which metadata types should support override.
The question
Today only a package can declare an assignment rule, so routing a new lead or case requires a package release. Should an organization be able to write its own routing rules in Setup?
Governing text: docs/adr/0142-ownership-queue-membership-claim-and-assignment-rules.md D5, and packages/spec/src/kernel/metadata-plugin.zod.ts:1301-1306: "declared only: organization-authored routing is deferred (merge question 11)", and "customized by a clone under a new name, never by an overlay".
Measured
Measured on main 63b86b385d.
| Type | Can an organization create one? | How |
|---|---|---|
| Sharing rule | Yes | A sys_sharing_rule record, in Setup's navigation. A create with empty criteria is refused, correctly, and the seat did not complete a valid one |
| Queue | Yes | A sys_queue record, in Setup's navigation; created 201 |
| Assignment rule | No | PUT /api/v1/meta/assignment_rule/... answers 403 NOT_CREATABLE; there is no sys_assignment_rule object |
The seat first reported all three as closed, and that was wrong: only assignment rules are. The same D5 note says a rule is disabled by the ledger or customized "by a clone under a new name", but a clone cannot be created at runtime either, so the customer's only route today is a package.
Options
- A. Organization-authored rules in Setup, as records (
sys_assignment_rule), the way sharing rules and queues already work. Package-declared rules keep working, with a stated order between the two. - B. Open
assignment_ruleto runtime creation in the.custompackage, so it is authored in Studio, drafted and published like other metadata. - C. Keep package-only. Routing changes go through a package release or code.
Recommendation: A
四轴分析
实际业务需求。 线索按地区分给谁、工单按产品进哪个队列,每家客户都不一样,还会随人员变动常改。原厂没法预置,这正是运营要自己配的东西。频率上,组织调整时就要改。今天的绕行办法是写钩子或发包(ADR-0142 记录 HotCRM 为此写了 587 行临时代码),运营做不到。开放卡片没有人提过,这只说明没有反馈数据,不是反面证据。
项目长远合理性。 A 和共享规则、队列同一个模型:包声明默认,组织用记录配置自己的,ADR-0142 背景部分也写着分配规则「declared by the package or the organization」,只是 D5 把组织这一半推迟了。B 能走草稿和发布,但路由规则会点名具体的人和队列,这些是组织数据,放进可导出的包并不合适。C 把一项运营能力永远留在开发手里。
防 AI 写错。 A 的 criteria 和 load 沿用现有 CEL 校验,发布时报错的那一套要在保存记录时同样响亮,不能静默匹配全部。这是 A 必须付的成本。B 的校验最现成。C 没有新增风险。
创业阶段不扩散。 A 要新增一个系统对象、一个设置页和两种规则的先后顺序,规模中等,不算小。可以先只做「条件 → 队列或人」,轮询和最少负载两种策略复用现有实现。如果当前重点仍在 Studio 定制,排在 #30059 之后完全合理。
| 棱 | A | B | C |
|---|---|---|---|
| 业务拉动 | 强 | 中 | 弱 |
| 长远一致 | 强 | 中 | 弱 |
| 防错 | 中 | 强 | 强 |
| 不扩散 | 中 | 中 | 强 |
Not measured
How a package rule and an organization rule should order at runtime. What the engine does when a rule's target has been removed. These are for the execution card, after the decision.
Re-check
git grep -n "organization-authored routing is deferred" origin/main -- packages/spec/src/kernel/metadata-plugin.zod.ts
Activity
objectstack-fleet commentedon Nov 4, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
The maintainer, 2026-11-04: 「30079这种不是应该在元数据应用中自己去开发自定义操作吗?你的意见是批量换负责人放到内核吗?其他全部同意」.
Ruling: option A. An organization authors its own assignment rules in Setup, as records, the same model as sharing rules and queues. Order it after #30059. Package-declared rules keep working, and the execution card states the order between the two. This updates the deferral in ADR-0142 D5 (merge question 11), so the ADR's note changes with it.
needs-user-decisionis removed; triage routes this card.objectstack-fleet commentedon Nov 4, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 kept ·
domain:server· 2026-11-04T13:03Z⛔ Not a claim.
domain:server(the records and the engine, platform first). The Setup page is thedomain:uihalf. The ADR-0142 D5 update isdomain:skillstext that the PR names.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL
Ruling confirmed: letter A (Ruling-ref 6631036940) · the execution parameters below were presented and agreed · executed by
domain:server#2.Maintainer: 「同意」 to the presentation, in the decision thread, 2026-11-05T21:34Z (project files
decisions/30066-组织自配分配规则-呈批-v2.md).What A binds. An organization authors its own assignment rules in Setup as
sys_assignment_rulerecords, on the same two-source model assys_sharing_ruleandsys_queue.packages/objectql/src/assignment-rules.ts~L239) and the activation ledgersys_metadata_activationis one row per deployment with no organization column (route-ledger.ts~L621), so an organization cannot switch a package rule off for itself alone; package-first would let it never win. A pin holds the order.warn, the insert is never refused). Today the engine writes the owner without resolving it (~L263).sys_assignment_rulecarries a non-nullorganization_idbehind the organization wall; the comment atmetadata-plugin.zod.ts~L1301–1306 changes with the code PR.docs/adr(Tier H): its own small PR for the maintainer to merge by hand, never bundled with the code PR.domain:uicard.State change: none yet; the seat dispatches the server execution from this card.
objectstack-fleet commentedon Nov 5, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Claim: PM loop round 3 · 2026-11-05T23:33Z
Session:
session_016WV4cytLmJxfAmgPVjUbbWAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch: the card thread's designated
claude/project-thread-*branch, named in a claim amendment once the thread reports itWorktree:
objectstack-issue-30066Domain:
domain:serverSeat:
domain:server#1File surface (to be narrowed by amendment after the thread's first reading):
packages/objectql/src/assignment-rules.ts,engine-insert.ts(the assignment call site only), the newsys_assignment_ruleobject inpackages/platform-objects/src/**and its registration, the save-time validation for organization rules (user target, name namespace, CEL compile), their tests, one changeset. Apackages/specroster line or themetadata-plugin.zod.ts~L1301–1306 comment only as the execution parameters (6670178869) item 6 requires, named in an amendment before editing. ⛔ui/**(the Setup page is a separatedomain:uicard); ⛔docs/adr/**(item 7: ADR-0142 D5 rewording is its own Tier H PR)Container & model:
L,mode:thread,model: default,effort: xhighClause-②: yes (widening)
Metadata-protocol: added
Responsibility: assignment rules in
packages/objectql| package-declared rules only | an organization admin who cannot author their own assignment rules in SetupThread-read: 6670178869
Premise: holds per ruling A (6631036940) with the agreed execution parameters 6670178869 (maintainer 「同意」 2026-11-05T21:34Z). #30059, which A orders this after, is closed
Serial constraints cleared: #30059 closed. Open PR file lists to be re-read by the thread before its first push.
objectstack-fleet commentedon Nov 5, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
席位核对 · Amendment · 2026-11-05T23:41Z · claim 6672306838
Branch:
claude/project-thread-kl2al6(the card thread's harness-pinned branch, objectstack-ai/objectstack; platform exception toclaude/issue-<n>-<slug>), base6ef7f583.Dev session:
session_016WV4d14C8K4gMehVb6jBvAFile surface (replaces the claim's, after the thread's first reading):
packages/objectql/src/assignment-rules.ts,packages/objectql/src/engine.ts(the insert-time assignment call site ~L6205 only;engine-insert.tsis untouched), their tests,federated-injected-column-readers.test.ts(reader ledger).packages/plugins/plugin-sharing/src/objects/sys-assignment-rule.object.ts(new; the object lives besidesys_queue/sys_queue_member/sys_assignment_cursorper ADR-0029 K2, not inplatform-objects),objects/index.ts,sharing-plugin.ts(manifest, hook binding, and a Setup nav entry besidenav_queuespointing at the generic record list; any purpose-built Setup page stays thedomain:uicard's),assignment-rule-hooks.ts(new),objects/sys-sharing-rule.by-object.test.ts(one roster row), new tests, andtranslations/for the new object in all eleven platform languages (ruling F, [Decision] One locale list, one completeness rule — the long-term i18n regime across Console, platform metadata, cloud and mobile #29349), written after i18n phase 3 (platform metadata and built-in emails): extraction and bundle checks read PLATFORM_LOCALES; backfill the seven missing locales #30512's plugin-sharing PR lands (serial, same files).packages/cli/src/utils/organization-ownership-inventory.ts(one row),scripts/platform-object-tenancy-census.json(regenerated).packages/spec/**: three hunks, pendingdomain:specconsent (asked on mailbox pm:mailbox — domain:spec#3 · os-sales #30558): themetadata-plugin.zod.ts~L1301–1306 comment (execution parameters item 6), the one roster line'sys_assignment_rule'insystem/constants/platform-object-names.ts, and theautomation/assignment-rule.zod.ts~L131–132 TSDoc with its regenerated references page. Until consent lands, nothing underpackages/spec/**is pushed.Expected
Clause-②: yes (widening),Metadata-protocol: added; one PR, estimated 1500–2000 lines. ⛔docs/adr/**in the code PR (item 7); ⛔ui/**.objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
席位核对 · Amendment · 2026-11-06T00:29Z · claim 6672306838
domain:specconsent received (6672529419 on mailbox #30558). The threepackages/spechunks are now a declared cross-lane change on this card, under those terms:kernel/metadata-plugin.zod.ts≈:1301–1305: only the[ADR-0142 D5]comment's "declared only: organization-authored routing is deferred" clause. Theassignment_ruleentry'sdescriptionand flags stay. Moving one is a separate spec card: stop and report.system/constants/platform-object-names.ts: one line'sys_assignment_rule'underPLATFORM_OBJECTS_BY_PACKAGE['plugin-sharing'], between'sys_assignment_cursor'and'sys_queue', in the same commit as the object, its census row and its ownership-inventory row.automation/assignment-rule.zod.ts≈:131–132: theAssignmentRuleSchemadocblock sentence, rewritten to state that package-declared rules andsys_assignment_rulerecords combine in the evaluation order the code implements. ⛔ No key, shape or.describe()change. Generated output is regenerated, never hand-edited.Terms:
Clause-②: yes (widening),Metadata-protocol: added, a@objectstack/specminor changeset with aSkills-docs:line (grepskills/**for the "deferred" / "declared only" reading and name any hit), and this seat's contract review atCONTRACT_REVIEW_TIERcovering all three hunks plus the untouchedassignment_ruleregistry entry. Nothing else underpackages/spec.objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4d14C8K4gMehVb6jBvA
os-dev-report
{ "issue": 30066, "status": "blocked", "branch": "claude/project-thread-kl2al6", "pr": "https://github.com/objectstack-ai/objectstack/pull/30760", "session": "session_016WV4d14C8K4gMehVb6jBvA", "premise_still_valid": true, "summary": "An organization now authors its own assignment rules as sys_assignment_rule rows (new object in plugin-sharing, Setup nav beside Queues, one rule with one entry per row, organization_id required). The objectql seam evaluates an object's organization rules first (sort_order, then name) and the declared rules as the fallback, first match; a user target from either source owns the row only while the user is an active member (sys_member row, no ban in force), otherwise the row lands ownerless and the lapse warns once; an organization whose rules cannot be read routes by no rule. A save guard refuses, by field, a declared or non-snake_case name, an unknown or queue-less object, any entry shape AssignmentRuleSchema refuses, a criteria/load the engine's CEL paths cannot compile, a non-member or banned user target, and a row with no organization; a boot report logs shadowed rows at error. Blocked on the seat for one file outside the surface: check:empty-state (exit 1) needs a `scope` entry for sys_assignment_rule.criteria in packages/spec/scripts/liveness/empty-state-registry.mts, drafted locally and proven (gate then prints 'all classified'). The three spec#3-consented packages/spec edits are in (roster line, the item-6 comment sentence, the AssignmentRuleSchema docblock); translations wait for #30512, and the ADR-0142 D5 rewording goes in its own docs/adr PR.", "tests": "At head ab98c3b8. pnpm --filter @objectstack/objectql test: Test Files 556 passed (556), Tests 11086 passed (11086), lock VERDICT command-exit 0. pnpm --filter @objectstack/plugin-sharing test: Test Files 66 passed (66), Tests 1421 passed (1421), VERDICT command-exit 0. cli unit src/utils/organization-ownership-inventory.test.ts: 26 passed (26). typecheck objectql + plugin-sharing + cli: VERDICT command-exit 0. spec src/system/constants/platform-object-names.test.ts (repo project): 1 failed / 13 passed without the roster line, 14/14 with it. pnpm --filter @objectstack/spec test: Test Files 817 passed (817), Tests 25792 passed | 1 todo. Ablations via scripts/ablation-replace.mjs, suites import src (no dist hop): (1) apply() own-first order swapped -> objectql ownership-queue 3 failed / 38 passed; (2) routeToUser 'member' check widened to !== 'unconfirmable' -> 3 failed / 38 passed; (3) guard declared-name branch short-circuited -> plugin-sharing ownership-queue 2 failed / 65 passed. Each: mutation landed (anchor 1 -> 0, blob changed), restore proven (blob == HEAD, git diff HEAD empty).", "gates": "Final derivation (head 796614dc, same 99 at ab98c3b8): 99 families, 98 exit 0, `pnpm --filter @objectstack/spec run check:empty-state` exit 1 (UNCLASSIFIED permissive empty state at sys-assignment-rule.object.ts criteria; registry entry outside the surface, drafted locally). dispatch-gates --ran: '99 derived famil(ies) accounted for — 99 run, 0 NOT-MEASURED (a DERIVED zero)'. Earlier pre-spec derivation: 77; all run with exit codes captured before any pipe. First pass: 72 exit 0, 5 exit 3 (check-plugin-teardown-shape --self-test: pinned fixture commit outside the shallow clone; check:i18n, check:i18n-coverage, check:i18n-walk-parity, check:dual-build-cjs-loads: unbuilt dist). Fetched 621a4876 at depth 1 and built each gate's named closure; all five re-ran exit 0. dispatch-gates --ran: '77 derived famil(ies) accounted for — 77 run, 0 NOT-MEASURED (a DERIVED zero)'. check-commit-card-trailers --range origin/main..HEAD: 5 messages clean; check-nul-bytes exit 0. eslint --no-inline-config over changed .ts: exit 0. pnpm lint (repo-wide eslint . --no-inline-config): EXIT=0 on the pre-spec tree and at 796614dc; eslint on the one file ab98c3b8 changes: exit 0. CI check-runs: in_progress at report time (not polled).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm (Clause-②: yes (widening))", "line_budget": "18 files, +1282 / -36; under the 3000-line split line, one PR", "files_changed": [ ".changeset/30066-organization-assignment-rules.md", "packages/cli/src/utils/organization-ownership-inventory.ts", "packages/objectql/src/assignment-rules.ts", "packages/objectql/src/engine.ts", "packages/objectql/src/federated-injected-column-readers.test.ts", "packages/objectql/src/index.ts", "packages/objectql/src/ownership-queue.test.ts", "packages/plugins/plugin-sharing/src/assignment-rule-hooks.ts", "packages/plugins/plugin-sharing/src/index.ts", "packages/plugins/plugin-sharing/src/objects/index.ts", "packages/plugins/plugin-sharing/src/objects/sys-assignment-rule.object.ts", "packages/plugins/plugin-sharing/src/objects/sys-sharing-rule.by-object.test.ts", "packages/plugins/plugin-sharing/src/ownership-queue.test.ts", "packages/plugins/plugin-sharing/src/sharing-plugin.ts", "packages/spec/src/automation/assignment-rule.zod.ts", "packages/spec/src/kernel/metadata-plugin.zod.ts", "packages/spec/src/system/constants/platform-object-names.ts", "scripts/platform-object-tenancy-census.json" ], "deviations": [ "Object placed in packages/plugins/plugin-sharing (beside sys_queue / sys_sharing_rule) instead of packages/platform-objects, and the call site changed is engine.ts (evaluator.mayRoute) with engine-insert.ts untouched: both accepted by the seat's amendment 6672490095.", "packages/spec edits landed after domain:spec#3 consent (6672529419, claim amendment 6673262187), within its limits: only the 'declared only… deferred' sentence of the metadata-plugin.zod.ts comment (description and flags untouched), the roster line between sys_assignment_cursor and sys_queue, and the AssignmentRuleSchema docblock (no key/shape/describe change; gen:schema + gen:docs regenerate with no diff).", "Commit split: spec#3 asked for the roster line in the same commit as the object; the object is in already-pushed 4b6296e8, so the line is in the next commit d86cdc3e with the census and inventory rows, both merging in this PR. Accepted by the seat (relay 2026-11-06), no history rewrite.", "Translations (11 platform languages, ruling F) not in this push: they wait for #30512's sharing PR to merge, then rebase (seat's direction). check:i18n is green today because plugin-sharing's i18n-extract.config.ts lists its objects explicitly and does not yet name SysAssignmentRule.", "Two checks beyond the ruling, added as mechanism and named in Acceptance notes: the save guard refuses an object that does not exist or has no queue column, and a boot report logs at error a stored row a later-installed package's rule now shadows.", "PR body opens with the harness attribution comment and the Requested-by line, then Fixes #30066 (os-dev: the harness's first-line attribution is kept as is and only reported).", "pnpm lint (repo-wide eslint) was run because the standing lines ask for it, although os-dev.md calls the repo-wide scan CI's run; reported here, not a conflict of substance.", "This session is subscribed to #30760's activity (the harness default for a PR it opens); per os-dev it does not wait on or poll CI, and landing stays the seat's." ], "mcp_calls": "5 — all reads: issue_read get, issue_read get_comments, pull_request_read get_files x2, pull_request_read get (#30760 draft state); no write tool", "api_writes": "2 — relay (fleet-write): pr_create #30760 (draft, no assignees, no labels; read-back 14826/14826 bytes identical, run 42142333356); comment on #30066 via post-stamped.mjs (this report). git push (2 pushes this card) is not a REST write.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: seat (Skills-docs: follow-up owed) · content/docs/automation/queues-and-assignment-rules.mdx does not teach an organization's own rules, their order before declared ones, or the active-member rule for a user target; skills/objectstack-automation/SKILL.md's `assignTo: { type: 'user' }` line likewise · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
席位核对 · Amendment · 2026-11-06T01:27Z · claim 6672306838
File surface adds
packages/spec/scripts/liveness/empty-state-registry.mts, one newscopeentry forsys_assignment_rule.criteriaafter the declared rule'scriteriaentry, as hunk 4 of the declared cross-lane change.domain:spec#3consented in 6674042568 under the terms of 6672529419. Why:check:empty-stateis red on PR #30760 headab98c3b8, because the object'scriteriadescription states a real permissive empty state (an empty condition is the catch-all). The description stays and the empty state is declared. No changeset line is owed for this hunk. The seat's contract review covers all four hunks.Everything else in the claim and its amendments stands.
objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
席位核对 · Amendment · 2026-11-06T01:34Z · claim 6672306838
File surface adds
packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.ts, one line each: the Setup nav label fornav_assignment_rules, undernav_queue_members. Why: on PR #30760, CI "Type Check · consumer gates" is red atpnpm check:app-nav-i18n, because the new nav item has no label in the four locales the Setup app ships today. The nav id exists only after the runtime merge, so neither the derived gate list norcheck:i18nsees it locally. These four lines ride this PR. The labels in the other seven platform locales come with #30512, as already planned.Everything else in the claim and its amendments stands.
objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
ACCEPT → PR #30760 comment 6675131618 · head
ac7e06d9· review of record 6675095900 (CONTRACT_REVIEW_TIER PASS) · ready + auto-merge set 2026-11-06T02:22Z