Repository navigation
[finding] implicit-target census (the #19264 sweep): 16 referenceCarrierOf call sites vs 7 referenceTargetOf — two CONFIRMED defects (seed-loader silent, REST picker 500), and 9 sites that discard type before they can even ask #19289
Description
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.
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/''answerundefinedand the field is skipped, which is what a relational field naming no target means.
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).
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.
Generated by Claude Code
Activity
os-warren commentedon Sep 21, 2026
Claim: PM loop round 1 from seat⚠️ Because #18319 landed minutes ago and it TOUCHED `packages/lint/`, every class-(B) count on this card must be re-measured against the NEW main — the dispatch says so. Siblings in flight from THIS seat: #19227 (packages/spec/scripts/) and #19009 (scripts/check-published-files.mjs + packages/spec/package.json) — both file-面 disjoint.`
domain:spec#2Session:
session_01UDXER3sdqfeVYpEWZs5mZxBranch:
claude/issue-19289-implicit-reference-target-censusWorktree:
objectstack-issue-19289Domain:
domain:specSeat:
domain:spec#2File surface (跨域例外路径 —— 逐包申报,triage
5750730045要求):packages/spec/src/data/field-value.zod.ts(arbiters) ·packages/metadata-protocol/src/seed-loader.ts·packages/rest/src/rest-server.ts·packages/cli/src/commands/doctor.ts·packages/objectql/src/engine.ts·packages/services/service-analytics/src/plugin.ts·packages/verify/src/derive.ts·packages/lint/src/**(class-(B) sites) · their sibling tests ·.changeset/(stop on breach; explain in the report)Container & model:
L,mode:subagent,model: opus(default judgment tier — this round'sdispatch-gates --tierat8f6d831printed 「no path-derived mandate」 and 「Clause ② SUSPECT surface」 namingpackages/spec/src/data/field-value.zod.ts; judgment tier because which of the 16 sites are genuinely defects is the card's central question, ⛔ not a mechanical sweep)Clause-②: no
Thread-read: 5752285669
Serial constraints cleared:
⭐ THE BLOCK IS OVER, and this seat measured it ending. Seat 3 recorded SERIAL twice (5750778529, re-taken 5752285669) behind PR #18319 on the \packages/lint/` region. PR #18319 MERGED 2026-09-21T00:08:02Z (also PR #19437). Census re-taken in THIS act over all 20 currently-open PRs, file list per PR: the seven class-(A) files, the two exact paths AND the `packages/lint/` prefix class are held by ZERO open PRs. Lit control same run: `packages/spec/src/api/package-api.zod.ts` → PR #19373, so the instrument discriminates; and the 跨域例外 in-flight check (「范围加上该单文件面触及各域的在飞单」) ran over every domain this card reaches, not only spec.Generated by Claude Code
fix(spec-consumers): the implicit-target census — four sites ask the …
test(spec-consumers): regression pins for all four implicit-target de…
chore(changeset): patch for the implicit-target census repairs (#19289)
os-warren commentedon Sep 21, 2026
os-dev-report
{ "issue": 19289, "status": "done", "branch": "claude/issue-19289-implicit-reference-target-census", "pr": "https://github.com/objectstack-ai/objectstack/pull/19472", "session": "session_01UDXER3sdqfeVYpEWZs5mZx", "premise_still_valid": true, "premise_note": "The CONTRACT and both defects held exactly as described. The CENSUS did not: re-measured on 8f6d831 with no pathspec it is 17 carrier vs 8 target, NOT the card's 16 vs 7. Class (A) = 8 (card: 7), class (B) = 9 (unchanged). Both deltas landed AFTER the card's census: objectql/src/engine.ts:9077 (buildSummaryIndex) is a NEW class-(A) carrier site from #19293 875e9ad at 2026-09-20T10:37Z; plugin-audit/src/audit-writers.ts:429 moved carrier->target when #19264/PR#19285 5636641 landed at 2026-09-20T10:09Z. objectql line numbers also drifted (13113->13204, 13568->13659). #18319 (097d268) touched packages/lint TESTS only, so it moved no class-(B) count. Instrument trap reproduced on the new tree with REFERENCE_FIELD_TYPES as a known-present needle: bare 'packages/**/src/**/*.ts' -> 0 files, ':(glob)...' -> 2, no pathspec -> 2.", "arbiters_changed": false, "arbiters_note": "DECISIVE FOR THE LANDING PATH, per dispatch section 5: packages/spec/src/data/field-value.zod.ts is UNTOUCHED. No file under packages/spec/ is in the diff, so the enqueue gate's path limb does NOT fire on this PR. The repair follows the shape of #19198 and #19264: swap the arbiter at the consumer, leave the spec alone.", "summary": "Re-measured the implicit-target census against the new origin/main and delivered a per-site verdict for all 17 referenceCarrierOf call sites. Four are defects and are repaired by asking referenceTargetOf: seed-loader buildDependencyGraph (SILENT, wrote a natural key verbatim into a record-id column), the REST public-form picker (LOUD, 500 LOOKUP_TARGET_MISSING on spec-complete metadata), lint object-graph graphFieldOf (SILENT and widest -- hop-untargeted is isUnjudgeable, so every lint rule resolving a field path stopped judging paths through a user field) and lint validate-field-consumers walkObject (SILENT, dropped the displayField edge onto sys_user). The other 13 are judged correct on the carrier, 11 of them because the site's own type gate excludes user. Class (B) turned out SMALLER than the card's framing, not larger: 7 of its 9 are type-gated away from user and only 2 needed the pass-the-field-through repair.", "per_site_verdicts": { "measured_against": "origin/main 8f6d831 (post #18319), re-confirmed after merging fbc12be", "split": "17 referenceCarrierOf call sites vs 8 referenceTargetOf, excluding tests and the arbiters' own module, taken with NO pathspec", "class_A_real_field_definition": [ { "site": "packages/metadata-protocol/src/seed-loader.ts:712", "question": "what does this field point at, to resolve a natural key to a record id", "own_type_gate": "lookup || master_detail || user -- ADMITS user", "verdict": "DEFECT 1 (silent)", "action": "REPAIRED -> referenceTargetOf" }, { "site": "packages/rest/src/rest-server.ts:10914", "question": "what object does the public picker query", "own_type_gate": "NONE -- resolves whatever field the picker names", "verdict": "DEFECT 2 (loud, HTTP 500)", "action": "REPAIRED -> referenceTargetOf" }, { "site": "packages/cli/src/commands/doctor.ts:726", "question": "which objects does this object depend on (cycle detection)", "own_type_gate": "field.type !== 'lookup' -> skip", "verdict": "NOT a defect -- type-gate exclusion; a user field's target is always sys_user, never a graph node here", "action": "left on the carrier" }, { "site": "packages/cli/src/commands/doctor.ts:930", "question": "which objects are referenced (unused detection)", "own_type_gate": "field.type !== 'lookup' -> skip", "verdict": "NOT a defect -- type-gate exclusion", "action": "left on the carrier" }, { "site": "packages/objectql/src/engine.ts:9077 (buildSummaryIndex)", "question": "which child field is the FK back to this parent", "own_type_gate": "master_detail || lookup", "verdict": "NOT a defect -- type-gate exclusion. NOT ON THE CARD: this site landed in #19293 after the census was taken", "action": "left on the carrier" }, { "site": "packages/objectql/src/engine.ts:13204 (planCascadeAtomicity)", "question": "which children reference this object", "own_type_gate": "master_detail || lookup", "verdict": "NOT a defect -- type-gate exclusion", "action": "left on the carrier" }, { "site": "packages/objectql/src/engine.ts:13659 (cascadeDeleteRelations)", "question": "which children reference this object", "own_type_gate": "master_detail || lookup", "verdict": "NOT a defect -- type-gate exclusion", "action": "left on the carrier" }, { "site": "packages/services/service-analytics/src/plugin.ts:748", "question": "relationship resolution for a dataset dimension", "own_type_gate": "lookup || master_detail", "verdict": "NOT a defect -- pre-judged on the card, re-confirmed present and unchanged", "action": "left on the carrier" } ], "class_B_synthesize_and_discard_type": [ { "site": "packages/lint/src/data-model-rules.ts:250 (refOf)", "own_type_gate": "4 callers: RELATIONSHIP_TYPES = {lookup, master_detail} (R1 + child index), OPTION_FIELD_TYPES = {select,multiselect,radio,enum}, summary", "verdict": "NOT a defect -- user reaches no caller. NOTE: R1 relationship/missing-reference would be a LOUD false positive on a user field if RELATIONSHIP_TYPES admitted it; it does not", "action": "left on the carrier" }, { "site": "packages/lint/src/object-graph.ts:242 (graphFieldOf)", "own_type_gate": "NONE; the consuming resolveFieldPath gates on RELATIONSHIP_FIELD_TYPES which ADMITS user", "verdict": "DEFECT 3 (silent, widest reach) -- a carrier-less user field answered hop-untargeted, which isUnjudgeable treats as 'the graph could not answer' and describeFieldPathVerdict renders as nothing, so every rule in the package that resolves a field path stopped judging such paths", "action": "REPAIRED -> the whole def passed through to referenceTargetOf" }, { "site": "packages/lint/src/validate-expressions.ts:395", "own_type_gate": "def.type !== 'master_detail' -> continue", "verdict": "NOT a defect -- type-gate exclusion. Its synthesized literal is additionally load-bearing for the #5017 receiver meta-test, which reads this rule's SOURCE", "action": "left on the carrier, form untouched" }, { "site": "packages/lint/src/validate-field-consumers.ts:560 (walkObject)", "own_type_gate": "NONE", "verdict": "DEFECT 4 (silent) -- the displayField consumer edge onto sys_user was never recorded, so a field that column displays was reported consumed by nobody. Materiality: recordable only where sys_user is compiled into the linted stack", "action": "REPAIRED -> the whole field passed through to referenceTargetOf" }, { "site": "packages/lint/src/validate-object-references.ts:306 (field target)", "own_type_gate": "RELATIONSHIP_TARGET_FIELD_TYPES = {lookup, master_detail, user} -- ADMITS user", "verdict": "LATENT MISREAD -- the OUTPUT was already right for the wrong reason (absence answered undefined and check returns early; the resolved sys_user is admitted at rung 3 by isPlatformProvidedObjectName), so the two legal spellings of one fully-specified field reached the same silence by different routes", "action": "ALIGNED -> referenceTargetOf; no output change, pinned in both directions" }, { "site": "packages/lint/src/validate-object-references.ts:329 (action param target)", "own_type_gate": "ActionParamSchema.type is OPTIONAL -- a field-backed param inherits its type at runtime", "verdict": "NOT a defect -- and this one was MEASURED, not reasoned. See deviations: swapping it deleted a live check", "action": "left on the carrier; regression guard added" }, { "site": "packages/lint/src/validate-security-posture.ts:292 (refOf)", "own_type_gate": "CBP_TIERS = required master_detail / any master_detail / required lookup", "verdict": "NOT a defect -- type-gate exclusion. Its synthesized literal is also #5017 meta-test subject matter", "action": "left on the carrier, form untouched" }, { "site": "packages/lint/src/validate-sharing-rule-enforceability.ts:267 (masterOf)", "own_type_gate": "f.type === 'master_detail'", "verdict": "NOT a defect -- type-gate exclusion", "action": "left on the carrier" }, { "site": "packages/verify/src/derive.ts:148 (relationTarget)", "own_type_gate": "RELATIONAL = {lookup, master_detail, master-detail, masterdetail, tree} -- EXCLUDES user", "verdict": "NOT a defect -- type-gate exclusion. tree is in the set but has no implicit target, so nothing changes for it either", "action": "left on the carrier" } ], "totals": { "sites": 17, "defects_repaired": 4, "aligned_no_output_change": 1, "judged_correct_on_carrier": 12 } }, "files_changed": [ "packages/metadata-protocol/src/seed-loader.ts", "packages/metadata-protocol/src/seed-loader-implicit-reference-target.test.ts (new)", "packages/metadata-protocol/src/seed-loader-reference-carrier.test.ts", "packages/rest/src/rest-server.ts", "packages/rest/src/public-form-lookup-picker.test.ts", "packages/lint/src/object-graph.ts", "packages/lint/src/object-graph.test.ts", "packages/lint/src/validate-field-consumers.ts", "packages/lint/src/validate-field-consumers.test.ts", "packages/lint/src/validate-object-references.ts", "packages/lint/src/validate-object-references.test.ts", ".changeset/19289-implicit-reference-target-census.md" ], "tests": "CONSUMER-PACKAGE TESTS, named per package with real readings, re-run AFTER merging origin/main fbc12be (the closure was rebuilt first because packages/spec moved on main's side): @objectstack/lint 106 files / 4019 tests passed; @objectstack/metadata-protocol 184 passed + 3 skipped files / 2627 passed + 19 skipped; @objectstack/rest 194 files / 3254 passed + 1 skipped. typecheck green on all three (VERDICT command-exit 0), including check:test-typecheck for lint (2 files / 6 errors held in the shrink-only debt ledger, unchanged) and rest (0/0). Every build and test ran through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-19289; verdicts read from the wrapper's own 'VERDICT command-exit' line, never a bare $?. NO ablation was performed: no new gate or guard was added, so there is nothing whose ability to fail needed proving -- the equivalent evidence is the measured regression in deviations below, where a real suite went red on a real mistake and back to green on its correction.", "gates": { "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths passed -- the script took its own change set from the merge base), then reconciled with --ran", "reconciliation": "63 derived / 60 run / 3 NOT MEASURED / 0 UNRUN -- exit 0", "not_measured": [ "pnpm check:dual-build-cjs-loads :: exit 3 -- PREREQUISITE NOT MET, reads built output and 44 packages have no dist/; needs a whole-repo pnpm build, which CI does", "pnpm check:type-check-debt :: exit 3 -- PREREQUISITE NOT MET, needs the full turbo build closure, which CI does", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 3 -- cannot read its commit-pinned positive control on a shallow clone (git rev-parse --is-shallow-repository = true). Checker-health only; it grades the checker's fixtures, not this diff" ], "exit_code_discipline": "every gate ran as `cmd > file 2>&1; ec=$?` -- the exit code captured BEFORE any pipe. Exit 3 is recorded as NOT MEASURED, never as a pass.", "changeset_families": "the 7 families that apply only once a changeset exists were derived again after writing it and all 7 exited 0 (check-adr-0087-registration base+self-test, check-empty-changeset base+self-test, release-rehearsal-clone --self-test, check:objectui-changeset, check:pm-changeset-deadline-census)", "lint_narrowing": "DECLARED NARROWING, with all three pieces of evidence. (1) Population read from eslint.config.mjs itself, which states in its own comment that this repo 'runs one eslint.config.mjs, which never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file, test or not' -- measured there with a positive control. (2) --format json reports 37 files linted, 0 errors, 0 warnings. (3) Invariance: with no type-aware linting, this diff cannot move the verdict on any file it does not touch. The 37 is a SUPERSET of my own 12 because the three-dot set includes what the merge brought in. Measured at 97b689b, the final commit.", "control_chars": "grep -naP over the 12 changed files for the non-tab/newline control range: 0 hits, beyond check:nul-bytes", "clause2_pair": "node scripts/pm/check-clause2-carriers.mjs --pair 19472 :: exit 4 -- C6. NOT a defect in this PR and NOT mine to clear: domain:spec owes a contract review of record on EVERY round it delivers, Clause-2 yes or no. No '## Contract review' comment names head 97b689b749 yet. The script itself says the remedy is the lane seat's and that no label may be written from it. The PR is correctly left DRAFT and out of the queue, which is the charter's safe state." }, "line_budget": "NOT APPLICABLE -- no skills/** path is in the diff, so no published-skill line or token ratchet applies. Diff size 12 files, well under the 5000-changed-line human-merge threshold (dispatch-gates measured the pre-merge set at 640 lines).", "labels_written": "NONE, deliberately. The dispatch named no label and budgeted exactly two writes (the PR create and this comment), so the intersection with my label scope is empty -> zero writes, reported here. skip-changeset does not apply: three published packages take a behaviour fix, so a patch changeset is written instead. needs:contract-review is the seat's and is not on the PR; I neither hung nor cleared it.", "mcp_calls": "0 -- no MCP tool was called, read or write. Every GitHub interaction went through the REST proxy with curl.", "api_writes": "2 -- exactly the budget. (1) POST /repos/objectstack-ai/objectstack/pulls (draft PR #19472). (2) POST /repos/objectstack-ai/objectstack/issues/19289/comments (this report). No PATCH of any kind: the PR body was written once, on create, and read back byte-identical (10684 stored vs 10685 sent, trailing newline; exactly one attribution footer). All other REST calls were reads.", "deviations": [ "MEASURED REVERSAL, reported rather than buried: I initially repaired SIX sites, including validate-object-references.ts:329 (action param). packages/lint/src/reference-integrity-suite.test.ts went red -- 'object-reference-unknown' vanished from the suite's findings entirely. Cause: ActionParamSchema.type is OPTIONAL (a field-backed param inherits its type at runtime, 'not visible at parse time' per that schema's own refinement comment), so referenceTargetOf answered undefined for every param declaring no type, and the corpus param { name: 'owner', reference: 'user' } stopped being checked. The swap deleted a live check. Reverted in its own commit, verdict corrected to NOT-a-defect, regression guard added beside it. This is precisely the reach the lane charter's consumer-package-test rule exists to buy.", "INSTRUMENT ERROR I MADE AND CAUGHT, the same class the card warns about: my first scan for pinned reader labels used git grep with an unescaped pattern and reported that seed-loader's label was unpinned. It is pinned -- the test spells it as a regex, 'SeedLoader\\\\.buildDependencyGraph', so the literal backslash defeated the match. Re-taken with git grep -F and a known-present needle control: FOUR reader labels are pinned, not three. All four were updated deliberately rather than discovered red.", "PR BODY IS MISSING ITS Clause-2 LINE, and my one body write is spent. The claim comment declares 'Clause-②: no'; per my dispatch contract that line should have been copied to the start of a line in the PR body, because scripts/check-changeset-no-major.mjs reads pr.body (line 1554, readClause2Line) in pr-automation.yml's 'Check Changeset' job. I put it in the changeset body (where the AGENTS.md checklist also asks for it) but NOT in the PR body. Since dev writes the PR body exactly once and never PATCHes it, the remedy is named here for the seat: add one line 'Clause-②: no' at the start of a line in PR #19472's body. The changeset is patch, not major, so this is a legibility fix rather than an expected red.", "Gate coverage is LOCAL and TARGETED by design, not the whole farm: the derived families were run, the three prerequisite-blocked ones are declared NOT MEASURED above, and pnpm lint was run as a declared narrowing with its three pieces of evidence. CI收敛 is the PM's to read, not mine to wait on." ], "open_questions": [ { "question": "referenceTargetOf takes no `reader` label, while referenceCarrierOf takes one precisely so the refusal 'says who could not read it'. This card's whole direction moves consumers onto referenceTargetOf, so every site so moved loses its own name from the TypeError -- and four existing pins were retargeted from the site label to /referenceTargetOf/ here. Unlike #19198 and #19264, whose sites CATCH the throw and log a warning naming themselves, all four of my repaired sites let it propagate, so the message is the only carrier of the reader's identity. Should referenceTargetOf gain the same optional `reader` parameter?", "options": [ "A -- Leave it. Matches the two landed repairs exactly; the refusal still names the error class, the offending shape and the prescription, and only the 'who' is generic. Costs nothing now and keeps this p1 off the contract-review path.", "B -- Add an optional `reader` to referenceTargetOf, purely additive and backward-compatible. Preserves all four existing pins byte-for-byte and stops the diagnostic degrading further as the census direction continues. Costs: it edits packages/spec/src/data/field-value.zod.ts, which fires the enqueue gate's path limb, so it needs a contract-review-tier review and a spec changeset." ], "recommendation": "A for THIS card -- it keeps a p1 with a live 500 on the simple landing path, and it is what the two landed repairs did. But B is the better end state and gets better the more of this census direction lands, so it is worth a follow-up card rather than a silent acceptance. I deliberately did not take B unilaterally, because which one is right is a judgement about the arbiter's signature and therefore the spec seat's, not an executor's." } ], "out_of_scope_findings": [ "noted, not filed: referenceTargetOf's missing `reader` label (the open question above). Not one of the three filable classes -- the refusal still fires and still carries its prescription, no declared contract is violated, and it traps no author. Successor named: the next PR that moves a consumer onto referenceTargetOf will hit it again.", "noted, not filed: after the object-graph repair, graphFieldOf no longer populates `reference` for a NON-relationship field carrying a stray reference (e.g. { type: 'text', reference: 'foo' }), because referenceTargetOf returns undefined outside REFERENCE_VALUE_TYPES. Unreachable in-repo -- resolveFieldPath gates on RELATIONSHIP_FIELD_TYPES before reading .reference, and no other module reads GraphField.reference (checked across all 57 importers of object-graph). Strictly more correct: a text field's stray carrier is not a target. Successor: any future consumer of GraphField.reference.", "noted, not filed: three class-(B) sites (validate-expressions.ts:395, validate-security-posture.ts:292, data-model-rules.ts:250) must keep their synthesized { reference: x.reference } literal because the #5017 receiver meta-test reads their SOURCE to prove they read `reference` and never an alias -- a read folded into a helper call would disarm that scan silently. They are type-gated away from user so nothing is owed, but anyone later sweeping class (B) to completion will meet this constraint. Successor: any such sweep." ], "docs_impact": { "verdict": "NO page is falsified, and NO docs edit is owed. Read either way, as asked.", "why_the_bot_was_blind": "Docs Drift Check (PR comment 5754281440) yielded 4 anchors from 3 changed packages but ZERO from packages/rest/src/rest-server.ts, and said so: those pages are NOT COVERED by that run. That file is the loud defect site, so the hand check was run instead.", "pages_read": 9, "the_five_prescribed": [ "content/docs/references/api/contract.mdx:222 — a bare alphabetical list of error codes. NOT falsified: LOOKUP_TARGET_MISSING still exists and is still the answer for a lookup/master_detail naming no target; only the user case stopped reaching it. AUTO-GENERATED besides.", "content/docs/references/api/error-code-ledger.mdx:390 — the same bare list, same reading. NOT falsified. AUTO-GENERATED.", "content/docs/references/index.mdx:385 — an export-name table naming FormFieldPublicPicker. No behavioural claim at all. NOT falsified. AUTO-GENERATED.", "content/docs/references/ui/view.mdx:186,351 — the only behavioural sentence among the five: \"Without it the route answers 403 LOOKUP_NOT_PUBLIC and the field is stripped from the rendered public form.\" That is about the ABSENCE OF publicPicker, not about target resolution, and the 403 branch is untouched. NOT falsified. AUTO-GENERATED from the spec .describe(), which this PR does not edit.", "content/docs/releases/v15.mdx:961 — names publicPicker only inside the #3022/#3036 anchor-forgery entry (server-managed fields denied on managed anchors). Unrelated to target resolution. NOT falsified, so nothing to report and nothing to edit — the releases tree is off-limits in a code PR either way." ], "four_more_pages_i_added": "The prescribed grep keys on publicPicker|LOOKUP_TARGET_MISSING, which cannot reach the seed-loader or lint halves of this PR. A second pass for pages claiming a relationship field needs an explicit reference added: content/docs/automation/flows.mdx, content/docs/concepts/metadata-driven.mdx, content/docs/data-modeling/field-types.mdx, content/docs/references/data/seed-loader.mdx. seed-loader.mdx is AUTO-GENERATED and documents only schema SHAPES (the fieldType enum lookup|master_detail|user, targetObject, targetField) — every one still accurate, and no spec schema, .describe() or export is in this diff, so that page cannot have drifted. The other three carry no falsified sentence.", "the_corroboration": "content/docs/data-modeling/field-types.mdx:323 is HAND-WRITTEN and points the other way — it documents the user type as \"a lookup specialized to the built-in sys_user object\", gives it NO reference property row at all (where lookup:315 and master_detail:337 both mark reference **required**), and every example omits it: Field.user({ label: \"Assignee\" }). The published documentation already told authors to write exactly the metadata the runtime was refusing with a 500. The docs were right and the code was wrong, which is the same conclusion IMPLICIT_REFERENCE_TARGETS states.", "generated_pages": "None regenerated, and none needed: this PR edits no packages/spec schema, .describe() or export, so every content/docs/references/** page is unchanged at its source. No hand-edit of a generated artefact was made." } }Generated by Claude Code
docs(ui/forms): the public picker's `object` row states the implicit-…
os-warren commentedon Sep 21, 2026
Contract-text:— the citation this seat'sClause-②: noowed, supplied lateSeat
domain:spec#2,os-warren·session_01UDXER3sdqfeVYpEWZs5mZx, ⏱️ 2026-09-21T02:11Z.⛔ This seat's gap, ⛔ not the dev's.
references/contract-review.mdis explicit: 「删误拒的Clause-②: no附Contract-text:引已发布契约文本,双载体同载;缺引即缺申报」. My claim declaredClause-②: noon the 「deleted refusal」 ground and carried noContract-text:line. The at-tier review (5754474832) named the omission and supplied the citation itself; this comment puts it on the record where the discipline requires it — on both carriers, this card and PR #19472.Contract-text:
packages/spec/src/data/field-value.zod.ts, theIMPLICIT_REFERENCE_TARGETSdocblock, verbatim fromorigin/main:⇒ the
500 LOOKUP_TARGET_MISSINGthis PR deletes for a{ type: 'user' }field was a refusal of metadata the published contract already declares complete. That is what makes the declarationnorather thanyesunder 「删已发布契约文本本就否定的误拒本身是no」 — and the citation is what turns that from an assertion into a declaration.nocorrect on the real diff (0 files underpackages/spec/, no key/member/export added, the #12920 alias guard preserved and pinned both ways).Generated by Claude Code
os-warren commentedon Sep 21, 2026
The
reader-label question: ⛔ NOT filed as a card, and the disagreement is recorded rather than resolved silentlySeat
domain:spec#2,os-warren·session_01UDXER3sdqfeVYpEWZs5mZx, ⏱️ 2026-09-21T02:12Z.⛔ Correcting myself: in the rework message I told the dev 「I will file it」. Re-reading the filing rule before acting, I should not, and I am saying so here rather than quietly not doing it.
The question
referenceTargetOftakes noreaderlabel, whilereferenceCarrierOftakes one precisely so a refusal 「says who could not read it」. This card's direction moves consumers onto the target arbiter, so each moved site loses its own name from theTypeError, and four existing pins were retargeted from the site label to/referenceTargetOf/in this PR.The two positions, both on the record
noted, not filed— 「the refusal still fires and still carries its prescription, no declared contract is violated, and it traps no author」, with a named successor (the next PR that moves a consumer ontoreferenceTargetOf).5754474832): recommends it be a filed follow-up card in the spec lane rather thannoted, not filed.Why this seat follows the dev
The filing rule admits three classes and no others: (a) a reproducible defect, (b) a violation of an already-declared contract, (c) an AI-metadata trap the runtime rejects or silently drops. This is none of them — nothing fails, no declared text says the refusal must name its reader, and it is not metadata. 「其余进 PR
## Acceptance notes,⛔ 不立卡」, and the dev did exactly that with a successor named, which is what the carrier test asks for.⇒ ⛔ No card filed. ⛔ And the at-tier recommendation is not discarded: it is quoted above so a later reader meets the dissent instead of re-deriving it, and ⛔ a review at tier does not override the filing classes — tier decides the verdict on the diff, not what the backlog admits.
Generated by Claude Code
os-warren commentedon Sep 21, 2026
ACCEPT — PR #19472 reviewed at tier, corrected, and ENQUEUED
domain:specseat 2 (座位贴 #18549),os-warren·session_01UDXER3sdqfeVYpEWZs5mZx, R2. ⛔ 本席未合并、未批准、未绕队:落地动作只有 ready + auto-merge,合并由队列做。三条落地前置,逐条摆出来
① 达档复核 PASS 在案 — PR #19472 评论
5754774179:## Contract review·Served-tier: 163/163 CONTRACT_REVIEW_TIER·Head-sha: 01b387e05b08bc34099f583410c6b1a5e22df9ae· VERDICT: PASS。隔离子代理渲染,本席逐字采纳,⛔ 未润色。model盖章,0 个其它,该值即CONTRACT_REVIEW_TIER。⛔ 未用get_session(它量的是派发会话)。⭐ 本席自纠一处:
Served-tier:第一次写成163/163, then \CONTRACT_REVIEW_TIER`,被--pair以 **C7** 退回 —— 活形是N/N空格接常量名,, then` 会被读成值。模板帮助行里的「75/75, then the constant」是描述不是字面量,是本席读错。两条记录(本卡与 PR #19480 的 FAIL 记录)已同笔改成活形并回读:各一条页脚、前缀未动。② 双载体一致 —
node scripts/pm/check-clause2-carriers.mjs --pair 19472= exit 0(修正后复跑)。本对无needs:contract-review可剥:Clause-②: no,两侧本就未挂;check-widening-tells对 13 个文件全 NOT MEASURED(仪器不覆盖,⛔ 不读作干净),复核 ①.2 用四条 pin 与控制独立回答了同一个问题。③ CI 全绿,⛔ 非必查子集 — head
01b387e0上 35 个 check 名,最新一条/名:30 success · 4 skipped · 0 failure · 0 pending。Check Changeset与Governed Surface Queue Guard是本席两次状态写之后重新读的,不是写之前的读数。node scripts/pm/check-governed-merges.mjs --pr 19472= exit 0:13 个路径零命中受管面册 ⇒ 普通队列落地;642 改动行 ≤ 5000。本席按复核建议改了 PR 正文一行(dev 的一次写已用尽,正文归席位)
复核 ①.5 指出
## Acceptance notes里 「no other module readsGraphField.reference」是假的。本席自己在 head 上复测,⛔ 未凭复核员转述:object-graph.ts:405的RELATIONSHIP_FIELD_TYPES闸门在中间 hop 上(hop-untargeted),不在叶子上;object-graph.ts:413逐字if (obj.names.has(leaf)) return { kind: 'ok', object: current, field: leaf, meta: obj.fields.get(leaf) };—— 叶子不论类型都带meta返回;validate-preset-comparands.ts:450逐字return verdict?.kind === 'ok' ? strName(verdict.meta?.reference) : undefined;—— 它就是在读。⇒ 那句已撤回并替换为实测后果:
user选择器的筛选行现在绑到sys_user(正是路由查的那个对象,正确);text带野生 carrier 的选择器筛选行绑到空。两者都是拉回已声明契约(reference的 describe 限于 lookup/master_detail,forms.mdx:238限选择器为 lookup / master-detail /user),落在文档面之外的形状上 ⇒ ⛔ 不是放宽,Clause-②: no不受影响。裸 REST
PATCH /pulls按改侧处方不送页脚;回读 12998 = 送出 12940 + 58,页脚恰一条、无 session-URL 形、Clause-②:行仍在行首。落地证据(两读数,⛔ 不凭属性字段)
ready_for_review2026-09-21T02:57:20Z →added_to_merge_queue2026-09-21T02:58:35Zgh-readonly-queue/main/pr-19472-d00692f5…存在,排在pr-19471之后(堆叠链,⛔ 不是停滞)⛔ 尚未落地。 合并证明要的是
origin/main上按内容 grep,⛔ 不是merged布尔、⛔ 不是 head 祖先。本卡跟到 MERGED 再关;卡态与 assignee 此刻不动。复核留下的两件,⛔ 本轮不做
validate-preset-comparands对user情形的 pin —— 复核明写 recommended, not required。⛔ 不扩本 PR。referenceTargetOf不收reader标签」的后继者指针原样保留;复核判其对本 PR sound,异议双方位置已在5754514878留档。Reading taken 2026-09-21T02:59Z.
Generated by Claude Code
os-warren commentedon Sep 21, 2026
LANDED — PR #19472 is in
mainas2b321a4Closing record from seat
domain:spec#2,session_01UDXER3sdqfeVYpEWZs5mZx. Both required readings taken; ⛔ neither themergedboolean alone nor head ancestry was accepted as proof.Reading 1 — queue membership (timeline).
ready_for_review2026-09-21T02:57:20Z →added_to_merge_queue2026-09-21T02:58:35Z →removed_from_merge_queue2026-09-21T03:14:12Z onCI_FAILURE→ re-queuedadded_to_merge_queue2026-09-21T03:16:27Z →removed_from_merge_queue2026-09-21T03:45:25Z →merged2026-09-21T03:45:26Z at2b321a46d4f8→closed. It went through the queue; ⛔ not direct-merged, ⛔ not bypassed.Reading 2 — content on
origin/main, aftergit fetch origin main.origin/main=2b321a46d4f8, subjectfix(spec-consumers): the implicit-target census — 17 sites judged, four repaired through referenceTargetOf (#19289) (#19472). By content, not by subject:referenceTargetOfnow reads 4 in each ofpackages/lint/src/object-graph.ts,packages/metadata-protocol/src/seed-loader.tsandpackages/rest/src/rest-server.ts. ⭐ LIT CONTROL, same instrument, same corpus:referenceCarrierOfstill reads 1 inobject-graph.ts— so the instrument reads these files and discriminates between the two arbiters; the 4s are a reading, not a grep that matches anything.The dequeue in the middle, and why it was not this PR's
The queue dropped this PR once on
CI_FAILURE:Temporal Conformance (live PG + MySQL)in queue build35555936360. Four readings said it was not this PR's — green on this head01b387e0, green on the queue's based00692f, green ×3 on the prior tip32b5831, and the failure named a SQL-driver row-byte-budget case while this diff touches no driver. One re-queue was spent on that reading (record5754926086), and it paid: on the re-queued buildTemporal Conformancereadcompleted success. ⛔ No test was skipped, disabled or quarantined; ⛔ no empty commit; ⛔ no second re-queue.The round, end to end
97b689b7; the at-tier review FAILED it (5754474832) on one ground —content/docs/ui/forms.mdx:260stated the reference-target rule by its inputs («itsreferencekey, and only that key»), which this very change made false.01b387e0. The dev did not merely append a clause: it split the neighbouring refusal sentence, because half of it (500 LOOKUP_TARGET_MISSING) had become type-dependent. The re-review re-measured that againstreferenceTargetOfand the route and found every sentence true — PASS (5754774179,Served-tier: 163/163 CONTRACT_REVIEW_TIER).--pair 19472= exit 0; ③ 35 check names green or skipped, 0 failures, 0 pending, re-read after the last state write.check-governed-merges --pr 19472= exit 0 (0 of 13 paths governed; 642 changed lines ≤ 5000).Two seat self-corrections on the record
Served-tier:spelling. First written163/163, then \CONTRACT_REVIEW_TIER`;--pairrefused it with **C7** — the live form isN/N**space** then the constant's name, and, then` reads as the value. The template's help line 「75/75, then the constant」 is a description, not a literal, and this seat misread it. Both records (this one and PR fix(lint): give the liveness walk a seam of its own, and report a ledger that could not be read #19480's FAIL) were corrected in place and read back.GraphField.reference」 as wrong. ⛔ Not adopted on the reviewer's word — re-measured here:object-graph.ts:405'sRELATIONSHIP_FIELD_TYPESgate is on the intermediate hops, the leaf returnsokwith itsmetawhatever its type (:413), andvalidate-preset-comparands.ts:450reads it. The sentence was withdrawn and replaced with the measured consequence before landing, so the squash message does not carry it.Label state
pm:dispatchedremoved in the same act as this record (read-back MATCHES:priority:p1,domain:spec).domain:specand the grading label stay — ownership is not state. GitHub closed the card on the closing keyword,state_reason=completed.Left open on purpose — ⛔ neither filed nor swept
validate-preset-comparandsfor theusercase: the review marked it recommended, not required.referenceTargetOf-takes-no-reader-label successor pointer stands; both positions are recorded at5754514878.Reading taken 2026-09-21T03:46Z.
Generated by Claude Code
fix(spec-consumers): the implicit-target census — 17 sites judged, fo…