Skip to content

fix(spec): MONGODB_MULTI_TENANT_UNSUPPORTED leaves the error-code ledger — a boot refusal never reaches a wire envelope (#8035) - #8239

Merged
huangyiirene merged 4 commits into
mainfrom
claude/issue-8035-unregister-unemittable-code
Aug 13, 2026
Merged

fix(spec): MONGODB_MULTI_TENANT_UNSUPPORTED leaves the error-code ledger — a boot refusal never reaches a wire envelope (#8035)#8239
huangyiirene merged 4 commits into
mainfrom
claude/issue-8035-unregister-unemittable-code

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes#8035

ERROR_CODE_LEDGER (ADR-0112 D3) registered MONGODB_MULTI_TENANT_UNSUPPORTED under @objectstack/driver-mongodb (#3724). The finding-grading round on #8035 measured the code as registered-but-unemittable; this PR re-verified both readings on the merged tree before deleting, then unregisters the ledger row only.

Premise re-verification (both readings, on this branch's tree)

  1. Boot path rethrows pre-HTTP.packages/cli/src/commands/serve.ts:1279 rethrows a driver-construction failure on e?.code === 'MONGODB_MULTI_TENANT_UNSUPPORTED' into run()'s fatal handler — the process prints and exits 1 before any HTTP server exists. No envelope.
  2. The one request-reachable trigger is swallowed.assertObjectsNotTenantScoped (thrown from the driver's syncSchema/syncSchemasBatch) reaches request scope only via engine.syncObjectSchema inside ensureObjectStorage (packages/metadata-protocol/src/protocol.ts:9915-9922), whose catch is a documented "best-effort + non-fatal" console.warn. No envelope.
  3. Sweep of every other caller of the sync path (beyond the two readings the grading round measured): ObjectQLEngine.syncSchemas catches per-object into logger.error ([convention] best-effort 降级导致"看起来正常、实则不持久"时不应记 warn——把 #4460 的点状修复定成规则 #4632); the objectql plugin's boot sync catches into reportSyncFailure; service-datasource's connect path catches into logger.warn; the lifecycle sweep's archiveObject cold-store sync is caught by the per-object handler into report.errors + warn (a message string, never a wire error.code); packages/verify is CLI tooling. No wire path exists.

Consumer check (per the ledger's "Retiring a code" section)

  • Repo-wide exact-name search: the ledger row itself, two generated reference pages (contract.mdx, error-code-ledger.mdx), the driver's guard constant + README + drivers.mdx (all describe the thrown error object, which is unchanged — every sentence stays true), the CLI's duck-typed boot match (a consumer of the thrown object, not of the wire vocabulary — stays), and historical changesets/CHANGELOGs. No wire producer, no wire consumer.
  • objectui at origin/main: 0 hits (positive control on VALIDATION_FAILED confirms the grep saw the tree).
  • cloud (GitHub org-wide code search): 0 hits outside this repo.
  • Nothing gates an emission on ledger membership, so the removal changes no accept/reject verdict at runtime; the narrowing is type-level (ErrorCode / ApiErrorSchema.code).

What changed

  • packages/spec/src/api/error-code-ledger.zod.ts: the @objectstack/driver-mongodb entry (whose only row was this code) is removed. The header's "Retiring a code" section now names the second shape of the class: a row that never had a wire path, as opposed to one that lost its producer (OVERLAY_PERSISTENCE_FAILED / 删除 saveMetaItem legacy 写入分支后的三处残留:一个无生产者的 ledger 错误码 + 两处已过期的跨包注释 #5783, the precedent this PR mirrors).
  • packages/spec/src/api/error-code-ledger.test.ts: the wire vocabulary's refusal is asserted — ErrorCode.parse('MONGODB_MULTI_TENANT_UNSUPPORTED') throws, the union does not contain the string, and the owner key is gone (a future driver-mongodb wire code re-registers deliberately, with an emit path, not by reverting this PR).
  • packages/drivers/driver-mongodb/src/mongodb-tenancy-guard.test.ts: new pin — MULTI_TENANT_UNSUPPORTED_CODE === 'MONGODB_MULTI_TENANT_UNSUPPORTED'. With the ledger row gone this is the only cross-package guard on the literal the CLI matches duck-typed; a silent rename would un-arm the CLI's loud-fail path and boot the server with no driver (the exact failure driver-mongodb 完全没有行级租户隔离:读不加谓词、写不打戳,多租户下跨租户可读写 #3724 removed). The boot refusal itself — constant, error class, message, CLI match — is untouched, and its existing tests (message names the knobs, the escape route, and every offending object) all still pass.
  • content/docs/references/api/*.mdx: regenerated — the code leaves the two reference pages, and the ErrorCode enum count drops 264 → 263 across the API pages.
  • Changeset: @objectstack/specpatch — wire impact none (no response ever carried the code; the narrowing is type-level, and there is no emit site to reject), mirroring the 删除 saveMetaItem legacy 写入分支后的三处残留:一个无生产者的 ledger 错误码 + 两处已过期的跨包注释 #5783 precedent's bump. Not a declared-breaking changeset, so no ADR-0087 disposition marker is owed (check-adr-0087-registration --base origin/main: "adds no declared-breaking changeset"); the ADR-0087 conversion registry governs authorable metadata shapes, not the response vocabulary.

Verification (all post-merge of main @ fa4897335)

  • @objectstack/spec test: 385 files / 10196 tests passed; typecheck green.
  • @objectstack/driver-mongodb test: 16 files passed, 5 skipped / 365 tests passed, 143 skipped (skips are the suites needing a live MongoDB binary — pre-existing).
  • @objectstack/rest test: 104 files / 1744 tests passed.
  • check:generated: all 13 artifacts up to date from the merged tree (api-surface unchanged — the export set is unchanged, only the union narrows).
  • Gate farm: adr-anchors, changeset-gate-self-tests, cross-package-test-inputs, doc-formula-expressions, docs-audit-scope, i18n (9 bundles in sync), merge-driver, release-body, spec-parsed-alias, nul-bytes, error-code-casing — all pass.

Reverse verification (direction predicted before running)

  • Declarations: a probe compiled against the rebuilt dist.d.tsconst retired: ErrorCode = 'MONGODB_MULTI_TENANT_UNSUPPORTED' fails with TS2322 while the 'VALIDATION_FAILED' control compiles; a @ts-expect-error variant compiles clean (exactly one absorbed error). Proves the narrowing is in the built declarations, not a cached tree.
  • Runtime: temporarily restoring the deleted ledger row turned exactly the new rejection test red (1 failed | 8 passed in error-code-ledger.test.ts — the retired string parses again) and nothing else; restoring the committed state returned the file to 9 passed.

Generated by Claude Code


Generated by Claude Code

…ger (#8035)
The code was registered under @objectstack/driver-mongodb (#3724) but no
response envelope can carry it: the CLI boot handler rethrows the tenancy
refusal pre-HTTP and aborts, and the one request-reachable trigger
(assertObjectsNotTenantScoped via syncObjectSchema inside the metadata
protocol's ensureObjectStorage) is swallowed by a documented best-effort
catch. Registered-but-unemittable is the class the ledger header names;
precedent OVERLAY_PERSISTENCE_FAILED / #5783 — this instance is the sister
shape (never had a wire path, rather than lost it), now documented in the
header.
The boot refusal stays untouched: MULTI_TENANT_UNSUPPORTED_CODE, the error
class, its message, and the CLI's duck-typed literal match. A new
driver-mongodb test pins the code literal (with the ledger row gone it is
the only cross-package guard on the literal serve.ts matches); the ledger
test now asserts the wire vocabulary refuses the retired string. Reference
docs regenerated (ErrorCode enum 264 -> 263).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 13, 2026 2:16am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/permissions/system-context.mdx(via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)

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.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

os-regen merge lap for landing: merge origin/main (committed first, per
scripts/pm/os-regen-merge.sh), then wholesale regen of the os-regen'd
references/api pages from the merged tree. Sibling assertions on the
regenerated tree: #8230 retirement state (def names only in retirement
prose + migration entry), #8232 currency fraction-digit guard, #8234's
13 comparand-door exports in api-surface/data.json, #8236's groupBy
union row all survive; MONGODB_MULTI_TENANT_UNSUPPORTED stays absent
from the ledger and the references ErrorCode union reads main-minus-one
(+265 -> +264; the union grew by 2 on main since the previous lap).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
@huangyiirene
huangyiirene marked this pull request as ready for review August 13, 2026 02:35
@huangyiirene
huangyiirene added this pull request to the merge queueAug 13, 2026
Merged via the queue into main with commit 4dc1c7dAug 13, 2026
27 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-8035-unregister-unemittable-code branch August 13, 2026 02:53
huangyiirene pushed a commit that referenced this pull request Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] MONGODB_MULTI_TENANT_UNSUPPORTED may be registered-but-unemittable in the error-code ledger — a boot refusal never reaches a wire envelope

2 participants

@huangyiirene@claude