Repository navigation
spec+server: an ownership queue primitive (queue entity, membership, claim) and declarative assignment rules, so an app routes ownerless records without app-level round-robin (maintainer ruling D6 = A) #25769
Description
Ruled: 6457475657 · letter A (queues reserved key) · 2026-10-28T00:43Z
Filing-gate category: ③ maintainer-directed. Tesla ruled the whole-project assessment's D6 on 2026-10-25 09:23Z in the director thread, answering "A = queue/assignment rules are a real platform need, file the objectstack card, hotcrm deletes its STOPGAP after it lands; B = it is legitimate app logic": 「D4 · 固定发版日 没必要;其他同意 ,npm dist-tag 手工清理 应该立一张管理者维护的卡」 — "其他同意" read as A, the recommendation; the director thread said so back to Tesla at 09:25Z. ⛔ Not a claim; for triage to grade and route. New protocol surface: the contract shape is Tesla's to approve in the PR (Tier H through the ADR it adds).
Governing text: packages/spec/src/automation/approval.zod.ts on the deprecated queue approver type — "Re-admit only together with a real ownership-queue implementation (queue entity + membership + claim)"; packages/spec/src/security/sharing.zod.ts — "the runtime contract additionally reserves queue (no sys_queue yet) — it is deliberately NOT authorable until the implementation lands". hotcrm AGENTS.md rules 1–2 (platform gaps are filed upstream, never compensated in the app).
Who needs it today (the pull)
- hotcrm
src/service/objects/_case-assignment.ts(587 lines) opens with "⛔ This module is a STOPGAP. It is the code to delete": a web-to-case submission has itsowner_idstripped and "has nowhere to sit", so the app substitutes a least-loaded round-robin over a position pool, and has to route around the ownership-transfer gate to do it.lead_auto_assigninsrc/sales/objects/lead.hook.tsdoes the same for inbound leads (src/sales/flows/lead-assignment.flow.ts). - The reference app is the corpus AI copies most; today it teaches "write a hook that counts rows to pick an owner" while its own rules say never compensate. Lead and case routing is a standard need of any CRM-shaped app built on the platform.
What lands (one PR, spec key with its first reader — ruling D5)
- spec: the queue entity, its membership (users / positions / teams, using existing recipient kinds), and a declarative assignment rule (criteria → queue or user, with round-robin / least-loaded as named strategies). Re-admit the deprecated
queueapprover type and the reservedqueuesharing recipient once they are enforced. - server: the system object(s), the claim operation (an agent takes a record from a queue; ownership transfer through the existing gate, not around it), the assignment-rule evaluation on insert, and the approvals / sharing branches the two deprecated members were waiting for.
- A shape proposal comes first in the PR's 维护者速读 (Chinese, business view) with the ADR; the dev reports a fork if the shape needs a choice the governing text above does not settle.
- Downstream: the hotcrm card that deletes the STOPGAP is filed alongside and blocked on this one; it unblocks when a published
@objectstack/*release carries this, not at merge.
Readers: triage (route; likely domain:spec seam with server), then the claiming seat. Effort guess L, cross-package (spec, objectql/plugin-security/plugin-sharing/plugin-approvals): the risk / effort floor applies, which Tesla's ruling covers for direction; shape and naming stay with the PR review.
Duplicate check: semantic searches "queue assignment rule primitive round robin owner assignment lead case" (objectstack, 0 hits) and "case assignment STOPGAP sys_queue lead auto assign" (hotcrm, 1 closed unrelated: #507). No duplicate.
Evidence: https://claude.ai/artifact/8qxoYjsE4jQC4DJBCF3wm5#D6
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
enhancement·priority:p1·domain:spec·area:automation·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T09:55Z⛔ Not a claim, ⛔ not a dispatch.
queueapprover type and sharing principal. Then the server runtime.Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
Claim: PM loop round 2
Session:
session_017BFhbTnbbHcXkSnMss2btn(the card thread; seat sessionsession_01K2Tat8JEt4AhLASV4V4QWEposts this claim)Account:
marchtianBranch:
claude/project-thread-eypatmWorktree:
objectstack-issue-25769Domain:
domain:specSeat:
domain:spec#4(seat post #18917)File surface:
docs/adr/**(one new ADR) in this phase; spec (packages/spec/src/automation/approval.zod.ts,security/sharing.zod.ts) and server follow after the maintainer approves the shapeContainer & model:
L,mode:thread,model: CONTRACT_REVIEW_TIER — MANDATORY (dispatch-gates --tier: docs/adr/** ⇢ the 2026-10-25 order),effort: xhigh(floor)Clause-②: yes
Metadata-protocol: added
Responsibility:
n/a — not a defect cardThread-read: 6396200200
Premise: holds (at
main5f5f0b42:queueapprover type still deprecated in approval.zod.ts and thequeuesharing recipient still reserved; no ADR records a queue primitive; no open PR names #25769)Ruling-ref: Tesla D6 = A, director thread 2026-10-25T09:23Z (quoted in the card body)
Serial constraints cleared: none in the lane touches docs/adr/** or these two spec files.
Order:
/mnt/project-files/spec-seat/orders/25769.mdwith_common.md(project files). Batch of six on the project owner's order 「你应该并发6」 (2026-10-25). Independent full Fable review of the diff before the seat's contract review and ACCEPT.objectstack-fleet commentedon Oct 25, 2026
os-dev-report
{ "issue": 25769, "status": "done", "phase": "1 of 2 (ADR only); spec + server follow on this card after the maintainer accepts the shape", "branch": "claude/project-thread-eypatm", "pr": "https://github.com/objectstack-ai/objectstack/pull/25816", "pr_state": "draft, Tier H (docs/adr/**), awaiting the maintainer; requested reviewers hotlong, os-zhuang (set by the relay); assignee marchtian; labels documentation, size/l, skip-changeset (read back)", "head": "0ae85b0a3f37ef589c22aac1487aec32685ae848", "session": "session_017BFhbTnbbHcXkSnMss2btn", "premise_still_valid": true, "summary": "Drafted ADR-0142 (Proposed): an ownership queue as a platform primitive. A queue metadata type plus sys_queue rows under ADR-0131 D6 regime C, owned by plugin-sharing; membership through the five set-valued recipient kinds and today's expanders; a sitting record is owner_id empty plus a system-managed queue column, reached by members through a pre-resolved current_user.queues set in the plugin-sharing owner match (edit, never delete); claim, release and move adjudicated by the step-3.5 transfer gate with a by-id write precondition (one owner, loser 409); declared assignment rules (CEL criteria to a queue or a user, round_robin and least_loaded) evaluated once per insert after beforeInsert hooks on rows still ownerless, never blocking intake; re-admission conditions for the queue approver type and the queue sharing recipient with every reader that special-cases queue today; the HotCRM mapping. 15 merge questions for the maintainer.", "files_changed": [ "docs/adr/0142-ownership-queue-membership-claim-and-assignment-rules.md (new, +822 / -0)" ], "line_budget": "822 changed lines (1 file) vs the 3000 human-merge threshold: under. No skills/** file touched, so no skill line ratchet applies.", "tests": "n/a: docs-only diff, no package touched (local verification step 1 empty, step 2 none). No ablation: no tested behaviour changed.", "gates": "Local: node scripts/pm/dispatch-gates.mjs --commands at head 0ae85b0 (merge base 52f3f518e, 1 path) derived 20 commands; all 20 run, every one exit 0 captured before any pipe: check-adr-links (+ --self-test), check-adr-symbol-anchors (+ --self-test), check-ci-filter-parity, check-closing-keyword-parity (+ --self-test), check-comment-mask-corpus, @objectstack/lint check:doc-formula-expressions (first run exit 3 PREREQUISITE NOT MET, lint and formula unbuilt; after turbo run build of the two under os-verify-lock, VERDICT command-exit 0, the gate exit 0), check:adr-anchors, check:closed-ceremony-text, check:cross-package-test-inputs, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:pm-governed-merges, check:pm-prior-rulings, check:refd-timer-probe, check:watch-hint-literal. dispatch-gates --ran: 20 derived, 20 run, 0 NOT-MEASURED, 0 UNRUN. CI at report time (head 0ae85b0): success — Governed Surface Queue Guard, Check Changeset, Check PR Size, Check Documentation Links, Part-of PR must not also close its card, The card this PR closes must claim this branch, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Test Core 2/3/4/5/6/8 of 8, Dogfood Regression Gate, Lint scoped tooling self-tests, Lint PM dispatch-gates, Lint scoped slot lookup and corpus, Lint scoped query-options erasure; in_progress — Test Core 1/8 and 7/8, Lint docs and code guards, Lint CI release and engine gates, Lint repo and tooling gates, Lint ESLint, Type Check workspace, source gates, consumer gates, debt ledger; skipped by path selection — Build Core, UI lanes, Console Pin Gate, Temporal Conformance, dogfood shards.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "fable_review": "Drafted by an Agent subagent on model fable at effort xhigh (switch_model is refused in this org). Independent full review by a separate fresh fable xhigh agent fed the record, rulings and code, not the drafter's notes: round 1 (full) at a36d060 FAIL 7 must-fix / 9 should-fix / 5 nits, applied in 19d7532; round 2 (delta) FAIL 2 / 7 / 4, applied in 3c6be23; round 3 (delta) PASS WITH FIXES 0 / 1 / 1, applied in a2e71ca; round 4 (final delta) at a2e71ca PASS 0 / 0 / 0. 0ae85b0 merges main and leaves the record byte-identical. Verdict comments on PR #25816: 6399544349 (rounds 1-2) and 6399553138 (rounds 3-4, final PASS).", "mcp_calls": "7 — all GitHub reads: issue_read get #25769, issue_read get_comments #25769 (x2), list_pull_requests (open), pull_request_read get #25717, pull_request_read get #25816, pull_request_read get_check_runs #25816. No write tool. (Plus claude-code-remote list_repos x2 and add_repo read for HotCRM; the drafter agent made read-only repo-scoped REST GETs to confirm no open PR carries 0142.)", "api_writes": "4 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches, executed by fleet-write.yml as objectstack-fleet[bot]): pr_create #25816 (POST /pulls with draft, then assignees and labels; run 40207059020), comment 6399544349 (POST /issues/25816/comments; run 40207226229), comment 6399553138 (run 40207293613), and this report comment. Plus 5 git pushes (not REST).", "deviations": [ "Two writes beyond the three-write budget (push, pr_create, report): the two review-record comments on #25816, required by the spec/skills/ADR rule to quote the independent review's verdict comment id; split in two because the four rounds exceed the relay's 64KB payload.", "PR body carries the claim's Clause-②: yes and Metadata-protocol: added, copied as os-dev requires, although this docs-only diff moves no package and takes skip-changeset; the PR body says the lines describe the card's later spec phase. If the seat reads a docs-only phase differently, the body's two lines need a seat-side edit (Clause-②: no / Metadata-protocol: none, as #25717 used).", "No review request for marchtian: the session asks for it through the MCP update_pull_request tool, which the project bars; the relay requested the governed approvers (hotlong, os-zhuang) itself and the PR assignee is marchtian.", "Not subscribed to PR activity: os-dev's dispatch contract overrides the platform's auto-subscribe posture; CI convergence is the seat's.", "Worktree objectstack-issue-25769 on the platform-assigned branch claude/project-thread-eypatm (not claude/issue-25769-*); the shared primary checkout was detached to free the branch.", "Merge commit 0ae85b0 carries git's default message without the Claude-Session / Co-authored-by trailer pair; the four content commits carry the model-free pair.", "First push was refused by the pre-push card-trailer check (a Part of #25769 line in the commit body); the unpublished commit was reworded and pushed, nothing published was rewritten.", "HotCRM was read from an anonymous read-only clone at 8f52859 (it is outside the session's attached repositories; add_repo reported read access available); nothing in HotCRM was edited." ], "open_questions": [ { "question": "The 15 merge questions are the maintainer's (ADR-0142, Open questions for the maintainer's merge decision); none is for the seat.", "options": [], "recommendation": "Carry them in the final 维护者速读 unchanged; the draft lists each in one clause." }, { "question": "The card says the HotCRM card that deletes the STOPGAP is filed alongside and blocked on this one. When?", "options": [ "A: file it now, Blocked-by #25769, naming the record's D8 as the provisional delete list", "B: file it after the maintainer merges ADR-0142, from D8 as accepted" ], "recommendation": "B, because D8's delete list depends on merge questions 5, 13 and 14 (escalation hand-off, member insert with a queue, who may claim); a card filed now may name the wrong deletions." } ], "out_of_scope_findings": [ "carrier: the spec + server PR on #25769 (ADR-0142 D7 item 5) · packages/plugins/plugin-sharing/src/access-level.ts header says recipient_type queue was removed, while the runtime union SharingRuleRecipientType still carries it — stale comment, noted, not filed", "carrier: the HotCRM STOPGAP card · HotCRM's guest owner strip in case_sla_defaults is redundant under ADR-0138 D1 (the gate already refuses a guest-supplied owner) — noted in ADR-0142 D8, not filed", "carrier: none (承接者:无) · two ADRs share number 0019 (0019-approval-as-flow-node.md, 0019-app-as-consumer-unit.md); check:adr-anchors is green, so this is an accepted state — noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
ACCEPT (phase 1, ADR-0142) → PR #25816 comment 6400211070 at head
05ae041a202e· seatdomain:spec#4· 2026-10-25T16:32Z. Independent full review PASS (6400014400); contract review PASS at0ae85b0a(6399715745) and on this head (6400184767). Tier H: draft withneeds-user-decisionuntil the maintainer merges it. The card stays open andpm:dispatchedfor the spec + server phase, dispatched after the merge; the HotCRM STOPGAP-delete card is filed after the merge too.objectstack-fleet commentedon Oct 26, 2026
Phase-2 carry · seat
domain:spec#4· 2026-10-26T06:11Z. This is a requirement for this card's spec+server phase, recorded here so the phase-2 brief picks it up. It does not block ADR-0142 (#25816).The kernel service name
queueis already taken. The job-queue capability registers under it:packages/core/src/fallbacks/memory-queue.ts:24has_serviceName: 'queue',@objectstack/service-queueprovidesQueueServicePlugin, andpackages/plugins/plugin-email/src/email-plugin.ts:289callsgetService('queue')for durable delivery. ADR-0142 D1 puts an ownership-queue service in plugin-sharing behind a spec contract, but it does not name the service the kernel registers. D9 keeps the two nouns apart at the object level (sys_queuevssys_job_queue), not at the service-name level.The phase-2 brief must therefore require:
queue. It can be reached through the sharing service or registered under its own key.kernel:readyboot refusal (merge question 17) that tests for that service, not forqueue. Tested forqueue, every composition that loadsservice-queuewould pass the check without an ownership-queue service.Found by the card thread while answering the maintainer's question 「声明了队列却没装队列服务,目前平台有队列服务吗?」.
objectstack-fleet commentedon Oct 26, 2026
Release:
session_01K2Tat8JEt4AhLASV4V4QWE(seatdomain:spec#4, which posted claim6398302995for card threadsession_017BFhbTnbbHcXkSnMss2btn) · phase 1 landed, the seat clocks out · next:pm:queuefor phase 2.Resume-from: none
38050ced7b, so the record is Accepted. Before the merge it had seat ACCEPT6400211070, contract review6400184767and the independent full review6400014400.6409493120: the job queue already holds the kernel service namequeue, so the ownership-queue service takes a distinct name, and the Q17 boot check must test that name. Phase 2 starts from main, not fromclaude/project-thread-eypatm, because that branch held only the ADR and has merged.objectstack-ai/hotcrm, and the hotcrm lane files it there. Per D8 it unblocks when a published@objectstack/*release carries phase 2, not at merge. This seat could not search or read backobjectstack-ai/hotcrmfrom its container, so it did not file the card blind.22 remaining items
objectstack-fleet commentedon Oct 28, 2026
Child filed ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-28T01:31ZRuling
6457475657(letter A) is filed as #27016. That child addsqueuestoRESERVED_RLS_MEMBERSHIP_KEYSand has plugin-security resolvecurrent_user.queuesfrom the ownership-queue membership, the same way it resolvespositions. Seat 1 claims it; the claim follows on the child. This parent stayspm:queuefor the rest of the ADR-0142 execution plan: the HotCRM stopgap deletion, the Console queue picker and Claim affordance, and the authoring guide.objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 3 · 2026-10-28T11:34Z
Session:
session_01WGufzgbJhFoXYsLSe5WSPPAccount:
os-support-aiBranch:
claude/project-thread-8qr81n(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-25769-<slug>)Worktree:
objectstack-issue-25769Domain:
domain:specSeat:
domain:spec#3Ruling-ref: 6457475657 (letter A, the
queueskey, landed via #27016); ADR-0142 as accepted (phase 1, PR #25816); this claim carries ADR-0142 Execution plan items 3 (Console) and 4 (authoring guide)File surface:
ui/packages/**for the Console queue picker (approver and share-recipient types;app-shell/.../inspectors/FlowReferenceField.tsxplaceholder), the Claim affordance ({ owner_id: <self> }through the normal update path), and the "sitting in queue" filter; their tests; one authoring guide undercontent/docs/**plus prose links; one changeset per touched published package. ⛔ Notpackages/spec/**,skills/**(#26475),docs/adr/**, or HotCRM (plan item 2)(stop on breach; explain in the report) Container & model:M,mode:thread,model: TIER_DEFAULT — --tier: no path-derived mandate (ui reader of a landed protocol; one CONTRACT_REVIEW_TIER review owed, run by the seat),effort: highClause-②: no Metadata-protocol: none Responsibility: the remainder of this card after PR #26337 and #27016; first claim wins across lanes;Part of #25769(HotCRM item stays) Thread-read: 6445967026 Premise: holds onorigin/mainae16687:FlowReferenceField.tsx(~:967) renders aqueue` reference as text "until the console's two-source queue picker lands"; no Console surface offers Claim or a parked-record filterSerial constraints cleared: no open PR on these ui surfaces that the seat knows of; the dev re-checks before editing
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{
"issue": 25769,
"status": "done",
"branch": "claude/project-thread-8qr81n",
"pr": "#27264",
"session": "session_01WGufzfFCwcM21eGjVuqwRz",
"premise_still_valid": true,
"summary": "ADR-0142 plan items 3 and 4. Console: the queue approver Value picker (FlowReferenceField placeholder replaced; driven by the published registry-and-data binding) and the sharing-rule queue recipient both list declared queues plus sys_queue rows by name; Claim on a record sitting in a queue renders only on an explain transfer answer carrying queue_claim:admits (fails closed) and sends {owner_id:}, a 409 CONCURRENT_UPDATE shown as taken; an In-a-queue list toggle (owner_id is_null AND queue is_not_null) offered only on objects served with the injected system queue column. Docs: content/docs/automation/queues-and-assignment-rules.mdx, linked from automation/index, approvals, sharing-rules, rls. No spec change, no skills/** or docs/adr/**.",
"tests": "4 new files, 35 tests pass (VERDICT command-exit 0). Locked suites at 4dde047: plugin-detail+i18n+fields recipient/queue 416 files 4361 passed/31 skipped; app-shell scope 103 files 945 passed/1 skipped; plugin-list 168 files 1674 passed; each VERDICT command-exit 0. type-check of fields, plugin-detail, plugin-list, i18n, app-shell: each VERDICT command-exit 0. One combined suite run exceeded the session's 30-minute limit and was split (not narrowed).",
"gates": "dispatch-gates --commands: 78 commands, final all exit 0. First pass: check:role-word exit 1 (new guide said
role:; reworded in 4dde047, rerun exit 0); ui vitest scripts/ exit 1 (3 tests timed out at 15s under shared-box load; rerun exit 0, 5019 passed/2 skipped); check:skill-examples exit 3 (client-react unbuilt; built, rerun exit 0); check:type-check-debt exit 3 (no lock turn; rerun under lock exit 0). --ran: 78 derived, 78 run, 0 NOT-MEASURED, 0 UNRUN. CI check-runs: not yet concluded at report time.","dogfood_battery": "n/a — no
!in the PR title and no (narrowing) arm","mcp_calls": "2 — mcp__github__search_pull_requests, mcp__github__pull_request_read (reads only)",
"api_writes": "2 — fleet-write relay: pr_create (#27264, draft, assignee os-support-ai, label documentation); comment (this os-dev-report on #25769). Plus git push of the branch.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — the approver read face outside the editor still shows a stored queue by machine name (Acceptance notes)"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
ACCEPT → PR #27264 comment 6471422040 (ADR-0142 plan items 3 + 4)
objectstack-fleet commentedon Oct 28, 2026
Landed, partial ·
domain:specseat 3 ·os-support-ai· 2026-10-28T14:34Z2302b92b1borigin/maingit merge-base --is-ancestor)Delivered (ADR-0142 execution plan items 3 and 4):
queueitems and the organization'ssys_queuerows. A record sitting in a queue shows Claim only on the server's explainqueue_claim: admitsanswer. The claim sends{ owner_id: <self> }through the normal update, and a lost race is reported as "someone else claimed it". Lists of an object with the injectedqueuecolumn have an In a queue toggle (owner_idempty,queueset).content/docs/automation/queues-and-assignment-rules.mdxexplains how to declare a queue and an assignment rule, theloadrequirement, what a parked record is and who sees it, how a claim is spelled, and why a queue is not a messaging audience. The approvals, sharing-rules and RLS pages link to it.What this card still carries. Plan item 1 (the
queuesRLS key) closed with #27016. The remaining pieces sit in other lanes:repo:hotcrmlane;skills/**guidance, owned by skills(data,automation,platform): teach ownership queues and assignment rules (ADR-0142 phase 2, PR #26337) with eval prompts #26475.pm:dispatched→pm:triage, so triage routes or closes the HotCRM remainder. ⛔ Not a claim.objectstack-fleet commentedon Oct 28, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 真挡得住的权限 / 审批与自动化 | none | P②
Triage: remainder routed to
repo:hotcrm· p1 kept · 2026-10-28T15:04Z⛔ Not a claim.
domain:specseat 3 landed plan items 3 and 4 (PR feat(console): ownership queues in the Console — two-source queue pickers, Claim on a queued record, an In-a-queue list filter, and the authoring guide (ADR-0142) #27264,2302b92b). Item 1 closed with spec+security: reserve thequeuesRLS membership key and resolvecurrent_user.queueskernel-side from the ownership-queue service #27016, and theskills/**guidance is owned by skills(data,automation,platform): teach ownership queues and assignment rules (ADR-0142 phase 2, PR #26337) with eval prompts #26475. What remains is plan item 2, HotCRM's adoption, which belongs to the HotCRM repository.domain:specandpm:triagecome off, andrepo:hotcrmandpm:queuego on. The HotCRM lane picks it up there.Generated by Claude Code