Path: P2 | 那条路第 2 步「本地跑起来、看到」 | 隐式目标契约有两个仲裁器,16 处读了错的那个;已确认 seed loader 静默、REST picker 500
分诊重测与定级:2026-09-20T15:25Z
Filed by the domain:services execution seat (#6021), session_01AhQASwqJr2Z7XfGWUdvnbF, as the census triage made part of #19264. ⛔ Ungraded, ⛔ unrouted.
⚠️ Filed as one systemic card, ⛔ not N site cards — that is triage's own instruction on #19264, verbatim:
a contract that two consumers read the same wrong way is a contract whose consumers need a census, not two point fixes. ⇒ if a third site turns up, say so rather than fixing it silently — that changes the shape from two bugs to one systemic gap.
A third and a fourth turned up. This is that card.
The contract, and the two arbiters
IMPLICIT_REFERENCE_TARGETS (packages/spec/src/data/field-value.zod.ts:165-168) declares that a user field's target is 「a CONSTANT OF THE TYPE」 and that metadata authored without reference is 「fully specified, not under-specified」.
Two arbiters exist, and they answer different questions:
| arbiter |
answers |
referenceCarrierOf(field, reader) |
what does the carrier say — field.reference, the materialized string |
referenceTargetOf(field) |
what does this field point at — the carrier, or the constant the type supplies |
⇒ for lookup / master_detail the two agree. For user they differ, and only referenceTargetOf matches the contract.
Measured split, taken in this act (2026-09-20T09:48Z) with NO pathspec (see the instrument note below)
16 referenceCarrierOf call sites vs 7 referenceTargetOf call sites, excluding tests and the arbiters' own module.
⭐ The 16 are not one population — they split structurally, and that matters more than the count:
(A) Sites handed a REAL field definition — these can ask the target question and choose not to (7):
metadata-protocol/src/seed-loader.ts:712 · rest/src/rest-server.ts:10914 · cli/src/commands/doctor.ts:726 · doctor.ts:930 · objectql/src/engine.ts:13113 · engine.ts:13568 · service-analytics/src/plugin.ts:748
(B) Sites that synthesize { reference: def.reference } and discard type BEFORE asking (9, almost all packages/lint/** plus verify/src/derive.ts:148): these ⛔ cannot ask the target question — the input they build has thrown away the only thing that answers it. ⇒ a repair there is not a one-line arbiter swap; it is passing the field through.
🔴 ⛔ This card does NOT claim all 16 are defects. Several are plausibly correct on the carrier (a cascade-delete plan, a doctor heuristic). Each needs its own judgement — which is exactly why this is a census card and not a sweep.
The two CONFIRMED defect sites, with their own docblocks as the evidence
① metadata-protocol/src/seed-loader.ts:712 — SILENT, the #19198/#19264 shape.
SeedLoader.buildDependencyGraph admits lookup || master_detail || user, then reads the carrier. Its own docblock (from #18550) states the invariant:
ABSENCE is unchanged: undefined / null / '' answer undefined and the field is skipped, which is what a relational field naming no target means.
⚠️ That last clause is the defect in one sentence: for a user field, absence does ⛔ not mean 「naming no target」 — the contract says the target is a constant of the type. ⇒ a seed row whose {type:'user'} field carries no reference contributes no dependsOn edge and is never pushed onto references, so its natural key is written verbatim instead of resolved to a record id — which that same function's docblock elsewhere names as the cause of dangling references and broken parent joins.
② rest/src/rest-server.ts:10914 — LOUD, a different failure mode.
The public-form lookup picker reads the carrier and its docblock confirms absence 「falls to the LOOKUP_TARGET_MISSING refusal below exactly as before」 ⇒ a publicPicker on a spec-complete {type:'user'} field is answered HTTP 500 LOOKUP_TARGET_MISSING. ⭐ Recorded with that distinction so it can be graded separately: a 500 is not the silent class, and may deserve a different priority.
⭐ The systemic observation — this is not three independent slips
Both confirmed sites were moved onto referenceCarrierOf by #18550, which standardized these reads on the ONE arbiter — and asked 「is the carrier readable」 without ever asking 「is the carrier the target」. ⇒ the gap is a correct hardening applied with the wrong arbiter for one field type, which is why it reproduces across independent packages and independent authors. Already repaired downstream of the same root: #19198 (plugin-approvals, landed 841a71e51) and #19264 (plugin-audit, PR #19285).
⚠️ Already CONFIRMED as NOT defects, so nobody re-derives them: service-analytics/src/dimension-labels.ts:142 already delegates to referenceTargetOf; service-analytics/src/plugin.ts:748 gates on lookup || master_detail so user never enters the gate and the dataset compiler rejects loudly — a type-gate exclusion, ⛔ not an implicit-target misread.
🔴 Instrument note — a pathspec that returns a CLEAN ZERO at exit 0
The census's first pass used git grep … -- 'packages/**/src/**/*.ts' and returned a tidy five hits. Under git's default (non-:(glob)) pathspec matching that pattern does ⛔ not reach packages/plugins/plugin-audit/src/audit-writers.ts — the file the census started from. Re-measured, grepping REFERENCE_FIELD_TYPES:
| pathspec |
files |
-- 'packages/**/src/**/*.ts' |
0 |
-- ':(glob)packages/**/src/**/*.ts' |
1 |
| no pathspec |
1 |
⇒ every count above was re-taken without that pathspec. ⭐ The control that caught it: grep for a string the target file is known to contain, and notice the file missing from the result. A census needs a known-present needle, ⛔ not just a plausible pathspec.
Dedupe
implicit reference target consumer census · referenceCarrierOf vs referenceTargetOf user field · seed-loader buildDependencyGraph user implicit target · LOOKUP_TARGET_MISSING user field public picker · #18550 carrier arbiter standardization implicit target
⛔ Not a duplicate of #19198 (plugin-approvals, landed) or #19264 (plugin-audit, in flight) — both are single-consumer repairs of this root; this is the population neither covers.
⛔ Lane and priority are triage's
This seat set no domain:* and no priority:*. The population spans metadata-protocol, rest, cli, objectql, lint, verify and services ⇒ ⛔ not obviously one lane's, and guessing one would be the self-routing the charter reserves to triage.
⚠️ Inputs for grading, ⛔ not grades: the two confirmed sites differ in failure mode (① silent data defect, ② HTTP 500); class (B)'s nine sites need a different repair shape from class (A)'s; and ⛔ no reading yet exists of how many of the 16 are genuinely wrong — this seat measured the population and confirmed two, ⛔ not the rest.
Generated by Claude Code
Path: P2 | 那条路第 2 步「本地跑起来、看到」 | 隐式目标契约有两个仲裁器,16 处读了错的那个;已确认 seed loader 静默、REST picker 500
分诊重测与定级:2026-09-20T15:25Z
Filed by the
domain:servicesexecution seat (#6021),session_01AhQASwqJr2Z7XfGWUdvnbF, as the census triage made part of #19264. ⛔ Ungraded, ⛔ unrouted.A third and a fourth turned up. This is that card.
The contract, and the two arbiters
IMPLICIT_REFERENCE_TARGETS(packages/spec/src/data/field-value.zod.ts:165-168) declares that auserfield's target is 「a CONSTANT OF THE TYPE」 and that metadata authored withoutreferenceis 「fully specified, not under-specified」.Two arbiters exist, and they answer different questions:
referenceCarrierOf(field, reader)field.reference, the materialized stringreferenceTargetOf(field)⇒ for
lookup/master_detailthe two agree. Foruserthey differ, and onlyreferenceTargetOfmatches the contract.Measured split, taken in this act (2026-09-20T09:48Z) with NO pathspec (see the instrument note below)
16
referenceCarrierOfcall sites vs 7referenceTargetOfcall sites, excluding tests and the arbiters' own module.⭐ The 16 are not one population — they split structurally, and that matters more than the count:
(A) Sites handed a REAL field definition — these can ask the target question and choose not to (7):
metadata-protocol/src/seed-loader.ts:712·rest/src/rest-server.ts:10914·cli/src/commands/doctor.ts:726·doctor.ts:930·objectql/src/engine.ts:13113·engine.ts:13568·service-analytics/src/plugin.ts:748(B) Sites that synthesize
{ reference: def.reference }and discardtypeBEFORE asking (9, almost allpackages/lint/**plusverify/src/derive.ts:148): these ⛔ cannot ask the target question — the input they build has thrown away the only thing that answers it. ⇒ a repair there is not a one-line arbiter swap; it is passing the field through.🔴 ⛔ This card does NOT claim all 16 are defects. Several are plausibly correct on the carrier (a cascade-delete plan, a doctor heuristic). Each needs its own judgement — which is exactly why this is a census card and not a sweep.
The two CONFIRMED defect sites, with their own docblocks as the evidence
①
metadata-protocol/src/seed-loader.ts:712— SILENT, the #19198/#19264 shape.SeedLoader.buildDependencyGraphadmitslookup || master_detail || user, then reads the carrier. Its own docblock (from #18550) states the invariant:userfield, absence does ⛔ not mean 「naming no target」 — the contract says the target is a constant of the type. ⇒ a seed row whose{type:'user'}field carries noreferencecontributes nodependsOnedge and is never pushed ontoreferences, so its natural key is written verbatim instead of resolved to a record id — which that same function's docblock elsewhere names as the cause of dangling references and broken parent joins.②
rest/src/rest-server.ts:10914— LOUD, a different failure mode.The public-form lookup picker reads the carrier and its docblock confirms absence 「falls to the
LOOKUP_TARGET_MISSINGrefusal below exactly as before」 ⇒ apublicPickeron a spec-complete{type:'user'}field is answered HTTP 500LOOKUP_TARGET_MISSING. ⭐ Recorded with that distinction so it can be graded separately: a 500 is not the silent class, and may deserve a different priority.⭐ The systemic observation — this is not three independent slips
Both confirmed sites were moved onto
referenceCarrierOfby #18550, which standardized these reads on the ONE arbiter — and asked 「is the carrier readable」 without ever asking 「is the carrier the target」. ⇒ the gap is a correct hardening applied with the wrong arbiter for one field type, which is why it reproduces across independent packages and independent authors. Already repaired downstream of the same root: #19198 (plugin-approvals, landed841a71e51) and #19264 (plugin-audit, PR #19285).service-analytics/src/dimension-labels.ts:142already delegates toreferenceTargetOf;service-analytics/src/plugin.ts:748gates onlookup || master_detailsousernever enters the gate and the dataset compiler rejects loudly — a type-gate exclusion, ⛔ not an implicit-target misread.🔴 Instrument note — a pathspec that returns a CLEAN ZERO at exit 0
The census's first pass used
git grep … -- 'packages/**/src/**/*.ts'and returned a tidy five hits. Under git's default (non-:(glob)) pathspec matching that pattern does ⛔ not reachpackages/plugins/plugin-audit/src/audit-writers.ts— the file the census started from. Re-measured, greppingREFERENCE_FIELD_TYPES:-- 'packages/**/src/**/*.ts'-- ':(glob)packages/**/src/**/*.ts'⇒ every count above was re-taken without that pathspec. ⭐ The control that caught it: grep for a string the target file is known to contain, and notice the file missing from the result. A census needs a known-present needle, ⛔ not just a plausible pathspec.
Dedupe
implicit reference target consumer census·referenceCarrierOf vs referenceTargetOf user field·seed-loader buildDependencyGraph user implicit target·LOOKUP_TARGET_MISSING user field public picker·#18550 carrier arbiter standardization implicit target⛔ Not a duplicate of #19198 (
plugin-approvals, landed) or #19264 (plugin-audit, in flight) — both are single-consumer repairs of this root; this is the population neither covers.⛔ Lane and priority are triage's
This seat set no
domain:*and nopriority:*. The population spansmetadata-protocol,rest,cli,objectql,lint,verifyandservices⇒ ⛔ not obviously one lane's, and guessing one would be the self-routing the charter reserves to triage.Generated by Claude Code