Skip to content

feat!: ownership queues and assignment rules (ADR-0142 phase 2) - #26337

Merged
objectstack-fleet[bot] merged 32 commits into
mainfrom
claude/project-thread-g33c7k
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 32 commits into
mainfrom
claude/project-thread-g33c7k

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Part of #25769. This is phase 2 of ADR-0142: the one spec + server PR of the record's Execution plan item 1. The card stays open for what this PR does not carry: the queues RLS membership key (an open question below, it needs a decision), the HotCRM STOPGAP deletion (its own lane, after a published release), the Console queue picker and Claim affordance (objectui lane), and the authoring guide (Execution plan item 4).

Clause-②: yes (narrowing)
Metadata-protocol: narrowed
Skills-docs: follow-up #26475

Contract review tier: H. The diff is over 3000 changed lines (168 files, +6230 / -380) and touches skills/**, so it lands only on a governed approver's review.

What lands, by decision

  • D1, the queue entity. New metadata type queue (QueueSchema, packages/spec/src/security/queue.zod.ts) and stack key queues; new system object sys_queue in plugin-sharing for an organization's own queues. The ownership-queue service (OWNERSHIP_QUEUE_SERVICE, contract IOwnershipQueueService) resolves a name from the registry first, then from the organization's rows. The service is not named queue, which the job queue already holds (phase-2 carry 6409493120).
  • D2, membership. A declared queue's members are positions (merge question 18); an organization adds users, teams, positions and business units through sys_queue_member. One member expander serves every consumer, and the BU-tree recompute covers queues.
  • D3, where a queued record sits. Every ownership: 'user' object gets a system-managed queue column beside owner_id; declaring your own queue field on one is refused at registration (INVALID_METADATA). queue joins the injected system columns and PUBLIC_FORM_SERVER_MANAGED_FIELDS. Members reach a sitting record (owner_id empty, queue in current_user.queues) through the plugin-sharing owner match: read and edit, never delete.
  • D4, claim, release, move. The step-3.5 owner gate handles queue. A claim sets owner_id from empty to the caller, by id, admitted when the sharing model allows the caller the record; the engine's by-id write precondition makes one claimant win and the other get 409 CONCURRENT_UPDATE. A release gives your own record back to its home queue. A move needs the transfer grant, and an unresolvable name is 422 QUEUE_NOT_FOUND. An insert naming a queue needs the transfer grant or membership; a guest has neither. Explain reports queue_membership and queue_claim on the sharing layer.
  • D5, assignment rules. New metadata type assignment_rule (AssignmentRuleSchema) and stack key assignmentRules. The engine routes each inserted row that is still ownerless and queueless after beforeInsert once: park it in a queue, or pick a member by round_robin (cursor in sys_assignment_cursor, advanced under the precondition) or least_loaded. Assignment never refuses an insert. kernel:ready refuses a stack that declares queues or assignment rules when no ownership-queue service is registered (merge question 17).
  • D6, the queue approver type is authorable again: it resolves to the queue's members when the step opens, an empty queue adds nobody and logs a warning, and the queue:NAME placeholder is never written again. APPROVER_VALUE_BINDINGS.queue reads both sources (Open item 1).
  • D7, the queue sharing recipient is authorable: ShareRecipientType gains queue, expandRecipient returns the queue's members, and grants are recomputed when membership changes or a queue is switched on or off.
  • Lint. A queue-only approval node is reported approval-approvers-may-resolve-empty; the org-axis red line admits the queue recipient; two advisory rules with os explain entries, assignment-rule-user-target (warning) and assignment-rule-queue-unstaffed (info).
  • Runtime. POST /security/_activation/:type/:name switches a declared queue or assignment_rule on and off.
  • Dogfood. The authz conformance matrix gains the claim admission, the access path and the two re-admitted members (Open item 5).

Edits outside the claim's declared file surface

The claim names spec, plugin-sharing, plugin-approvals and the gate site in objectql / plugin-security. Measurement put producers and gate-owned ledgers elsewhere too, each edited in place:

  • packages/lint, packages/runtime, packages/qa/dogfood: the lint, activation and dogfood rows of Execution plan item 1.
  • packages/metadata (the artifact door registers queues and assignmentRules), packages/metadata-core (queue classified as an injected system column), packages/platform-objects (labels and the queue recipient option in zh-CN, ja-JP, es-ES), ui/packages/app-shell (the flow designer stops warning that a queue approver resolves to nobody).
  • skills/objectstack-platform/SKILL.md: queues and assignmentRules in the top-level key list, required by check:skill-top-level-keys.
  • Gate-owned ledgers: scripts/platform-object-tenancy-census.json (regenerated), scripts/check-stack-collection-maps.mjs (waiver rows for the two keys), scripts/engine-double-contract.pinned.json, examples/app-showcase/src/coverage.ts and docs/qa/platform-checklist/coverage.json (waived: no fixture declares a queue or an assignment rule until the HotCRM phase), packages/spec/liveness/* (two hand-authored ledgers plus an empty-state registry row), packages/spec/llms.txt, the strictness-ledger counts under docs/audits/, and prose counts in six content/docs pages that count gates read. content/docs/automation/approvals.mdx now documents the queue approver type.

skills/** readings

  • Touched file skills/objectstack-platform/SKILL.md: 488 to 488 lines; 5824 to 5826 tokens (ceiling 5828).
  • Whole bundle, all 11 SKILL.md: 4525 to 4525 lines; 54450 to 54452 tokens.
  • The two added keys are paid by deleting two ADR citations, "(ADR-0019)" and "(ADR-0020)", from the same file. No re-wrap.

Deviations and readings for review

  • The ADR writes the explain kinds hyphenated; the spec enum is snake_case (queue_membership, queue_claim), matching every other ExplainRuleKind member.
  • sys_queue_member.queue is a text column holding the queue name, not a lookup, because an organization staffs a declared queue under its name and a declared queue has no sys_queue row.
  • The registration refusal for a declared queue field applies to the base registration layer only.
  • Declared assignment rules are validated at load time by lint only; the runtime logs an unresolved target once and leaves the row ownerless.
  • Round-robin cursor: a driver with no updateMany advances it unconditionally, logged once (merge question 7). A cursor that keeps losing races under concurrent inserts, past its bounded retries, parks the row in the queue with a warning; this second fallback is not in the record.
  • An organization queue whose name clashes with a declared queue is refused with field code invalid_value, because FieldErrorCode is a closed catalog with no duplicate member.
  • Gate readings, each with a case in ownership-queue-gate.test.ts: an echo of the stored queue is not a move; a claim on a record with no queue at all is admitted when sharing positively admits it (an edit share, Modify All); clearing owner_id on a record with no home queue is a disown and needs the grant; a member who supplies an empty owner beside a queue on insert is still stamped owner, because parking needs the grant (merge question 13); a predicate update writing queue fails closed without the grant.
  • Two merges of main, both on conflict (pin counts and generated references).

Open question (needs a decision)

The queues RLS membership key. Execution plan item 1 lists "the queue service and its queues membership-resolver key". The resolver contract has one IRlsMembershipResolver slot, which an app's own resolver also fills, and the producer screens reserved keys out of a resolver's answer. So plugin-sharing cannot add queues without either displacing an app's resolver or being screened out. This PR does not implement it; the access path of D3 does not need it (it goes through the sharing owner match). Recommendation: reserve queues in RESERVED_RLS_MEMBERSHIP_KEYS and have plugin-security resolve it from the ownership-queue service, as the kernel resolves positions, in a follow-up on this card.

Tests and gates (head 578e7a4, merge base 5cb1c1a)

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 163 commands; all 163 run, each exit code captured before any pipe, every one 0. --ran reconcile: 163 derived, 163 run, 0 NOT-MEASURED, 0 UNRUN. check:type-check-debt ran under os-verify-lock.sh.
  • Suites at this head, under the lock: objectql 474 files / 8820 tests; plugin-security 228 / 4964 passed (45 pre-existing skips, none added); plugin-sharing 57 / 1271; plugin-approvals 98 / 1280; platform-objects 96 / 1361; lint 166 / 7393; runtime activation door and route ledger 2 / 75; dogfood authz conformance 61; app-shell targeted 2 / 34.
  • Spec: the touched test files plus src/security, src/automation/assignment-rule, src/stack and src/data/filter.test.ts (54 / 1955), and src/type-alias-convention.pin.test.ts (3). The full spec suite was not run locally: it exceeds the 560s local budget on this box; CI runs it.
  • Full dogfood battery: owed by the narrowing; see the landing-head section below for the reading at the landing head.

维护者速读

(本节按 os-dev 对受管面 PR 的要求用中文书写;仓库其余 GitHub 文本按 AGENTS.md 用英文。两条规则在此并存,已在报告中注明。)

改了什么
平台有了「队列」。一条记录没有负责人时,可以先停在某个队列里:队列成员都能看到并编辑它(不能删除),谁先「认领」谁就成为负责人,同时认领的另一人会收到冲突提示。应用还可以声明「分配规则」:新建记录没有负责人时,按条件放进某个队列,或在队列成员中按轮询、最少负载挑一个人。审批里的「队列审批人」和共享规则里的「队列接收方」,以前是保留未开放的选项,现在真正可用。

为什么改
Tesla 裁定 D6 = A:队列与分配是平台能力,不该由应用自己写。HotCRM 目前用 587 行临时代码给工单和线索做轮询分配,还要绕过负责人转移的权限闸门。本 PR 落地并发版后,HotCRM 可以删掉这些代码,改成声明两个队列、两条规则。ADR-0142 已被接受,本 PR 是它的第二阶段(规范 + 服务端)。

风险与代价(含回滚)

  • 所有按用户归属的对象都会多一个系统字段 queue。已有对象若自己声明了同名字段,注册时会被拒,需要改名。
  • 新增三张系统表:sys_queue、sys_queue_member、sys_assignment_cursor。
  • 声明了队列或分配规则、却没装 plugin-sharing 的组合,启动时会被拒,而不是静默不分配。
  • 不声明队列的现有应用,行为不变。
  • 改动面大(168 个文件),还动了对外发布的技能包,属于 Tier H,必须人工批准。
  • 回滚:revert 本 PR 即可。新列和三张表里已有的数据会留下,但不再有代码读取。
  • 还没做:RLS 策略里暂时不能直接使用 current_user.queues(见上方待决问题)。

席位意见
(待席位填写)

你要做的

  1. 对待决问题拍板:queues 这个 RLS 成员键,是否由 plugin-security 作为平台保留键提供(推荐),在本卡后续补上。
  2. 审阅并批准本 PR。
  3. 合并并发版后,HotCRM 删除临时代码的那张卡才能开工。

Landing head — f35f41144 (seat note)

The Tests and Gates sections above were measured at 578e7a41. At the landing head f35f41144:

  • CI ran 62 check-runs: 57 success, 5 skipped by path, 0 failed.
  • The full dogfood battery covered 304 files: 301 passed, 3 skipped, 0 failed (2521 tests: 2503 passed, 18 skipped).
  • The contract review is 6441855511, PASS at cbd27d9a6. It closes all three must-fix items of 6430868091. The seat's delta review carries it to f35f41144: in every changed file, git diff cbd27d9a6 f35f41144 makes exactly main's own change from 7c34e5dc2 to fe53769a8, except the count lines of three files. Those are type-alias-convention.pin.test.ts (796), dropped-refinements.baseline.json (251 / 887) and references/index.mdx (1556).

The declaration lines above follow the changeset: Clause-②: yes (narrowing) / Metadata-protocol: narrowed.


Generated by Claude Code

claude added 14 commits October 26, 2026 12:24
Queue and assignment_rule metadata types, the stack's queues and
assignmentRules keys, the queue sharing recipient and queue approver
re-admitted, the ownership-queue service contract, the injected queue
column, the public-form strip, explain kinds and the ledger rows.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…itting-record access path

ADR-0142 phase 2, plugin-sharing half:

- sys_queue, sys_queue_member and sys_assignment_cursor, owned here.
- OwnershipQueueService, registered under `ownership-queue` in init():
  registry-first resolution, the activation-ledger switch, one expander
  ordered by user id, with an access/addressing purpose split.
- The read filter and the update arm gain `owner_id IS NULL AND queue IN
  current_user.queues`; checkEdit tests the basis before the public abstain.
- The `queue` sharing recipient expands through the service, with the
  recompute on queue, position, team and business-unit writes.
- Name guards on the authored tables and a boot report for shadowed rows.
- Spec: the contract's `purpose` option and `declaredQueueNamed`, the
  dropped-refinements baseline, and the regenerated schema artifacts.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…ment rules at insert (ADR-0142)

- Inject the system-managed `queue` text column on ownership:'user' objects,
  beside `owner_id`, and refuse an author-declared `queue` at registration
  (INVALID_METADATA, 422) with a rename prescription.
- Lower an update's write precondition to a guarded `updateMany` plus a
  readback; zero rows answers 409 CONCURRENT_UPDATE ("the record was taken").
  A driver with no `updateMany` degrades to a plain update, warned once.
- Evaluate declared assignment rules once per inserted row, after every
  beforeInsert hook, for rows whose owner and queue are both empty: park,
  round_robin (cursor under a version precondition, retried) and
  least_loaded, under a system context pinned to the row's organization.
  Assignment never refuses an insert.
- Refuse kernel:ready when a stack declares queues or assignment rules and no
  `ownership-queue` service is registered.
- Spec: govern `queue` and `assignment_rule` in the liveness ledger, add the
  strictness-ledger rows, localize the queue column label, register the three
  plugin-sharing platform objects, keep `queue` out of title derivation, and
  regenerate the reference docs and API artifacts.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…(ADR-0142 D3/D4)

Step 3.5 of the authz middleware reads the injected `queue` column:

- insert naming a queue: admitted with the transfer grant or for a member;
  a member's insert stays owned (the stamp is unchanged) with a home queue,
  and only the grant parks a row ownerless in one write
- claim (owner_id empty -> self, by id): admitted when the row's sharing
  verdict is 'allow', and the write is made conditional on owner_id being
  empty so a concurrent claimant gets 409 CONCURRENT_UPDATE
- release (owner_id self -> empty): admitted when the record has a home queue
- move (queue changed): transfer-class; a name that does not resolve in the
  row's organization is 422 QUEUE_NOT_FOUND
- a guest supplies neither anchor

Explain reports queue_membership and queue_claim on the sharing layer.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…-0142 D5/D6/D7)

- plugin-approvals: a `queue` approver resolves to the queue's members at
  node entry through the ownership-queue service (addressing purpose), in
  the directory organization and bounded by the request org's readers. An
  empty queue warns and adds no slot; the `queue:<name>` literal is never
  written again.
- lint: `queue` joins the group-routed set of the approver empty-slate
  check, the org-axis red-line table admits the queue recipient, and two
  advisory assignment-rule checks report a user-id target and a target
  queue declared with no member.
- ui: the flow designer stops warning that queue approvers resolve to
  nobody.
- dogfood: the authz matrix gains the ownership-queue row and re-syncs the
  sharing-rules recipient list.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…DR-0142 D1/D5)

- runtime: the catalog activation door takes `queue` (resolved by the
  ownership-queue service) and `assignment_rule` (resolved in the engine's
  metadata registry), with their own refusal wording.
- plugin-sharing: `declaredQueueNamed` answers a switched-off queue too, so
  the door can switch it back on; a ledger write for a queue recomputes the
  queue-recipient sharing rules naming it.
- i18n: the queue recipient option and the two metadata types' display
  pairs, translated in zh-CN, ja-JP and es-ES.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…s for queues

- Regenerate the platform-object tenancy census for sys_queue,
  sys_queue_member and sys_assignment_cursor.
- Classify the assignment-rule entry `criteria` empty state as scope.
- Count the queue and assignment-rule schema modules in llms.txt.
- Queue row refusals carry closed-catalog field codes: a declared-name
  clash is `invalid_value` (`duplicate` is not a FieldErrorCode member).
- Docs transcripts print 54 author-time rules.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
… counts and checklist waivers

- The shadowed-queue report keeps its logger receiver.
- The queue test's engine double applies the caller's limit by presence
  and no longer writes into its row source.
- The by-id precondition write passes driver options typed against
  IDataDriver instead of erasing them to any.
- Docs: the overlay page counts 31 registry types, the quick reference
  counts 15 automation and 6 security reference pages.
- Platform checklist: queue and assignment_rule are waived until an app the
  checklist drives declares them.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…kill keys

- The metadata artifact door registers declared `queues` and
  `assignmentRules` (beside `sharingRules`), so an artifact-only boot
  serves them; the stack-collection map waivers record the other sites.
- `AssignmentRuleEntryParsed` names the parsed entry; the four isomorphic
  queue and assignment-rule aliases are pinned (789 -> 793).
- The showcase waives `queue` and `assignment_rule` until an app adopts them.
- The platform skill lists the two new top-level keys; the two ADR
  citations in the next paragraph pay for them (488 lines either side).

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…; changeset names metadata and skills

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…export-origins snapshots

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
Conflicts resolved:
- objectql engine.ts: keep main's envelopeDriverWriteRefusal import beside
  the queue imports.
- platform-objects echo pin: 680 + 3 (main, the sharing composite) + 2
  (ADR-0142, queue and assignment_rule) = 685.
- plugin-security explain-access: main's resolveUserAuthzGrants import beside
  the ownership-queue contract import.
- spec dropped-refinements baseline: 244/838 at the base, +1/+2 from main,
  +3/+18 from this branch = 248/858.

Regenerated: spec api-surface/data.json, export-origins/data.json and the
approval and metadata reference pages.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
Resolves the type-alias pin count conflict with #25808 (790 + 4 = 794) and
regenerates the spec api-surface, export-origins and reference docs.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 11 package(s): @objectstack/cli, @objectstack/lint, @objectstack/metadata-core, @objectstack/metadata, @objectstack/objectql, @objectstack/platform-objects, @objectstack/plugin-approvals, @objectstack/plugin-security, @objectstack/plugin-sharing, @objectstack/runtime, @objectstack/spec, touching 351 documentable anchor(s). ⚠️ 34 changed file(s) yielded no anchor (packages/lint/src/index.ts, packages/lint/src/validate-org-axis-red-lines.ts, packages/metadata-core/src/injected-system-columns.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

108 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 1685a06dd941f4037536ff8253617a85d296e4e9.

⛔ 19 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 34 changed file(s) yielded no anchor (packages/lint/src/index.ts, packages/lint/src/validate-org-axis-red-lines.ts, packages/metadata-core/src/injected-system-columns.ts, …) — pages documenting those are invisible to this run
  • 2 cross-cutting symbol(s) contributed no route anchor: isSystem (5 routes), packageId (4 routes)
  • 5 anchor(s) matched too much of the corpus to be a work list: ObjectQL (symbol, 72 pages), objectName (symbol, 40 pages), VALIDATION_FAILED (literal, 35 pages), organization_id (literal, 38 pages), /api/v1/meta (route, 42 pages)
  • 50 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 55 of 207 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 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; 97 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 — 160 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 1685a06dd941f4037536ff8253617a85d296e4e9 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 4f77c91b6f37e201b68cb347e6787a2c6ac361c9 — the merge of head d6bbeaeb4eada74dc9e266fdfa25ac058ce4af7e into base 1685a06dd941f4037536ff8253617a85d296e4e9, 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 4f77c91b6f37e201b68cb347e6787a2c6ac361c9 && git checkout 4f77c91b6f37e201b68cb347e6787a2c6ac361c9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1685a06dd941f4037536ff8253617a85d296e4e9 d6bbeaeb4eada74dc9e266fdfa25ac058ce4af7e && git checkout -B drift-repro 1685a06dd941f4037536ff8253617a85d296e4e9 && git merge --no-ff d6bbeaeb4eada74dc9e266fdfa25ac058ce4af7e

node scripts/docs-audit/affected-docs.mjs --json 1685a06dd941f4037536ff8253617a85d296e4e9

⚠️ 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 1685a06dd941f4037536ff8253617a85d296e4e9 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…rn channel

check:optional-error-sink refuses a sink whose optional `error` has no
guaranteed fallback. The boot report now types its logger with a required
`warn` and calls `error` when present, `warn` otherwise.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…42 D3)

An object whose records have a user owner can no longer declare its own
`queue` field: registration refuses it. The changeset now declares
`Clause-②: yes (narrowing)` and `Metadata-protocol: narrowed`, and the
upgrade guide carries the D3 entry `object-queue-field-reserved` with the
remedy.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
Main moved the error-code ledger while this branch added QUEUE_NOT_FOUND,
so the merge ref's generated references (the ErrorCode enum count in
contract.mdx and automation-api.mdx) were stale in CI. Regenerated from
the merged tree.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
…ship inventory row

sys_queue, sys_queue_member and sys_assignment_cursor carry organization_id,
so the ADR-0131 D10 plan refused them as uninventoried. Each is written under
its organization; a member and a cursor name their queue or rule by name, so
no parent anchors them (fate 3, the Default Organization under single).

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
The merge queue removed the PR on a conflict. The one conflict is a count:
the metadata-forms positive control in object-lifecycle-panel-echo-
decisions.test.ts (686 on main after #26548, 688 here with this branch's
two registry types). Both comments are kept, main's first.

Claude-Session: https://claude.ai/code/session_01GTntg542RUmmRRbMY6wMZD
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat delta review: re-arm after the queue ejection. Seat domain:spec#4, session session_01HbmCEPMBmMLU3VZCEzfec7, 2026-10-27T14:54Z.

The queue removed this PR at 14:16Z for MERGE_CONFLICT. The new head d6bbeaeb4 merges main 1685a06dd.

Delta measured. File by file, git diff f35f41144 d6bbeaeb4 makes exactly main's own change fe53769a8..1685a06dd, with one exception: packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts. There the positive control moves to 688, which is main's 686 plus this diff's 2 for queue and assignment_rule; only that count line and its comment differ. Contract review 6441855511 and seat record 6442722673 therefore carry to this head.

Evidence at d6bbeaeb4. CI: 57 success, 5 skipped, 0 red. Full dogfood battery: 302 of 305 files passed, 3 skipped, 0 failed (report 6445215305). Mergeable: clean.

Next. Re-arm auto-merge.

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit a9f654e Oct 27, 2026
62 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-g33c7k branch October 27, 2026 15:21
os-project-manager pushed a commit that referenced this pull request Oct 27, 2026
…the same three plugin-security files)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5kdwdgQcbyZc22QQYf
This was referenced Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants