Skip to content

feat(plugin-hono-server,plugin-sharing): /auth/me/permissions answers hasAnyQueue from the ownership-queue service - #28654

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-7xha1v
Nov 1, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-7xha1v

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Part of #28508 (the server half; the strict spec response schema for the carrier is the other half, see Acceptance notes)

Clause-②: yes
Metadata-protocol: none

维护者速读

  • 改了什么:GET /auth/me/permissions 的每个已登录响应多一个布尔 hasAnyQueue,表示当前用户所在组织是否至少有一个归属队列(声明的、未被启用台账关掉的队列,或本组织的 sys_queue 行)。答案由 plugin-sharing 的归属队列服务新方法 hasAnyQueue(organizationId) 在系统上下文里算出。
  • 为什么改:列表工具栏的「在队列中」开关要知道这个组织有没有队列(List toolbar: offer the "In a queue" toggle only when the organization has at least one queue — today every owned object shows it, and in an app with no queues it always returns an empty list #28428,路线 A 已裁)。普通成员读 sys_queue 是 403,自己判断不了;现在服务端只回答这一个布尔,不给名字、不给数量。
  • 风险与回滚:只加字段,不加路由,不放宽成员对 sys_queue / sys_queue_member 的读权限(测试钉住 403)。服务未注册、无此方法或抛错都答 false,且不影响响应其余部分。回滚即 revert 本 PR。
  • 先看:packages/plugins/plugin-hono-server/src/current-user-endpoints.ts(organizationHasAnyQueue 与三个响应分支)、packages/plugins/plugin-sharing/src/ownership-queue-service.ts(hasAnyQueue)、packages/verify/src/me-permissions-has-any-queue.test.ts(真实 HTTP 门五个用例)。
  • 两分钟验证:pnpm --filter @objectstack/verify exec vitest run src/me-permissions-has-any-queue.test.ts,预期 5 passed:只有 sys_queue 行 → true,只有声明队列 → true,声明队列被关 → false,都没有 → false,服务未注册 → false,且每例成员读 /data/sys_queue 仍是 403。
  • 需要你做的:无,席位落地(contract review 由席位按 CONTRACT_REVIEW_TIER 完成后入队)。

What changes

  • OwnershipQueueService.hasAnyQueue(organizationId) (@objectstack/plugin-sharing). true exactly when some queue name would resolve in that organization: a declared queue the activation ledger leaves on, or a sys_queue row of the organization that does not sit under a declared queue's name. It reuses activeDeclaredQueues() and authoredQueueNames(), the reads queuesOf already makes, so the registry-first rule and the ledger filter stay in one place; there is no second resolver. It is a method on the class only: the spec contract IOwnershipQueueService is not edited in this PR (see Acceptance notes).
  • GET /auth/me/permissions carries hasAnyQueue: boolean (@objectstack/plugin-hono-server) on every authenticated body:
    • the full body (mePermissionsBody, now seven required grant keys, so tsc refuses a caller that drops it);
    • the degraded body (no SecurityPlugin): the question does not depend on it;
    • the closed body of the outer catch: false, since a fault vouches for nothing it did not finish.
    • An anonymous caller still gets { "authenticated": false } only, and the service is not asked.
  • Reaching the service (organizationHasAnyQueue): the kernel locator under OWNERSHIP_QUEUE_SERVICE, for the caller's tenantId (the organization queuesOf is asked for on the sitting-record path). No service, a service without the method, or a throw answers false; a throw logs one warn line and the rest of the body still answers in full. The function never throws.
  • Carrier choice. /auth/me/permissions, the card's candidate: the console's MePermissionsProvider already fetches it once per session (DEFAULT_ENDPOINT = '/api/v1/auth/me/permissions'), so the toolbar needs no extra request on list mount. No route is added.
  • Fixtures moved. Three exact-body pins in plugin-hono-server gain the new key as false (no ownership-queue service in those mounts): current-user-endpoints-outer-catch-closed.test.ts (closed body and the degraded byte-for-byte body) and current-user-endpoints-execution-context-envelope.test.ts.
  • Changeset .changeset/28508-me-permissions-has-any-queue.md: patch for @objectstack/plugin-sharing and @objectstack/plugin-hono-server (Metadata-protocol none takes patch, AGENTS.md Post-Task Checklist step 3). @objectstack/verify gains a test file only, which its files[] (dist) does not ship.

The field name for #28428's consumer and for the spec half: hasAnyQueue, type boolean, on the authenticated /auth/me/permissions body.

Tests

  • Service (plugin-sharing/src/ownership-queue.test.ts, new describe, 6 cases + 1 line in the no-engine case): row only ⇒ true for its own organization and false for another; declared and on ⇒ true; declared and switched off with no row ⇒ false (and resolveQueue agrees); a row under a switched-off declared name ⇒ false (it resolves nowhere); neither ⇒ false; a failed sys_queue read ⇒ false, the same posture as resolveQueue.
  • Route (plugin-hono-server/src/current-user-endpoints-has-any-queue.test.ts, 9 cases): the service's true / false carried as is, asked once with the caller's organization; no service ⇒ false with no warn; a service without the method ⇒ false; a throwing service ⇒ false, one warn, body otherwise full; degraded branch carries it; closed body carries false; anonymous body unchanged and service not asked; the only queue-named key on the wire is the boolean.
  • HTTP door (verify/src/me-permissions-has-any-queue.test.ts, 5 cases, two bootStack boots, a fresh sign-up as the plain member): the five Acceptance item 2 cases, each also asserting the member's GET /data/sys_queue is 403 with code: PERMISSION_DENIED. The switched-off case goes through the real activation door (POST /security/_activation/queue/:name as the admin); the absent-service case removes ownership-queue from the kernel's service maps.
Red first, green after, ablations (all through rebuilt dist/)
  • Red on main. Service + route tests, before the implementation: ownership-queue.test.ts 7 failed / 34 passed; current-user-endpoints-has-any-queue.test.ts 8 failed / 1 passed (the anonymous case, unchanged by design). HTTP door with origin/main's two source files checked out and both packages rebuilt (ablation-dist-preflight --absent hasAnyQueue on plugin-hono-server: absent from all 6 built files): 5 failed (5). Restored with git checkout HEAD --, git diff HEAD empty, blob = HEAD, rebuilt, marker present again in dist/index.js and dist/index.mjs.
  • Green. ownership-queue.test.ts 41/41, current-user-endpoints-has-any-queue.test.ts 9/9, door test 5/5.
  • Ablation 1, service always false (ablation-replace.mjs, anchor hit 1, marker ablation28508-always-false present in plugin-sharing dist/index.js + dist/index.mjs): service cases "row only" and "declared on" red; door cases 1 and 2 red, 3 passed. Restored: blob == HEAD, git diff HEAD empty; rebuilt, marker absent.
  • Ablation 2, ledger filter dropped (activeDeclaredQueues() → declaredItems(), marker ablation28508-no-ledger in dist): service cases "switched off" and "row under a switched-off name" red; door case 3 red, 4 passed. Restored the same way.
  • Ablation 3, the 403 pin (member_default given sys_queue read, marker ablation28508-member-reads-queue in plugin-security dist): door 5 failed (5), each expected 200 to be 403. Restored the same way; plugin-security rebuilt, marker absent.
  • A first attempt at ablation 1 put the marker in a comment, which the build strips, so the dist preflight could not see it (the test still went red). It was rerun with the marker in code, which is the reading above.

Acceptance notes

  • Premise gap on Target 4 / Acceptance 1 (the spec half). Measured on origin/main 004871f7: /auth/me/permissions has no spec response schema. Nothing in packages/spec declares its body; the console types it with a hand-written MePermissionsResponse interface (ui/packages/permissions/src/MePermissionsProvider.tsx) and fetches it raw; packages/client has no method for it. The nearest schema, GetEffectivePermissionsResponseSchema (packages/spec/src/api/protocol.zod.ts), declares only objects and systemPermissions, is a non-strict z.object, and backs the optional getEffectivePermissions protocol method that nothing implements. So "declared strictly, unknown sibling still refused" means a new strict schema for the whole body (three shapes: anonymous, degraded, full/closed). That half is with the maintainer through the seat; this PR leaves packages/spec untouched and says Part of.
  • Contract method. hasAnyQueue joins IOwnershipQueueService with the spec half. Until then the route reads it through a local optional type (AnyQueueReader), the same optional shape the contract already uses for declaredQueueNamed.
  • Organization id. The route passes the session's tenantId. With null (no active organization), the service counts every sys_queue row, which is what queuesOf already does for null.
  • The ui consumer (List toolbar: offer the "In a queue" toggle only when the organization has at least one queue — today every owned object shows it, and in an app with no queues it always returns an empty list #28428) is unchanged here; it returns to dispatch with the field name above.

Verification

All readings on head 3563cd56 (the final commit), after a whole-workspace build (turbo run build --concurrency=2, 118 tasks, VERDICT command-exit 0).

  • Package suites. pnpm --filter @objectstack/plugin-hono-server test: Test Files 37 passed (37), Tests 466 passed (466). pnpm --filter @objectstack/plugin-sharing test: Test Files 58 passed (58), Tests 1302 passed (1302). Door test: Tests 5 passed (5). VERDICT command-exit 0.
  • Typecheck. pnpm --filter @objectstack/plugin-sharing --filter @objectstack/plugin-hono-server --filter @objectstack/verify typecheck (each runs check:test-typecheck too): VERDICT command-exit 0.
  • Lint. eslint --no-inline-config over plugin-sharing/src, plugin-hono-server/src and the new verify test: exit 0. Repo-wide pnpm lint is CI's.
  • Consumers of the carrier body. The packages that read /auth/me/permissions through a real handler: @objectstack/runtime request-execution-context.conformance.test.ts + in-process-session-renewal.pin.test.ts (24/24), @objectstack/http-conformance hono-dispatcher-result-response.conformance.test.ts (3/3), @objectstack/verify parent-write-reach.test.ts + the door test (15/15). The other test files that name the route either mock their own body or assert one key (toMatchObject); the only exact-body pins were the three in plugin-hono-server, updated above. The console's MePermissionsProvider reads the body through a hand-written interface and ignores unknown keys.
  • Derived gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 64 commands, each run with its exit code captured before any pipe; all 64 exit 0. --ran reconciliation: "64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED". Three needed their prerequisite first (teardown self-test: one fetch of its pinned commit; check:dual-build-cjs-loads and check:i18n: the workspace build) and one was a real red fixed in 3563cd56 (check:objectql-double-limit: the new route test's find double now applies the caller's limit).
  • Not local. test:repo and the full Test Core shards are CI's (this diff changes no repo-project test). No full dogfood battery is owed: no ! in the title and no (narrowing) arm.
Gate record (64 lines, COMMAND :: exit N)
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/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.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:authz-resolver :: 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: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:error-status-conformance :: 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:pm-changeset-deadline-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:route-envelope :: 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

Generated by Claude Code

… hasAnyQueue from the ownership-queue service

The console's list toolbar needs to know, once per session, whether the
caller's organization has at least one ownership queue (declared and not
switched off in the activation ledger, or a sys_queue row). A plain member
may not read sys_queue, so the server answers with one boolean and discloses
nothing else.

- OwnershipQueueService.hasAnyQueue(organizationId) reuses the registry-first
  read and the ledger filter queuesOf already uses.
- /auth/me/permissions carries hasAnyQueue on every authenticated body; no
  service, no method, or a throw answers false.

Co-Authored-By: Claude <noreply@anthropic.com>
…ue through the real HTTP door

Co-Authored-By: Claude <noreply@anthropic.com>
…AnyQueue

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…caller's limit

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/plugin-hono-server, @objectstack/plugin-sharing, touching 11 documentable anchor(s).

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

  • content/docs/concepts/metadata-lifecycle.mdx (via IMetadataService (symbol, a top-level type))
  • content/docs/kernel/contracts/cache-service.mdx (via IMetadataService (symbol, a top-level type))
  • content/docs/kernel/contracts/data-engine.mdx (via IObjectQLEngine (symbol, a top-level type))
  • content/docs/kernel/contracts/index.mdx (via IMetadataService (symbol, a top-level type), ISecurityService (symbol, a top-level type))
  • content/docs/kernel/contracts/metadata-service.mdx (via IMetadataService (symbol, a top-level type))
  • content/docs/kernel/index.mdx (via IMetadataService (symbol, a top-level type))
  • content/docs/kernel/runtime-services/index.mdx (via ISecurityService (symbol, a top-level type))
  • content/docs/permissions/attachments-access.mdx (via ISecurityService (symbol, a top-level type))

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

  • content/docs/releases/v17/17-0.mdx (via ISecurityService (symbol, a top-level type))
  • content/docs/releases/v17/17-5.mdx (via IObjectQLEngine (symbol, a top-level type), ISecurityService (symbol, a top-level type))
  • content/docs/releases/v17/17-6.mdx (via ISecurityService (symbol, a top-level type))
  • content/docs/releases/v17/17-7.mdx (via ISecurityService (symbol, a top-level type))
  • content/docs/releases/v18/18-0.mdx (via ISecurityService (symbol, a top-level type))

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 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 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; 100 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 — 18 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 dd6349ecfa00cbd3eda805aed9f0f3958d5f1c65 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json dd6349ecfa00cbd3eda805aed9f0f3958d5f1c65

⚠️ 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 dd6349ecfa00cbd3eda805aed9f0f3958d5f1c65 → 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#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH

席位核对 · ACCEPT · PR #28654 · 2026-11-01T12:27Z

Head-sha: 3563cd56e990a0e8bb99fb8e72072f7c9121c5ec

维护者速读(席位终稿)

核对六项

  1. 形态:draft → main;Part of #28508,卡留开给 spec 一半(认领修订 6557730251);闭合关键词零命中(已扫)。文件 8 个,均在修订后的认领面内;packages/spec 未碰。
  2. 前提实测:dev 在 origin/main 上实测 /auth/me/permissions 无 spec 响应 schema、成员读 sys_queue 403。红先:服务 7 红、路由 8 红、HTTP 门 5/5 红;三处消融(服务恒 false、去掉启用台账过滤、给成员 sys_queue 读权)各自转红,复原后 blob 回 HEAD 并重建 dist。
  3. 门禁:本 head check-runs 35 success、13 skipped(路径未命中)、0 失败或待跑。dev 本地:64 条派生门禁全部 exit 0,--ran 0 NOT-MEASURED;check:objectql-double-limit 的真红已在 3563cd56 修。
  4. 接受的偏差:组织 id 为 null(无活动组织)时按全部 sys_queue 行算,与 queuesOf 对 null 的既有口径一致,只多透露一个布尔,接受。PR 正文「需要你做的」写的是 CONTRACT_REVIEW_TIER,实际欠的档是 TIER_DEFAULT(见第 6 项),以本 ACCEPT 为准。
  5. 范围外去向:spec 半(严格响应 schema + IOwnershipQueueService.hasAnyQueue)留在 Session payload: the console learns once per session whether the caller's organization has at least one ownership queue (declared or sys_queue), with no widening of member read on sys_queue #28508,已派同一卡线程(裁决记录 6557794880),第二个 PR Fixes #28508,入队前须 CONTRACT_REVIEW_TIER 复核;List toolbar: offer the "In a queue" toggle only when the organization has at least one queue — today every owned object shows it, and in an app with no queues it always returns an empty list #28428 的前端消费拿字段名 hasAnyQueue 回派。Metadata-protocol: none,无 Skills-docs: 欠账。
  6. 落地条款:Clause-②: yes(响应体加一个声明字段,放宽),非 spec 路径、非收窄、非 governed ⇒ 车道席位 TIER_DEFAULT 复核,即本 ACCEPT。逐项:① 公开面变化只有两处:已登录响应体多 hasAnyQueue: boolean(三个已登录分支都带,匿名体不变);OwnershipQueueService 类多一个公开方法(契约接口未动,路由经本地可选类型读,同 declaredQueueNamed 的既有形状)。判对:hasAnyQueue 复用 activeDeclaredQueues() 与 authoredQueueNames(),与 queuesOf 同一套注册表优先规则和台账过滤,没有第二个解析器;声明名下的 sys_queue 行不计,与 resolveQueue 一致。② semver:两个包 patch,Metadata-protocol: none 取 patch,与 changeset 一致。③ 边界旗:dev 提的 spec 半前提缺口已按裁决派回本卡线程;作者 objectstack-fleet[bot]。check-governed-merges --pr 28654:NOT governed,527 行。落地授权,维护者原话:「按照 pm-dispatch 规则复核过的都授权」(2026-10-25T11:35Z)。

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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant