You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
chore(objectql, plugin-sharing): one PackageScopedNameRefusedError class for the 409 a package-scoped by-name read refuses with #31852
Found in the contract review of E8 #31680 (PR #31847, record 6734365312, nits N1, N2 and N4). Nothing here blocks E8. All three are follow-ups.
What is split
N2: after PR feat(objectql)!: by-name readers of apps, pages, dashboards, reports, books, cubes, tools, skills and agents consult (package, name) #31847, PackageScopedNameRefusedError exists twice: in packages/plugins/plugin-sharing/src/package-scoped-declarations.ts (around line 229, from E4) and in the new packages/objectql/src/package-scoped-read.ts. Both have the same name and the same 409 RESOURCE_CONFLICT envelope, but instanceof against one never matches the other. No cross-package catcher exists today, so nothing is wrong yet. But the next caller that catches "the package-scoped refusal" has to pick one, and it will be the wrong one half the time.
N4: the lean entry @objectstack/objectql/core exports SchemaRegistry, whose getApp now throws that class, but not the class itself, so a lean consumer can only match it by code and status.
N1: packages/cli/scripts/check-app-nav-i18n.mjs:909 calls engine.registry.getApp(app.name) with no package and no catch. A first-party app-name clash would now throw 409 out of the script instead of being reported. It cannot happen today (every first-party app has one holder).
Ask
One class, declared once at the lowest package both use (@objectstack/core, beside resolvePackageScopedHolders, unless the reader finds a better home). objectql and plugin-sharing import it, and both keep exporting the name, so existing imports keep resolving.
The class is exported from @objectstack/objectql/core as well.
check-app-nav-i18n.mjs reads the app with its package (it has the manifest), or reports a refused read as a finding instead of throwing.
Pin: an error thrown by the objectql reader is instanceof the class plugin-sharing exports, and the other way round.
Clause-②: yes (widening) (a new export on core and on the lean entry), Metadata-protocol: none, patch.
Claim: PM loop round 19 · 2026-11-09T07:57Z
Session: session_01ADHkmQHXhv9SKTU1qQ9CNC (card thread, dev) · seat session_01ADHkmQKZzp4DLtqiCkZZRM
Account: os-steve
Branch: claude/project-thread-mvgczn
Worktree: objectstack-issue-31852
Domain: domain:server
Seat: domain:server#2
File surface: packages/core/src/package-scoped-resolution.ts (and core's barrel), packages/objectql/src/package-scoped-read.ts, the objectql lean entry, packages/plugins/plugin-sharing/src/package-scoped-declarations.ts, packages/cli/scripts/check-app-nav-i18n.mjs, their tests, root .changeset/. ⛔ No packages/spec/**, no ui/**
Container & model: S, mode:thread, model: default tier, effort: medium
Clause-②: yes (widening) — @objectstack/core and the lean entry @objectstack/objectql/core gain PackageScopedNameRefusedError
Metadata-protocol: none
Responsibility: nits N1, N2 and N4 of E8's contract review (6734365312), filed by this seat
Thread-read: none
Premise: holds as filed, read at claim on origin/main074a014c: the class is declared in both objectql/src/package-scoped-read.ts and plugin-sharing/src/package-scoped-declarations.ts
Serial constraints cleared: Blocked-by #31680 is closed (PR #31847 merged cfe3563)
Scope: Fixes #31852. One PackageScopedNameRefusedError in core, re-exported unchanged by objectql (including its lean entry) and plugin-sharing; check-app-nav-i18n.mjs never throws a refused read out of the script. No behaviour change at any door. Order: /mnt/project-files/server-seat2/standard-order.md + /mnt/project-files/server-seat2/31852-delta.md. Summons: the maintainer's 「server 任务很多,你应该跟进并发5」 (project chat, 2026-11-09T02:28Z).
{
"issue": 31852,
"status": "done",
"branch": "claude/project-thread-mvgczn",
"pr": "https://github.com/objectstack-ai/objectstack/pull/31876",
"session": "session_01ADHkmQHXhv9SKTU1qQ9CNC",
"premise_still_valid": true,
"summary": "PackageScopedNameRefusedError is declared once in @objectstack/core package-scoped-resolution.ts (code RESOURCE_CONFLICT, status 409, httpStatus 409, refusal; message = the refusal's sentence). objectql package-scoped-read.ts and plugin-sharing package-scoped-declarations.ts re-export that binding under the same name, and the lean entry @objectstack/objectql/core exports it too. check-app-nav-i18n.mjs reads each app through a new pure readMergedApp: a refused getApp becomes a composition finding naming the app and every candidate; any other error is rethrown. No door changes its answer; plugin-sharing refusals gain httpStatus 409.",
"tests": "Red first on origin/main 074a014c: core package-scoped-resolution.test.ts 1 failed | 23 passed; objectql package-scoped-e8-readers.test.ts 2 failed | 71 passed; plugin-sharing package-scoped-declarations.test.ts 3 failed | 23 passed. Green at 02e922a7 (one lock run, VERDICT command-exit 0): typecheck core+objectql+plugin-sharing; core test 139 files / 3207 tests; plugin-sharing 72 / 1496; objectql 580 / 11936. At 3bb5c3e6 after merging origin/main: rebuild of the three, typecheck, the three changed files plus core-boundary.ratchet.test.ts 24/24, 75/75, 26/26, VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs: plugin-sharing re-export replaced by a local class, anchor 1 -> 0, 3 failed | 23 passed (all three new cross-package pins), restored blob == HEAD 339e4de5b597; objectql re-export replaced by a local class, anchor 1 -> 0, 1 failed | 72 passed (identity pin), restored blob == HEAD 45f07d0a11c9. check-app-nav-i18n --self-test six new cases, exit 0; live pnpm check:app-nav-i18n exit 0 (setup 57 ids, account 13 ids).",
"gates": "dispatch-gates --commands: 73 commands, each with its exit code captured before any pipe; all exit 0 at 02e922a7 and again at head 3bb5c3e6. --ran: 73 derived, 73 run, 0 NOT-MEASURED (a DERIVED zero, all 73 recorded an exit code, none 3). eslint --no-inline-config on packages/core/src, packages/objectql/src, packages/plugins/plugin-sharing/src and the script: exit 0. Full turbo build of packages/** at 02e922a7: 81/81. PR check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"line_budget": "9 files, +235 / -44 against origin/main before the merge",
"files_changed": [
".changeset/31852-one-package-scoped-refusal-class.md",
"packages/core/src/package-scoped-resolution.ts",
"packages/core/src/package-scoped-resolution.test.ts",
"packages/objectql/src/package-scoped-read.ts",
"packages/objectql/src/core.ts",
"packages/objectql/src/package-scoped-e8-readers.test.ts",
"packages/plugins/plugin-sharing/src/package-scoped-declarations.ts",
"packages/plugins/plugin-sharing/src/package-scoped-declarations.test.ts",
"packages/cli/scripts/check-app-nav-i18n.mjs"
],
"deviations": "none. @objectstack/cli takes no changeset: its files[] is dist, README.md and CHANGELOG.md, so scripts/ does not publish. No existing test edited.",
"mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2), all reads",
"api_writes": "2 — relay pr_create (POST /pulls + assignees os-steve, PR #31876, body read back identical 8894 bytes); this os-dev-report comment via post-stamped.mjs",
"open_questions": [
{ "question": "N1: catch the refused read, or read the app by its shell package?", "options": ["A catch and report as a composition finding naming every candidate (done)", "B read getApp(name, shellManifestId)"], "recommendation": "A, because B would silently pick the shell's app and hide a real app-name clash from the gate" }
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: PR #31825 (plugin-email) · adds a third class for the same refusal family, EmailTemplateAmbiguousError in plugin-email/src/template-loader.ts (409 RESOURCE_CONFLICT, extra template and candidates fields) · left to that PR's seat per delta ruling 1 · noted, not filed"
]
}
PackageScopedNameRefusedError is declared once, in @objectstack/core. objectql and plugin-sharing re-export the same class under the same name, so one instanceof matches a refusal from either. The lean entry @objectstack/objectql/core exports it too.
check-app-nav-i18n reports a refused app read as a composition finding naming every candidate, instead of stopping on the 409.
Every door still answers 409 RESOURCE_CONFLICT with the same sentence.
Activity
objectstack-fleet commentedon Nov 9, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: confirmed p3 ·
pm:queue→pm:blockedon #31680 · 2026-11-09T07:55Z⛔ Not a claim.
domain:server, chore. The card's ownBlocked-by: #31680(PR feat(objectql)!: by-name readers of apps, pages, dashboards, reports, books, cubes, tools, skills and agents consult (package, name) #31847) is open, sopm:blocked.Generated by Claude Code
objectstack-fleet commentedon Nov 9, 2026
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM
Claim: PM loop round 19 · 2026-11-09T07:57Z
Session:
session_01ADHkmQHXhv9SKTU1qQ9CNC(card thread, dev) · seatsession_01ADHkmQKZzp4DLtqiCkZZRMAccount:
os-steveBranch:
claude/project-thread-mvgcznWorktree:
objectstack-issue-31852Domain:
domain:serverSeat:
domain:server#2File surface:
packages/core/src/package-scoped-resolution.ts(and core's barrel),packages/objectql/src/package-scoped-read.ts, the objectql lean entry,packages/plugins/plugin-sharing/src/package-scoped-declarations.ts,packages/cli/scripts/check-app-nav-i18n.mjs, their tests, root.changeset/. ⛔ Nopackages/spec/**, noui/**Container & model: S,
mode:thread,model: default tier,effort: mediumClause-②: yes (widening) —
@objectstack/coreand the lean entry@objectstack/objectql/coregainPackageScopedNameRefusedErrorMetadata-protocol: none
Responsibility: nits N1, N2 and N4 of E8's contract review (6734365312), filed by this seat
Thread-read: none
Premise: holds as filed, read at claim on
origin/main074a014c: the class is declared in bothobjectql/src/package-scoped-read.tsandplugin-sharing/src/package-scoped-declarations.tsSerial constraints cleared: Blocked-by #31680 is closed (PR #31847 merged cfe3563)
Scope: Fixes #31852. One
PackageScopedNameRefusedErrorin core, re-exported unchanged by objectql (including its lean entry) and plugin-sharing;check-app-nav-i18n.mjsnever throws a refused read out of the script. No behaviour change at any door. Order:/mnt/project-files/server-seat2/standard-order.md+/mnt/project-files/server-seat2/31852-delta.md. Summons: the maintainer's 「server 任务很多,你应该跟进并发5」 (project chat, 2026-11-09T02:28Z).objectstack-fleet commentedon Nov 9, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: correction · back to
pm:queue· 2026-11-09T07:58Z⛔ Not a claim.
pm:blockedon objectql/metadata-protocol/mcp: app, page, dashboard, report, book, cube, tool, skill, agent on (package, name); agent_prompt on the protocol read (ADR-0150 child E8) #31680. That was wrong: objectql/metadata-protocol/mcp: app, page, dashboard, report, book, cube, tool, skill, agent on (package, name); agent_prompt on the protocol read (ADR-0150 child E8) #31680 is closed, and its PR feat(objectql)!: by-name readers of apps, pages, dashboards, reports, books, cubes, tools, skills and agents consult (package, name) #31847 is merged, so the onlyBlocked-by:target is done.pm:queue.domain:server, p3, unchanged.Generated by Claude Code
objectstack-fleet commentedon Nov 9, 2026
domain:server#2 · os-steve · session_01ADHkmQHXhv9SKTU1qQ9CNC
os-dev-report
{ "issue": 31852, "status": "done", "branch": "claude/project-thread-mvgczn", "pr": "https://github.com/objectstack-ai/objectstack/pull/31876", "session": "session_01ADHkmQHXhv9SKTU1qQ9CNC", "premise_still_valid": true, "summary": "PackageScopedNameRefusedError is declared once in @objectstack/core package-scoped-resolution.ts (code RESOURCE_CONFLICT, status 409, httpStatus 409, refusal; message = the refusal's sentence). objectql package-scoped-read.ts and plugin-sharing package-scoped-declarations.ts re-export that binding under the same name, and the lean entry @objectstack/objectql/core exports it too. check-app-nav-i18n.mjs reads each app through a new pure readMergedApp: a refused getApp becomes a composition finding naming the app and every candidate; any other error is rethrown. No door changes its answer; plugin-sharing refusals gain httpStatus 409.", "tests": "Red first on origin/main 074a014c: core package-scoped-resolution.test.ts 1 failed | 23 passed; objectql package-scoped-e8-readers.test.ts 2 failed | 71 passed; plugin-sharing package-scoped-declarations.test.ts 3 failed | 23 passed. Green at 02e922a7 (one lock run, VERDICT command-exit 0): typecheck core+objectql+plugin-sharing; core test 139 files / 3207 tests; plugin-sharing 72 / 1496; objectql 580 / 11936. At 3bb5c3e6 after merging origin/main: rebuild of the three, typecheck, the three changed files plus core-boundary.ratchet.test.ts 24/24, 75/75, 26/26, VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs: plugin-sharing re-export replaced by a local class, anchor 1 -> 0, 3 failed | 23 passed (all three new cross-package pins), restored blob == HEAD 339e4de5b597; objectql re-export replaced by a local class, anchor 1 -> 0, 1 failed | 72 passed (identity pin), restored blob == HEAD 45f07d0a11c9. check-app-nav-i18n --self-test six new cases, exit 0; live pnpm check:app-nav-i18n exit 0 (setup 57 ids, account 13 ids).", "gates": "dispatch-gates --commands: 73 commands, each with its exit code captured before any pipe; all exit 0 at 02e922a7 and again at head 3bb5c3e6. --ran: 73 derived, 73 run, 0 NOT-MEASURED (a DERIVED zero, all 73 recorded an exit code, none 3). eslint --no-inline-config on packages/core/src, packages/objectql/src, packages/plugins/plugin-sharing/src and the script: exit 0. Full turbo build of packages/** at 02e922a7: 81/81. PR check-runs: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "9 files, +235 / -44 against origin/main before the merge", "files_changed": [ ".changeset/31852-one-package-scoped-refusal-class.md", "packages/core/src/package-scoped-resolution.ts", "packages/core/src/package-scoped-resolution.test.ts", "packages/objectql/src/package-scoped-read.ts", "packages/objectql/src/core.ts", "packages/objectql/src/package-scoped-e8-readers.test.ts", "packages/plugins/plugin-sharing/src/package-scoped-declarations.ts", "packages/plugins/plugin-sharing/src/package-scoped-declarations.test.ts", "packages/cli/scripts/check-app-nav-i18n.mjs" ], "deviations": "none. @objectstack/cli takes no changeset: its files[] is dist, README.md and CHANGELOG.md, so scripts/ does not publish. No existing test edited.", "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2), all reads", "api_writes": "2 — relay pr_create (POST /pulls + assignees os-steve, PR #31876, body read back identical 8894 bytes); this os-dev-report comment via post-stamped.mjs", "open_questions": [ { "question": "N1: catch the refused read, or read the app by its shell package?", "options": ["A catch and report as a composition finding naming every candidate (done)", "B read getApp(name, shellManifestId)"], "recommendation": "A, because B would silently pick the shell's app and hide a real app-name clash from the gate" } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: PR #31825 (plugin-email) · adds a third class for the same refusal family, EmailTemplateAmbiguousError in plugin-email/src/template-loader.ts (409 RESOURCE_CONFLICT, extra template and candidates fields) · left to that PR's seat per delta ruling 1 · noted, not filed" ] }objectstack-fleet commentedon Nov 9, 2026
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM
ACCEPT → PR #31876 comment 6736399559 · head
3bb5c3e6· 2026-11-09T09:14Z · landing: pr_ready + automerge_enableobjectstack-fleet commentedon Nov 9, 2026
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM
落地 · 2026-11-09T09:54Z
1a66fcfed(ACCEPT 6736399559; TIER_DEFAULT PASS 6736388931)Fixes #31852Landed:
PackageScopedNameRefusedErroris declared once, in@objectstack/core. objectql and plugin-sharing re-export the same class under the same name, so oneinstanceofmatches a refusal from either. The lean entry@objectstack/objectql/coreexports it too.check-app-nav-i18nreports a refused app read as a composition finding naming every candidate, instead of stopping on the 409.RESOURCE_CONFLICTwith the same sentence.Carried:
EmailTemplateAmbiguousErroris left to that PR's seat.refactor(core,objectql,plugin-sharing): one PackageScopedNameRefusedE…