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
Observation-class finding, recorded while implementing #6190 (PR #6973). Nothing a user hits today that I could measure — filed so the boundary is written down rather than rediscovered.
The gap
A metadata type with no entry in DEFAULT_METADATA_TYPE_REGISTRY is registered at runtime by a plugin (theme, connector, webhook, sharing_rule, …). Both existing write gates treat that family as permissive by construction, because getMetaTypes() synthesises allowRuntimeCreate: true for it and the write gate must agree with what the listing advertises:
ObjectStackProtocolImplementation.isRuntimeCreateAllowed — packages/metadata-protocol/src/protocol.ts, the "no static entry ⇒ creatable" branch.
SysMetadataRepository.assertAllowed — same carve-out, case 2 of its two-tier comment.
The two surfaces #6190 touched inherit that carve-out, deliberately and in opposite directions:
But loadMetaFromDb's filter is type-blind: where = { state: 'active', organization_id: null }. So an org-scoped row of a plugin-registered type is skipped at boot exactly like the flow and object rows #6190 is about — and it is the one family that gets neither the refusal nor the warning.
Measured incidentally while sweeping PR #6973's fixtures: webhook and sharing_rule org-scoped writes in packages/objectql/src/protocol-meta.test.ts stayed green through that change, which is what the carve-out predicts and what confirms it is live.
The 2026-08-08 ruling on #6190 was reasoned over DEFAULT_METADATA_TYPE_REGISTRY (19 of 27 entries). Extending the refusal to types outside that registry would apply a ruling to a surface nobody measured, and the plugin family is the one where "declared" is a synthesis rather than a declaration. That looked like exactly the widening the card told the dev not to do alone, so it is reported instead.
What would settle it
Someone needs to answer one question with evidence, not intuition: do any plugin-registered types actually get written org-scoped in a real deployment? Candidate readings:
If no — the cheapest correct move is to widen the diagnostic only (make reportUnhydratableOrgScopedRows scan runtime-registered types too, since it already derives its list at call time and could read the live registry rather than the static one). Warning is free; the refusal stays where the ruling put it.
If yes, and the rows matter — the type is claiming a per-org channel the loader does not implement, and that is a supportsOverlay / registry-declaration question for the owning plugin, not a gate question.
If yes, and the rows are accidental — the refusal should extend to the synthesised declaration, which means getMetaTypes()'s synthesis becomes the contract the write gate reads, in both places.
Not labelled pm:queue: severity is unmeasured in both directions and I would rather it be graded at triage than by me.
Refs: #6190 (the ruling and the two specimens), PR #6973 (the refusal, and the carve-out's TSDoc), PR #6600 (the cold-boot audit), #5086 (the code-only refusal that established the static-vs-runtime split), ADR-0005.
Observation-class finding, recorded while implementing #6190 (PR #6973). Nothing a user hits today that I could measure — filed so the boundary is written down rather than rediscovered.
The gap
A metadata type with no entry in
DEFAULT_METADATA_TYPE_REGISTRYis registered at runtime by a plugin (theme,connector,webhook,sharing_rule, …). Both existing write gates treat that family as permissive by construction, becausegetMetaTypes()synthesisesallowRuntimeCreate: truefor it and the write gate must agree with what the listing advertises:ObjectStackProtocolImplementation.isRuntimeCreateAllowed—packages/metadata-protocol/src/protocol.ts, the "no static entry ⇒ creatable" branch.SysMetadataRepository.assertAllowed— same carve-out, case 2 of its two-tier comment.The two surfaces #6190 touched inherit that carve-out, deliberately and in opposite directions:
reportUnhydratableOrgScopedRowsbuilds its scanned-type list by walkingDEFAULT_METADATA_TYPE_REGISTRY, so a type absent from that registry is absent from the scan.But
loadMetaFromDb's filter is type-blind:where = { state: 'active', organization_id: null }. So an org-scoped row of a plugin-registered type is skipped at boot exactly like theflowandobjectrows #6190 is about — and it is the one family that gets neither the refusal nor the warning.Measured incidentally while sweeping PR #6973's fixtures:
webhookandsharing_ruleorg-scoped writes inpackages/objectql/src/protocol-meta.test.tsstayed green through that change, which is what the carve-out predicts and what confirms it is live.Why it was not fixed in PR #6973
The 2026-08-08 ruling on #6190 was reasoned over
DEFAULT_METADATA_TYPE_REGISTRY(19 of 27 entries). Extending the refusal to types outside that registry would apply a ruling to a surface nobody measured, and the plugin family is the one where "declared" is a synthesis rather than a declaration. That looked like exactly the widening the card told the dev not to do alone, so it is reported instead.What would settle it
Someone needs to answer one question with evidence, not intuition: do any plugin-registered types actually get written org-scoped in a real deployment? Candidate readings:
reportUnhydratableOrgScopedRowsscan runtime-registered types too, since it already derives its list at call time and could read the live registry rather than the static one). Warning is free; the refusal stays where the ruling put it.supportsOverlay/ registry-declaration question for the owning plugin, not a gate question.getMetaTypes()'s synthesis becomes the contract the write gate reads, in both places.Not labelled
pm:queue: severity is unmeasured in both directions and I would rather it be graded at triage than by me.Refs: #6190 (the ruling and the two specimens), PR #6973 (the refusal, and the carve-out's TSDoc), PR #6600 (the cold-boot audit), #5086 (the code-only refusal that established the static-vs-runtime split), ADR-0005.