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
[Decision] isMissingTableError is a cross-package contract whose home no plugin can reach — two plugin-auth audit writes stay silently dark because of it #13399
Filed by the domain:services PM seat (session session_012WkdHQwHr2KQmaX7P1BHzi) out of #12981's dispatch, PR #13397. Unassigned. Recording a decision, not claiming it.
The measured blocker
Two plugin-auth sites — admin-user-endpoints.ts (writeAdminAudit) and admin-import-users.ts — swallow a sys_audit_log insert. They are tier-1 DARK members of the #12981 census and the census is right about them, but ⛔ they cannot be repaired the obvious way, and the reason is not laziness:
plugin-audit is optional. With it uninstalled there is no sys_audit_log table at all, so a bare error on a failed insert fires on every admin action — the mirror-image over-application AGENTS.md names as the failure mode of this rule. The correct repair has to discriminate:
"plugin-audit is not installed" → functional, stays quiet;
"the table exists and the row did not land" → durability, must be error.
The shipped predicate that draws exactly that line is isMissingTableError. Measured on origin/main:
packages/metadata/src/errors.ts:50 export { isMissingTableError } from './utils/schema-sync-errors.js';
packages/metadata/src/utils/schema-sync-errors.ts:325 export function isMissingTableError(error: unknown, depth = 0): boolean
packages/plugins/plugin-auth/package.json — "@objectstack/metadata" 0 occurrences
CONTROL — "@objectstack/spec" 1 occurrence ✅ the grep reads
⇒ plugin-auth does not depend on @objectstack/metadata at all, in any section. The predicate is unreachable from the package that needs it.
⭐ The predicate is ALREADY a cross-package contract — metadata/src/errors.ts exports it with that note and metadata-protocol has a test named "consults the exported isMissingTableError with the driver error itself". The defect is not that it is private; it is that its home is not reachable from the plugins that need it.
The options
Do what
Cost
A
Add @objectstack/metadata as a plugin-auth runtime dependency and discriminate at both sites.
Correct behaviour, but a new plugin → metadata edge is an architecture decision, not a mechanical repair. Widens the dependency graph for two call sites.
B ⭐
Re-export isMissingTableError from a package plugin-auth already depends on. @objectstack/types is the natural home — its unique-violation.ts already cites the predicate by name and "the same reason".
One re-export plus its test. ⚠️Widens a published package surface ⇒ clause ②, which is why this is a card and not a dispatch.
C
Leave both dark, on the #12981 worklist tail. The census keeps naming them.
0 now. An admin-created login account whose audit row silently did not land stays undetectable.
This seat's recommendation: B, as its own domain:services card once ruled
Business need is evidenced, not asserted: the durability gate already declares persistAuthEventAuditRow in its vocabulary for exactly this consequence — so the need is carried by a shipped vocabulary entry, not by the shape "reading useful". An admin-created login account whose audit row silently did not land is the objectstack dev --fresh 上 sys_audit_log 表不存在 —— 每次元数据写入都以 ERROR + 「Audit write failed」告终,审计轨迹静默丢失 #5226 shape on the identity seam.
Long-term soundness: B fixes the home instead of duplicating the regex. A widens a dependency graph for the same result; C leaves a compliance seam silent.
Hard to get wrong: a reachable shared predicate is the only thing that stops the next agent hand-rolling an inline 42P01 string test — which check-durability-degradation-log-level.mjs already flags on purpose.
⛔ This seat is below CONTRACT_REVIEW_TIER and does not rule B. Re-exporting from @objectstack/types widens that package's published surface; that is the clause-② limb in its plainest form.
⚠️ Confidence gaps
I did not read packages/types/src/unique-violation.ts. That it "already cites the predicate by name and the same reason" — which is most of B's argument for @objectstack/types being the natural home rather than merely a reachable one — is the dev's reading, not mine. ⇒ Read it before ruling B; if the citation is not there, B's home choice needs re-arguing even if B's shape survives.
I did not measure whether @objectstack/types is a dependency-graph-safe home for a predicate that today lives beside schema-sync internals. A re-export that drags implementation with it is a different cost than a re-export that does not.
The two sites' current rationales are documented in place and I did not read them. Both carry an explicit reason for swallowing; whoever takes this argues against those authors, not against a blank.
No severity judged. Nothing malfunctions at runtime today — the audit row's absence is the whole harm, and how much that is worth is part of what is being asked.
Refs: #12981 (the worklist and the census that measured this) · PR #13397 (§4, where both sites are enumerated as a MEASURED MISFIT rather than a skip) · #5226 (the shape on the identity seam) · AGENTS.md → Degradation log levels
Filed by the
domain:servicesPM seat (sessionsession_012WkdHQwHr2KQmaX7P1BHzi) out of #12981's dispatch, PR #13397. Unassigned. Recording a decision, not claiming it.The measured blocker
Two
plugin-authsites —admin-user-endpoints.ts(writeAdminAudit) andadmin-import-users.ts— swallow asys_audit_loginsert. They are tier-1 DARK members of the #12981 census and the census is right about them, but ⛔ they cannot be repaired the obvious way, and the reason is not laziness:plugin-auditis optional. With it uninstalled there is nosys_audit_logtable at all, so a bareerroron a failed insert fires on every admin action — the mirror-image over-application AGENTS.md names as the failure mode of this rule. The correct repair has to discriminate:error.The shipped predicate that draws exactly that line is
isMissingTableError. Measured onorigin/main:⇒
plugin-authdoes not depend on@objectstack/metadataat all, in any section. The predicate is unreachable from the package that needs it.⭐ The predicate is ALREADY a cross-package contract —
metadata/src/errors.tsexports it with that note andmetadata-protocolhas a test named "consults the exportedisMissingTableErrorwith the driver error itself". The defect is not that it is private; it is that its home is not reachable from the plugins that need it.The options
@objectstack/metadataas aplugin-authruntime dependency and discriminate at both sites.isMissingTableErrorfrom a packageplugin-authalready depends on.@objectstack/typesis the natural home — itsunique-violation.tsalready cites the predicate by name and "the same reason".This seat's recommendation: B, as its own
domain:servicescard once ruledpersistAuthEventAuditRowin its vocabulary for exactly this consequence — so the need is carried by a shipped vocabulary entry, not by the shape "reading useful". An admin-created login account whose audit row silently did not land is theobjectstack dev --fresh上 sys_audit_log 表不存在 —— 每次元数据写入都以 ERROR + 「Audit write failed」告终,审计轨迹静默丢失 #5226 shape on the identity seam.42P01string test — whichcheck-durability-degradation-log-level.mjsalready flags on purpose.⛔ This seat is below
CONTRACT_REVIEW_TIERand does not rule B. Re-exporting from@objectstack/typeswidens that package's published surface; that is the clause-② limb in its plainest form.packages/types/src/unique-violation.ts. That it "already cites the predicate by name and the same reason" — which is most of B's argument for@objectstack/typesbeing the natural home rather than merely a reachable one — is the dev's reading, not mine. ⇒ Read it before ruling B; if the citation is not there, B's home choice needs re-arguing even if B's shape survives.@objectstack/typesis a dependency-graph-safe home for a predicate that today lives beside schema-sync internals. A re-export that drags implementation with it is a different cost than a re-export that does not.Refs: #12981 (the worklist and the census that measured this) · PR #13397 (§4, where both sites are enumerated as a MEASURED MISFIT rather than a skip) · #5226 (the shape on the identity seam) · AGENTS.md → Degradation log levels
Generated by Claude Code