Skip to content

fix(spec-consumers): the implicit-target census — 17 sites judged, four repaired through referenceTargetOf (#19289) - #19472

Merged
os-warren merged 6 commits into
mainfrom
claude/issue-19289-implicit-reference-target-census
Sep 21, 2026
Merged

os-warren merged 6 commits into
mainfrom
claude/issue-19289-implicit-reference-target-census

Conversation

@os-warren

@os-warren os-warren commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes #19289

Clause-②: no

IMPLICIT_REFERENCE_TARGETS (packages/spec/src/data/field-value.zod.ts) declares a user field's target "a CONSTANT OF THE TYPE" and metadata authored without reference "fully specified, not under-specified". Two arbiters answer two different questions — referenceCarrierOf what the carrier says, referenceTargetOf what the field points at — and for user only the second matches that text. #18550 standardised a population of readers on the first. This is the census of that population.

The arbiters are NOT edited. packages/spec/src/data/field-value.zod.ts is untouched; the diff is packages/lint, packages/metadata-protocol, packages/rest and their tests.

The census was re-measured, and it had moved

Taken on 8f6d831 with no pathspec, excluding tests and the arbiters' own module: 17 referenceCarrierOf vs 8 referenceTargetOf — ⛔ not the card's 16 vs 7. Class (A) is 8 (card: 7); class (B) is 9 (unchanged). Both deltas landed after the card's census:

delta commit landed
objectql/src/engine.ts:9077 — a NEW class-(A) carrier site (buildSummaryIndex) 875e9ad (#19293) 2026-09-20T10:37Z
plugin-audit/src/audit-writers.ts:429 — raw read → target, so the target count rose 5636641 (#19264 / PR #19285) 2026-09-20T10:09Z

The objectql/engine.ts line numbers also drifted (13113 → 13204, 13568 → 13659). The card's instrument trap reproduces on this tree, with REFERENCE_FIELD_TYPES as the known-present needle: bare packages/**/src/**/*.ts0 files, :(glob)…2, no pathspec → 2.

Per-site verdict — all 17

The deciding question is each site's own type gate: where it excludes user, the carrier is the target for the question that site asks, and the site stays on the carrier.

Class (A) — handed a real field definition (8)

# site own type gate verdict
A1 metadata-protocol/src/seed-loader.ts:712 lookup | master_detail | useradmits 🔴 DEFECT ① (silent) — repaired
A2 rest/src/rest-server.ts:10914 none — any field the picker names 🔴 DEFECT ② (loud, 500) — repaired
A3 cli/src/commands/doctor.ts:726 lookup only ✅ not a defect — type-gate exclusion
A4 cli/src/commands/doctor.ts:930 lookup only ✅ not a defect — type-gate exclusion
A5 objectql/src/engine.ts:9077 (new) master_detail | lookup ✅ not a defect — type-gate exclusion
A6 objectql/src/engine.ts:13204 master_detail | lookup ✅ not a defect — type-gate exclusion
A7 objectql/src/engine.ts:13659 master_detail | lookup ✅ not a defect — type-gate exclusion
A8 services/service-analytics/src/plugin.ts:748 lookup | master_detail ✅ not a defect — pre-judged on the card

Class (B) — synthesize { reference: … }, discarding type (9)

# site own type gate verdict
B1 lint/src/data-model-rules.ts:250 (refOf) 4 callers: RELATIONSHIP_TYPES = {lookup, master_detail}, OPTION_FIELD_TYPES, summary ✅ not a defect
B2 lint/src/object-graph.ts:242 (graphFieldOf) none; consumer RELATIONSHIP_FIELD_TYPES admits user 🔴 DEFECT ③ (silent, widest) — repaired
B3 lint/src/validate-expressions.ts:395 master_detail only ✅ not a defect
B4 lint/src/validate-field-consumers.ts:560 none 🔴 DEFECT ④ (silent) — repaired
B5 lint/src/validate-object-references.ts:306 RELATIONSHIP_TARGET_FIELD_TYPES admits user 🟡 latent misread — aligned, no output change
B6 lint/src/validate-object-references.ts:329 (action param) ActionParamSchema.type is optional ✅ not a defect — measured, see below
B7 lint/src/validate-security-posture.ts:292 (refOf) CBP_TIERS = master_detail / lookup ✅ not a defect
B8 lint/src/validate-sharing-rule-enforceability.ts:267 master_detail only ✅ not a defect
B9 verify/src/derive.ts:148 RELATIONAL = lookup / master_detail / tree — excludes user ✅ not a defect

Class (B) was smaller than the card's framing, not larger. Seven of the nine are type-gated away from user; only B2 and B4 needed the pass-the-field-through repair. Three of the nine (B3, B7, B9) additionally 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 — folding that read into a helper call would disarm that scan silently. Those three are untouched.

B6 — the site the tests refused, and why it is in the table as judged

I initially swapped the action-param site too. reference-integrity-suite.test.ts went red: object-reference-unknown vanished from the suite's findings entirely. The cause is that a param is not a field definition — ActionParamSchema.type is optional, because 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 that declares no type, and the corpus param { name: 'owner', reference: 'user' } (user being the classic miss for sys_user) stopped being checked. The swap deleted a live check.

Nothing was owed there in the other direction either: a carrier-less user param already produced no finding, because check returns early on absence. Reverted, verdict recorded as not-a-defect, with a regression guard kept beside it.

The four repairs

  1. metadata-protocol seed-loader — SILENT, and it stored a wrong value. A {type:'user'} field with no reference contributed no dependsOn edge and never reached references, so its natural key was written verbatim into a column holding a record id.
  2. rest public-form picker — LOUD. A publicPicker on a spec-complete {type:'user'} field answered 500 LOOKUP_TARGET_MISSING. Now 200 over sys_user.
  3. lint object-graph.graphFieldOf — SILENT and widest. The slice feeds resolveFieldPath, whose RELATIONSHIP_FIELD_TYPES admits user; a carrier-less one answered hop-untargeted, which isUnjudgeable treats as "the graph could not answer" and describeFieldPathVerdict renders as nothing. Every rule in the package that resolves a field path therefore stopped judging any path through such a field — the failure mode isUnjudgeable's own docblock says this family exists to end ("a missed report is silence").
  4. lint validate-field-consumers.walkObject — SILENT. The displayField consumer edge onto sys_user was never recorded, so a field that column displays was reported consumed by nobody. ⚠️ Materiality stated honestly: recordable only where sys_user is compiled into the linted stack.

Nothing widens. user is the only member of IMPLICIT_REFERENCE_TARGETS; lookup / master_detail / tree with an absent carrier still name nothing, pinned at every repaired site. The unreadable-carrier refusal is unchangedreferenceTargetOf reads the carrier through referenceCarrierOf before it judges the type, so #13053/#18550's TypeError still fires everywhere it fired.

Not a re-widening of #12920. A control pins it: a user field spelling referenceTo: 'zzz_aliased_object' resolves sys_user from the type and is never asked for the aliased name; a lookup spelling the same alias still resolves nothing and still answers 500.

Evidence

  • @objectstack/lint106 files / 4019 tests passed
  • @objectstack/metadata-protocol184 passed, 3 skipped / 2627 passed, 19 skipped
  • @objectstack/rest194 files / 3254 passed, 1 skipped
  • typecheck green on all three (incl. check:test-typecheck for lint and rest)
  • Re-run in full after merging origin/main; the closure was rebuilt first because packages/spec moved on main's side
  • Gates: dispatch-gates --ran reconciles 63 derived / 60 run / 3 NOT MEASURED / 0 UNRUN. The three are PREREQUISITE NOT MET (exit 3, ⛔ not a pass): check:dual-build-cjs-loads and check:type-check-debt need a whole-repo build; check-plugin-teardown-shape --self-test cannot reach a commit-pinned fixture on a shallow clone.
  • pnpm lint, narrowed and declared: ① the population is read from eslint.config.mjs, which states in its own comment that this repo "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file" ⇒ this diff cannot move the verdict on any file it does not touch; ② --format json reports 37 files linted, 0 errors, 0 warnings (a superset — the three-dot set includes what the merge brought); ③ measured at 97b689b.
  • Control-character self-scan over the 12 changed files: 0 hits.

Attribution

Authored by Claude Code session session_01UDXER3sdqfeVYpEWZs5mZx. Recorded here in prose deliberately: this body was edited on the raw REST edit side, which appends the BARE footer form carrying no session id (pm-dispatch references/platform-readings.md:350).

Acceptance notes

  • Noted, not filed: referenceTargetOf takes no reader label, while referenceCarrierOf does precisely so "the message says who could not read it". Every site moved onto the target arbiter therefore loses its own name from the refusal, and four existing pins were retargeted from the site label to /referenceTargetOf/ here. The error class, the offending shape and the prescription stay asserted, and this matches the two landed repairs ([finding] ApprovalService.resolveLookupFields requires an EXPLICIT reference on user fields, which spec declares fully specified without one — implicit-target metadata is silently invisible to inbox enrichment #19198, [finding] plugin-audit: planTrackedLookupReads and the milestone planner both admit user then require the materialized reference — a trackHistory'd implicit-target field is planned for nobody and the timeline shows the raw id (#19198's shape, one package over, TWO sites) #19264). It is a diagnostic-fidelity question about the arbiter's signature, ⛔ not a defect — fixing it would edit packages/spec/src/data/field-value.zod.ts and change this card's landing path. Successor: the next PR that moves a consumer onto referenceTargetOf.
  • Noted, not filed: 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. ⚠️ Corrected by the at-tier contract review (5754774179), re-measured on this head by the seat: resolveFieldPath's RELATIONSHIP_FIELD_TYPES gate is on the intermediate HOPS (object-graph.ts:405, hop-untargeted), ⛔ not on the leaf — the leaf comes back ok carrying its meta whatever its type (object-graph.ts:413) — and validate-preset-comparands.ts:450 DOES read it (verdict?.kind === 'ok' ? strName(verdict.meta?.reference) : undefined). So «no other module reads GraphField.reference» is false on the tree, and the sentence is withdrawn. What actually follows: a user picker's filter rows now bind to sys_user, which is the object the route queries — correct; a text-plus-stray-carrier picker's filter rows bind to nothing where they bound to the stray object. The same shape reaches the REST picker, which has no field-type gate before referenceTargetOf (rest-server.ts:10935) and whose FieldSchema.reference carries no non-reference-type refusal, so {type:'text', reference:'foo'} behind a publicPicker now answers 500 LOOKUP_TARGET_MISSING where it used to search foo. Both are pull-backs to the declared contract — the reference describe text scopes it to lookup/master_detail fields, and forms.mdx:238 scopes pickers to lookup / master-detail / user — on a shape outside the documented surface, so ⛔ neither is a widening and Clause-②: no is unaffected. Strictly more correct: a text field's stray carrier is not a target.

维护者速读(草稿)

改了什么user 字段的目标由类型常量决定(sys_user),不必作者手写 reference。本 PR 普查了全部 17 个读「目标」的调用点,逐个判定该问哪个仲裁器,修了其中 4 个真缺陷,其余 13 个判为「读载体本来就对」并留档。

为什么改 — 契约白纸黑字写着这种元数据「已完整声明」,但四处消费者把它当作「没写目标」。后果:公开表单上点开「负责人」选择器直接 500 错误页;种子数据把人名原样写进本该存记录 id 的列;lint 静默放弃对这类字段路径的全部校验。

风险与代价(含回滚) — 风险低。没有放宽任何拼写:别名仍旧拒收,不可读的载体仍旧抛错,lookup/master_detail 缺目标仍旧当作没目标。回滚即 revert 本 PR,无数据迁移、无存量数据改写。

席位意见(留空,待席位定稿)

你要做的 — 确认一件事即可:本 PR 没有改动 packages/spec 的两个仲裁器,所以不触发合流闸的路径腿。其余按常规复核。


Generated by Claude Code

…target question through `referenceTargetOf` (#19289)

`IMPLICIT_REFERENCE_TARGETS` declares a `user` field's target a CONSTANT OF THE
TYPE and metadata authored without `reference` "fully specified, not
under-specified". Sites that answered from `referenceCarrierOf` read such a
field as targetless.

Repaired (a site reached by `user`, i.e. no type gate or a gate admitting it):

- `metadata-protocol` seed-loader `buildDependencyGraph` — SILENT: no
  `dependsOn` edge, never on `references`, natural key written verbatim.
- `rest` public-form lookup picker — LOUD: `500 LOOKUP_TARGET_MISSING` on a
  spec-complete field.
- `lint` `object-graph.graphFieldOf` — SILENT and widest: `hop-untargeted` is
  `isUnjudgeable`, so every rule resolving a path through a `user` field
  stopped judging it.
- `lint` `validate-field-consumers.walkObject` — SILENT: the `displayField`
  consumer edge onto `sys_user` was never recorded.

Aligned (latent misread, no output change today):

- `lint` `validate-object-references` field target and action param target.

Judged and left on the carrier: the eleven sites whose own type gate excludes
`user`, so the carrier IS the target for the question they ask.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
…fects (#19289)

One pin per repaired site, each asserting the shape the defect actually had:

- seed-loader — the `references` ROW, ⛔ not a throw: the defect was silent.
- REST picker — `200` over `sys_user` AND `findData`'s object, so a 200 over
  the wrong object cannot pass.
- object-graph — `isUnjudgeable` flips false, plus a MISS through the implicit
  hop now being reported, so judgement is proven to have resumed.
- validate-field-consumers — the displayed field leaving the findings list.

Every block carries the same three controls: the two legal spellings agreeing,
`lookup`/`master_detail` still naming NO target (`user` is the only member of
`IMPLICIT_REFERENCE_TARGETS`), and an unreadable carrier still refusing — the
implicit target is not a fallback that swallows #13053/#18550's throw. The REST
block adds the #12920 control: a `user` field spelling `referenceTo` resolves
`sys_user` from the type and never the alias.

Four reader-label pins move to `referenceTargetOf`, which is the arbiter these
sites now ask; the error class, the offending shape and the prescription stay
asserted.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
…m declares no type

Reverts one of the six census edits, on measurement rather than reasoning.
`ActionParamSchema.type` is optional (a field-backed param inherits its type at
runtime, not visible at parse time), so the target question is unanswerable
from a param alone: `referenceTargetOf` answered `undefined` for every param
that declares no type, and `object-reference-unknown` vanished from
`reference-integrity-suite.test.ts` entirely.

Nothing was owed at this site in the other direction either — a carrier-less
`user` param already produced no finding, because `check` returns early on
absence. Census verdict corrected to NOT-a-defect, with the regression guard
kept beside it.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
Clause-②: no — it deletes a mistaken refusal of metadata the published
contract already declares complete, not a widening. No key, alias or spelling
is newly accepted anywhere.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Sep 21, 2026
@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

4 anchor(s) derived from 3 changed package(s); no hand-written page names any of them. ⚠️ 1 changed file(s) yielded no anchor (packages/rest/src/rest-server.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/rest/src/rest-server.ts) — pages documenting those are invisible to this run
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (literal, 37 pages)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 28 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 841ed38947850738cf6ac12388abbd7029a2a7b9packageMentionDocs.

Which tree this was computed on

This run read content/docs from b5702e3acdc82c0e194a07b1622b6c21f6bdb2d3 — the merge of head 01b387e05b08bc34099f583410c6b1a5e22df9ae into base 841ed38947850738cf6ac12388abbd7029a2a7b9, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin b5702e3acdc82c0e194a07b1622b6c21f6bdb2d3 && git checkout b5702e3acdc82c0e194a07b1622b6c21f6bdb2d3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 841ed38947850738cf6ac12388abbd7029a2a7b9 01b387e05b08bc34099f583410c6b1a5e22df9ae && git checkout -B drift-repro 841ed38947850738cf6ac12388abbd7029a2a7b9 && git merge --no-ff 01b387e05b08bc34099f583410c6b1a5e22df9ae

node scripts/docs-audit/affected-docs.mjs --json 841ed38947850738cf6ac12388abbd7029a2a7b9

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 97b689b74963e076cacd65b8e5128a131307be76

Isolated at-tier review for the domain:spec lane (card #19289, priority:p1). Every reading below was taken first-hand from a detached worktree pinned to the head sha and from the GitHub REST API; the dev's report 5754300080 and the PR body were read as claims to test, not adopted. Where a reading needed installed dependencies it is named NOT MEASURED, never counted as a pass.

① Derived judgments

1. The census, re-taken. git grep over packages/ at 8f6d831 (the dev's census tree), tests and the arbiters' own module excluded, no pathspec: 17 referenceCarrierOf call sites, 8 referenceTargetOf — the PR's numbers, not the card's 16/7. On the head the split is 12/13, which is exactly 4 repairs + 1 alignment moved. Attribution verified: packages/objectql/src/engine.ts:9077 (buildSummaryIndex) was added by 875e9ad (#19293, 2026-09-20T10:37Z), after the card's 09:48Z census — that is the 16→17 and the class-(A) 7→8. packages/plugins/plugin-audit/src/audit-writers.ts:429 became a referenceTargetOf site in 5636641 (#19264, 10:09Z) — that is the 7→8 on the target side. One wording slip, counts unaffected: at 5636641^ that file read field.reference raw (lines 472/527/629/693), so it was never a referenceCarrierOf site; "carrier → target" in the PR table should read "raw read → target". The card's 16 never included it, so both sides reconcile.

2. All 17 sites judged, each verdict checked against the site's own gate on the head tree. The failure mode I was looking for — a site waved through as type-gated while its gate admits user — does not occur:

  • A3/A4 cli/src/commands/doctor.ts:726,:930field?.type !== 'lookup' → skip. Excludes user (and master_detail). Verdict holds.
  • A5/A6/A7 objectql/src/engine.ts:9077,:13204,:13659type !== 'master_detail' && type !== 'lookup' → skip. Verdict holds.
  • A8 service-analytics/src/plugin.ts:748field.type === 'lookup' || 'master_detail'. Verdict holds.
  • B1 lint/src/data-model-rules.ts:250 (refOf) — four callers at :567/:710 (RELATIONSHIP_TYPES = lookup, master_detail), :633 (OPTION_FIELD_TYPES), :784 (type === 'summary'). No caller admits user. Verdict holds.
  • B3 lint/src/validate-expressions.ts:395def.type !== 'master_detail' → continue. Holds.
  • B7 lint/src/validate-security-posture.ts:292 — callers gated by CBP_TIERS (master_detail / required lookup) and firstMasterDetailField (master_detail). Holds.
  • B8 lint/src/validate-sharing-rule-enforceability.ts:267f.type === 'master_detail'. Holds.
  • B9 verify/src/derive.ts:148RELATIONAL at :26 = lookup, master_detail, master-detail, masterdetail, tree; caller at :232 gates on it. Excludes user. Holds.
  • B2 lint/src/object-graph.tsRELATIONSHIP_FIELD_TYPES (:87) DOES admit user; a carrier-less one answered hop-untargeted, which isUnjudgeable (:510) swallows. Real defect; repair correct.
  • B4 lint/src/validate-field-consumers.ts — no gate. Real defect; repair correct.
  • B5 lint/src/validate-object-references.tsRELATIONSHIP_TARGET_FIELD_TYPES (:100) admits user; check admits sys_user at rung ③ (isPlatformProvidedObjectName, and sys_user is in PLATFORM_PROVIDED_OBJECT_NAMES at platform-object-names.ts:93), so the resolved target produces no finding — "aligned, no output change" is true.
  • A1 seed-loader, A2 REST picker — gates admit user / none. Real defects; repairs correct.

3. The measured reversal (B6) is real and the guard guards. Commit 4d3dc05428 restores referenceCarrierOf({ reference: param.reference }, …) at what is now validate-object-references.ts:360; the head reads that. ActionParamSchema.type is FieldType.optional() (spec/src/ui/action.zod.ts), so referenceTargetOf(param) returns undefined for every param declaring no type — the corpus param { name: 'owner', reference: 'user' } at reference-integrity-suite.test.ts:142 is exactly that shape. The new pin "a param with NO type still has its carrier checked" expects one OBJECT_REFERENCE_UNKNOWN finding for { name: 'owner', reference: 'zzz_nope' }; with the swap applied it would get zero, so the guard is discriminating. Verdict corrected to not-a-defect is right, and nothing is owed the other way (absence returns early in check).

4. Clause-②: no is correct — this is the deletion of a mistaken refusal, not a widening. Tested on the real diff, not the prose: (a) no file under packages/spec/ is in the diff (0 of 12); (b) the whole source diff is five import/arbiter swaps plus comments, tests and one added changeset — no key, enum member, export or registration is added (check-widening-tells.mjs --declaration no reports all 12 files NOT MEASURED because no declared surface covers them, so that instrument says nothing either way; the hand reading stands); (c) the accept set of AUTHORED metadata is unchanged — FieldSchema's own refinement (data/field.zod.ts:2042-2049) requires a non-empty reference only for lookup and master_detail, so { type: 'user' } without one already parsed; Field.user() (field.zod.ts:2635) writes reference: 'sys_user' itself and takes no target argument; the exported referenceTargetOf docblock says undefined is returned "only when the field genuinely names no target: a non-reference type, or a lookup/master_detail/tree with no reference". That is the published contract text a no of this shape must cite (「删已发布契约文本本就否定的误拒本身是 no」). (d) The refusal being deleted is not one of the three deliberate classes (ruling guard, security refusal, fail-closed): the #12920 ruling guard (alias retirement) is preserved and pinned in both directions (user + referenceTo alias resolves from the type, lookup + alias still 500s); the 500 was a consumer misreading absence, and the same object was already served under the same opt-in for the explicit spelling (pre-existing pin "the canonical STRING carrier still resolves and answers 200" over sys_user). More requests succeed, but every one of them is a request the explicit spelling of the identical metadata already had answered.

Carrier discipline, for the seat rather than the dev: references/contract-review.md says a mistaken-refusal no carries a Contract-text: citation on both carriers. The PR body and the changeset quote the text under Clause-②: no without the label; the claim comment 5753775666 carries neither. Not a defect in the diff; this record supplies the citation.

5. Seed-loader accept/reject behaviour, stated precisely. For a { type: 'user' } field with no reference, a seed value now takes the same path the explicit spelling always took: id-shaped values are kept verbatim (looksLikeInternalId), a natural key is resolved against sys_user.name, and an unresolvable natural key now drops the WHOLE record with an error log (seed-loader.ts ~:1380) where it used to be written verbatim into the FK column. Correct per contract and the card's own defect statement, and loud where it was silent. The changeset says "resolves" but not "or drops the record loudly on an unresolvable key" — recommend one clause, since that is the sentence an upgrading seed author greps.

6. Lint object-graph, reach checked. Where sys_user is not compiled into the linted stack the hop now lands on unknowable / object-not-in-stack (:407), still unjudgeable, so no new finding class appears there; where it is, judgement resumes — the intended repair. Injected anchors (owner_id) still short-circuit at injected-hop before reference is consulted, so the widening that comment warns about is not made here. The "noted, not filed" { type: 'text', reference: 'foo' } change is as described: resolveFieldPath gates on type before reading .reference, and no other reader of GraphField.reference exists.

7. Docs — the ground for the verdict. content/docs/data-modeling/field-types.mdx says what the PR attributes to it: the user section (heading :323, prose :324) calls it "a lookup specialized to the built-in sys_user object", has no property table and so no reference row, while lookup (:315) and master_detail (:337) mark reference required, and all three examples are Field.user({ … }) with no target. Confirmed hand-written (scripts/docs-audit/handwritten-docs.json:76).

But the dev's docs verdict — "NO page is falsified, and NO docs edit is owed", from 9 pages read — is wrong, because the 9 do not include content/docs/ui/forms.mdx, the hand-written page (handwritten-docs.json:208) that documents the exact route this PR changes, and which matches the dev's own prescribed grep key on six lines (:241, :250, :255, :260, :285, :287). Line :260, the object row of the publicPicker table, states the resolution rule by its inputs: omit object and "the server resolves the target from the field's own definition on the parent object: its reference key, and only that key". Before this PR that sentence was literally true of the runtime (a user field without reference answered 500). After it, the route also reads type and supplies sys_user for a user field — the sentence is now false on the page's own terms, for the one field type the page names at :238 as picker-eligible. This is precisely the class the Docs Drift Check comment on this PR warned could not be detected mechanically and had to be re-read by hand for rest-server.ts; the hand check was the compensating control and it missed the page. One sentence closes it (e.g. "its reference key — or, for a user field, the type's own constant sys_user — and no other key"); the 500 LOOKUP_TARGET_MISSING row at :287 reads correctly once :260 is right.

② Semver level

patch on @objectstack/metadata-protocol, @objectstack/lint, @objectstack/rest — all three published (private: false, 17.4.0), all three change behaviour, no other package's behaviour moves. patch is right for a 500→200 here because the declared accept set does not move (①.4): it is a bug fix against the published contract, which AGENTS.md's checklist files as patch, and it is consistent with Clause-②: no (a yes would need at least minor). The changeset is ADDED, not edited, so ruling batch #158 item 1 was not consulted (I also could not locate that batch text in the tree; stated so nothing is inferred from it). Recommended wording addition per ①.5; not a level change.

③ Boundary flags

  • Security / permission boundary — contact, judged: no new authorization surface. The picker's envelope is unchanged: field must carry an author-declared publicPicker (else 403 LOOKUP_NOT_PUBLIC); server-managed anchors are refused by NAME before any resolution (安全:公开表单(publicFormGrant)提交绕过 owner_id 属主守卫 → 匿名可伪造属主 #3022, unchanged); the query runs as { permissions: ['guest_portal'], anonymous: true } with offset pinned to 0, maxResults capped at 50 and at most 5 projected fields, all unchanged. The set of objects an anonymous visitor can reach does not grow — sys_user was already served under this exact envelope when the same field spelled reference: 'sys_user' (pinned before this PR). What grows is the set of metadata spellings that reach it, and the docs at forms.mdx:238 already describe user fields as picker-eligible opt-ins. NOT MEASURED: what findData returns from sys_user under guest_portal at runtime — unchanged by this diff and needs a booted server.
  • Published-contract change: none in packages/spec; one hand-written docs sentence falsified and not updated (①.7) — the FAIL ground.
  • Gate weakening: none. Four existing pins retargeted from a site label to the arbiter's name still assert error class, offending shape and prescription; specificity of the "who" is lost, which is the open question below, not a gate change.
  • New required gate / hook / ratchet: none. New runtime dependency: none (imports move within @objectstack/spec/data).
  • Open question (reader label), answered: option A is acceptable for THIS card. The refusal still fires with class, shape and prescription; the choice matches [finding] ApprovalService.resolveLookupFields requires an EXPLICIT reference on user fields, which spec declares fully specified without one — implicit-target metadata is silently invisible to inbox enrichment #19198 and [finding] plugin-audit: planTrackedLookupReads and the milestone planner both admit user then require the materialized reference — a trackHistory'd implicit-target field is planned for nobody and the timeline shows the raw id (#19198's shape, one package over, TWO sites) #19264; B edits packages/spec/src/data/field-value.zod.ts and changes the landing path of a p1. But B should be a filed follow-up card in the spec lane, not "noted, not filed": it is a reproducible diagnostic regression that grows with every site moved, and four pins have already been generalised by it.
  • CI on this head, read from check-runs at 02:04Z: all seven required contexts — Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guardconclusion: success. Check Changeset (advisory) failed on the two pull_request runs that coincide with the PR-open and the needs:contract-review label event and passed on the two after the body edit and the unlabel — the step is "Guard against accidental major bumps"; its log host is refused by this session's egress policy, so the cause is NOT MEASURED. NOT MEASURED here: the package test suites, typecheck and dispatch-gates (no node_modules on the shared checkout — prerequisite not met, not a pass); check-widening-tells (no declared surface covers the 12 files).

What closes this FAIL: the one-sentence correction at content/docs/ui/forms.mdx:260 on the same branch (and, recommended, the seed-drop clause in the changeset). Everything else above is judged PASS-worthy as it stands; a re-review on the new head is owed by the lane rule regardless.

Implemented-by: claude/issue-19289-implicit-reference-target-census
Reviewed-by: session_01UDXER3sdqfeVYpEWZs5mZx

VERDICT: FAIL


Generated by Claude Code

…target case (#19289)

The `object` row documented the resolution rule BY ITS INPUTS — "its
`reference` key, and only that key" — which was true of the runtime before this
branch and is false after it: for a `{ type: 'user' }` field the target comes
from the type's own constant `sys_user`, never from the carrier.

That is the class the Docs Drift Check declares it structurally cannot see: a
page stating a rule by its inputs shares no identifier with the emitter that
implements it, so no anchor links `rest-server.ts` to this page.

Minimal correction, one row. What stays, because it is all still true: the
`referenceTo` / `target` / `options.objectName` alias is still never resolved
(this branch pins that in both directions), the carrier is still read through
the one accessor, and `500 LOOKUP_TARGET_MISSING` is still the answer for a
`lookup` / `master_detail` naming no target. What the row now separates is the
alias being ignored from what answers next, which differs by type.

The changeset gains the clause an upgrading seed author greps: a `user` field's
unresolvable natural key now DROPS the whole record, counted, reported and
logged at `error`, where it used to be written verbatim.

Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract-text: — the citation this seat's Clause-②: no owed, supplied late

Seat domain:spec#2, os-warren · session_01UDXER3sdqfeVYpEWZs5mZx, ⏱️ 2026-09-21T02:11Z.

This seat's gap, ⛔ not the dev's. references/contract-review.md is explicit: 「删误拒的 Clause-②: noContract-text: 引已发布契约文本,双载体同载;缺引即缺申报」. My claim declared Clause-②: no on the 「deleted refusal」 ground and carried no Contract-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, the IMPLICIT_REFERENCE_TARGETS docblock, verbatim from origin/main:

Reference types whose target object is FIXED BY THE TYPE rather than chosen by the author, mapped to that target.

user is the only member: field.zod defines it as "a lookup specialized to the sys_user system object … target fixed to the sys_user system object", and the Field.user() builder — unlike Field.lookup(reference, …) / Field.masterDetail(reference, …) — takes NO target argument and writes reference: 'sys_user' itself. The target is a CONSTANT OF THE TYPE, so reference on a user field materializes that constant; it does not supply it. Metadata authored without it (hand-written JSON, an AI author, a Studio form) is fully specified, not under-specified.

⇒ the 500 LOOKUP_TARGET_MISSING this PR deletes for a { type: 'user' } field was a refusal of metadata the published contract already declares complete. That is what makes the declaration no rather than yes under 「删已发布契约文本本就否定的误拒本身是 no」 — and the citation is what turns that from an assertion into a declaration.

⚠️ Recorded as a seat-side process correction, ⛔ not a re-declaration: the direction is unchanged and the at-tier review judged no correct on the real diff (0 files under packages/spec/, no key/member/export added, the #12920 alias guard preserved and pinned both ways).


Generated by Claude Code

os-warren commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: 163/163 CONTRACT_REVIEW_TIER
Head-sha: 01b387e05b08bc34099f583410c6b1a5e22df9ae

⚠️ How that first line was obtained. The isolated reviewer counted its own transcript's per-request model stamps (151/151 at its read) and, by instruction, did not type the value — it asked the adopting seat to map it. The seat did: parsing the same transcript at completion gives 163 assistant requests, 163 carrying one identical model stamp, 0 carrying anything else, and that value IS CONTRACT_REVIEW_TIER. ⛔ get_session was not used — it measures the dispatching session, not the review. Everything below the tier line is the reviewer's own text, adopted verbatim; ⛔ the seat rewrote nothing and polished nothing.

Isolated at-tier re-review after the FAIL recorded in comment 5754474832 on head 97b689b7. The delta between that head and this one is exactly two files, 2 insertions / 2 deletions: the object row of content/docs/ui/forms.mdx:260 and one clause in the changeset. Everything below was read first-hand from the GitHub REST API and from git show at the head sha in the shared checkout (fetched, never switched); the dev's report 5754300080, the PR body and the prior record were treated as claims to re-measure.

① Derived judgments

1. The prior FAIL's ground is CLOSED — measured, not accepted. forms.mdx:260 no longer says "its reference key, and only that key". Every sentence of the new row is true of the shipped code: (a) "its reference key, or — for a field type whose target is fixed by the TYPE (user, constant sys_user) — that constant. Those two are the only sources" matches referenceTargetOf (field-value.zod.ts:193-200: carrier via referenceCarrierOf first, REFERENCE_VALUE_TYPES gate, carrier if non-empty, else IMPLICIT_REFERENCE_TARGETS.get(type), whose only member is userSystemObjectName.USER); an explicit carrier still wins on a user field (pinned: object-graph.test "an EXPLICIT carrier still wins"), which the row's ordering states and does not contradict. (b) The split refusal sentence — alias spellings resolve nothing; lookup/master_detail then 500 LOOKUP_TARGET_MISSING, user answers from the constant — is pinned in both directions (public-form-lookup-picker.test.ts #19289 block: user+referenceTo → 200 over sys_user and never the alias; lookup+referenceTo → 500) and CI Test Core is green on this head. (c) "read through the one carrier accessor … refused rather than searched" is pinned ({ type: 'user', reference: { object } } → 500 INTERNAL_ERROR, findData not called). The error-table row at :287 ("names no target object at all, or its object metadata is unreachable") stays true for every type: a user field always names one, and with obj unreachable fieldDef is undefined so referenceTargetOf(undefined) is undefined and the route still answers LOOKUP_TARGET_MISSING (rest-server.ts:10935-10943). Row :288 INTERNAL_ERROR unchanged and true. :238 (lookup, master-detail, user are the picker-eligible types) unchanged and consistent. forms.mdx is hand-written: scripts/docs-audit/handwritten-docs.json:208 (control: generated references/** pages return 0 hits in the same file; 196 entries total).

2. Accept set and public surface — nothing widens; Clause-②: no is CORRECT. (i) 0 of the 13 diff files are under packages/spec; the arbiters are untouched (diff read in full). (ii) The accept set of authored metadata is unchanged: FieldSchema's superRefine (field.zod.ts:2042-2049) requires a non-empty reference only for lookup/master_detail, so { type: 'user' } without one parsed before this PR; Field.user() (:2633) writes reference: 'sys_user' itself and takes no target. (iii) The source diff is five import/arbiter swaps (seed-loader.ts:726, rest-server.ts:10935, object-graph.ts:261, validate-field-consumers.ts:572, validate-object-references.ts:321) plus comments, tests and one changeset — no key, enum member, export or registration is added. (iv) check-widening-tells.mjs, extracted from this head and run on the PR diff with --declaration no: exit 0, 13 of 13 files NOT MEASURED (no declared surface covers them) — the instrument says nothing either way; its --self-test passes 523 of 525 cases, the two failures being tree-presence checks over packages/** paths absent from my scripts-only extraction, so the diff-judging cases all pass and the instrument discriminates. (v) Controls pinned at every repaired site: carrier-less lookup/master_detail still name nothing (seed rows empty; picker 500; graph hop-untargeted); an unreadable carrier still throws TypeError (referenceTargetOf reads it through referenceCarrierOf before the type gate). (vi) The #12920 alias guard is preserved both ways. What more requests succeed are requests the explicit spelling of identical metadata already answered (pre-existing pin "the canonical STRING carrier still resolves and answers 200" over sys_user).

3. The Contract-text: citation licenses the no. Comment 5754509575 (PR) and 5754509371 (card) quote the IMPLICIT_REFERENCE_TARGETS docblock verbatim modulo bold; I compared it to field-value.zod.ts:157-168 at head and at main, identical. Published: the identifier is a module-private const, but packages/spec's package.json files ships src/**/*.zod.ts, and main's 5e7d83c states those sources ship to be read by path inside consumer node_modules; the same rule is also stated by the EXPORTED referenceTargetOf docblock ("Returns undefined only when the field genuinely names no target: a non-reference type, or a lookup/master_detail/tree with no reference") and by hand-written content/docs/data-modeling/field-types.mdx:323-324 (user is "a lookup specialized to the built-in sys_user object", no reference row, every example omits it; lookup :315 and master_detail :337 mark it required). Scope: the docblock is a statement about the metadata ("fully specified, not under-specified"), not about one consumer, so it is not read past its scope. The deleted refusal is none of the three deliberate classes (SKILL.md:522): the #12920 ruling guard is kept and pinned, the 403 LOOKUP_NOT_PUBLIC security envelope (rest-server.ts:10819-10838, server-managed anchors refused by name) precedes resolution and is untouched, and the old picker comment described the carrier read's absence behaviour, not a designed refusal of user metadata. Per SKILL.md:520-521 the declaration stays no; no downgrade to yes.

4. Docs and code agree. forms.mdx (item 1). field-types.mdx unchanged and already consistent. No spec .describe()/export edited, so every generated references/** page is unchanged at its source. The Docs Drift Check (5754281440) states rest-server.ts yielded no anchor, so the hand check was the compensating control; this round it reached the page the prior review named.

5. One consequence the PR does not name — judged correct, NOT a FAIL ground, should be named in the acceptance notes. The picker route has no field-type gate before referenceTargetOf(fieldDef) (rest-server.ts:10819-10841, :10935), and FieldSchema.reference (field.zod.ts:1320) is z.string().optional() with no refinement refusing it on a non-reference type (the only refinements: required on lookup/master_detail; referenceVia exclusivity). So a non-reference field carrying a stray reference (e.g. { type: 'text', reference: 'foo' }) with a publicPicker used to search foo via the carrier read and now answers 500 LOOKUP_TARGET_MISSING because text is outside REFERENCE_VALUE_TYPES. Likewise in lint, validate-preset-comparands.ts:450 reads the resolved leaf's meta.referenceresolveFieldPath returns ok with the leaf's meta regardless of the leaf's type (object-graph.ts:413) — so the PR's acceptance-note sentence "no other module reads GraphField.reference" is wrong on the tree: a user picker's filter rows now bind to sys_user (judgement resumes, and it is the object the route now queries — correct), and a text+stray-carrier picker filter binds to nothing where it bound to the stray object. Both are pull-backs to the declared contract (the reference describe text scopes it to "lookup/master_detail fields"; forms.mdx:238 scopes pickers to lookup/master-detail/user; the exported arbiter docblock says a non-reference type names no target) on a shape outside the documented surface — SKILL.md:520 拉回已声明契约不触条款②. Not a widening, not a docs falsity (the row does not claim every type's reference is honoured). Recommended: name it in the acceptance notes and correct the "no other module" sentence; a pin on validate-preset-comparands for the user case is recommended, not required.

6. Census table — re-measured, every row true. git grep, no pathspec, tests, the arbiter module, docs/changesets and comment-only lines excluded: 17 referenceCarrierOf / 8 referenceTargetOf at 8f6d831; 12 / 13 at this head = four repairs plus one alignment moved. All 17 table rows' path:line reproduce, including the drifted engine.ts:13204 / :13659. Controls: the instrument trap reproduces with needle REFERENCE_FIELD_TYPES at 8f6d831 (bare packages/**/src/**/*.ts → 0 files; :(glob) → 2; none → 2; the same audit-writers.ts and implicit-reference-target-summary.test.ts the PR names); a callback-form control (bare identifier without a paren) finds only imports, CHANGELOGs and api-surface rows, so the paren pattern missed no call; the two definitions are reachable by the unfiltered instrument. Attribution: 875e9ad (2026-09-20T10:37:15Z, #19293) adds carrier = referenceCarrierOf(cd, 'ObjectQL.buildSummaryIndex') — the 16→17 and A 7→8; 5636641 (10:09:32Z, #19264 / PR #19285) — audit-writers.ts at its parent has 0 referenceCarrierOf and 4 raw field.reference reads, replaced by referenceTargetOf(field) — the body's "raw read → target" is the right wording and the 7→8 on the target side. Type gates re-read first-hand at head: A3/A4 field?.type !== 'lookup'; A5/A6/A7 type !== 'master_detail' && type !== 'lookup'; A8 lookup || master_detail; B1 RELATIONSHIP_TYPES = {lookup, master_detail} (data-model-rules.ts:105); B2 RELATIONSHIP_FIELD_TYPES admits user (object-graph.ts:87-90) — real defect; B3 def.type !== 'master_detail'; B5 RELATIONSHIP_TARGET_FIELD_TYPES admits user (validate-object-references.ts:100); B8 f.type === 'master_detail'; B9 RELATIONAL excludes user (derive.ts:26). B7's CBP_TIERS not re-read this round — the prior record's reading stands, that file being unchanged between the two heads and vs main.

7. B6 reversal and its guard. referenceTargetOf({ name: 'owner', reference: 'zzz_nope' }) has no type, fails the REFERENCE_VALUE_TYPES gate and returns undefined, so check returns early — the guard's expected 1 OBJECT_REFERENCE_UNKNOWN finding is produced only on the carrier path; the guard discriminates. The site correctly stays on the carrier (validate-object-references.ts:360).

8. Reader-label pins. Four pins retargeted from site labels to /referenceTargetOf/; each still asserts TypeError, "reference is an object" and the FieldSchema … optional STRING prescription (all four test hunks read). No gate weakened.

② Semver level

patch on @objectstack/metadata-protocol, @objectstack/lint, @objectstack/rest — all three published (no private, 17.4.0) and all three change behaviour; no other package's behaviour moves. patch is right: the declared accept set does not move (①.2), this is a bug fix against the published contract (AGENTS.md:1075-1077), and it is consistent with Clause-②: no, which sits at line start in the changeset and at line 3 of the PR body. Changeset prose verified sentence by sentence against the head: the seed clause — a natural key resolving to no sys_user row DROPS the whole record, counted, reported and logged at error, with the three remedies — is TRUE: the references row targets sys_user.name (DEFAULT_EXTERNAL_ID_FIELD = 'name', seed-loader.ts:39); resolution (:1696-1710) keeps id-shaped values and looks up this load then the DB; on a miss with !dryRun && !multiPass it calls pushError (reported), logger.error (:1383), sets unresolvedRefError and the row is errored++; continue (:1414-1417); multiPass defers instead (:1329-1362); the dependsOn edge orders sys_user first when it is in the seeded set (pinned). The remedies quoted are the loader's own error text (:1387-1391). "Unreadable-carrier behaviour is unchanged" TRUE (arbiter order); "Nothing else widens" TRUE (user sole member, pinned); "No authoring change" TRUE (carrier wins; Field.user() writes it). Level and prose match ①. Recommended, not required: one clause naming the non-reference-type stray-carrier picker consequence (①.5).

③ Boundary flags

  • Head sha reviewed: 01b387e05b08bc34099f583410c6b1a5e22df9ae, unchanged on both reads of the PR (last at 02:49:58Z; updated_at 02:34:33Z; still draft, open).
  • Un-merged base — accepted, immaterial. Merge base fbc12be is an ancestor of the head. main (5e7d83c) is 3 commits ahead: adabccf (spec(ui): BulkActionParamSchema is strict and declares dependsOn #19090, BulkActionParamSchema strict), 841ed38 (fix(spec): six dist-reading gates answer an unmet build prerequisite with exit 3, not a finding's 1 #19463, gates answer exit 3), 5e7d83c (fix(gate): correct why check-published-files lets packages/spec ship its zod sources — a read route, not an import route #19468, one reason string in scripts/check-published-files.mjs, no code-path change). git diff --stat fbc12be..main over the 13 touched paths plus field-value.zod.ts, field.zod.ts, ui/action.zod.ts, packages/lint/src, seed-loader.ts, rest-server.ts is EMPTY. CI's pull_request runs on this head already used merge base 841ed38 (Docs Drift Check, merge b5702e3), so only the non-behavioural 5e7d83c is untested against the head. Nothing this diff's contract story depends on moved.
  • Follow-up card declined (reader label) — accepted as sound for this PR. Seat comment 5754514878 records both positions; the filing classes are the backlog's rule, the pins keep class, shape and prescription, and the refusal still fires. The dissent is on the record; not a gate change, not a contract change.
  • Body edit left to the seat. The Clause-②: no line the dev reported missing is now present at line start in the PR body, and the table wording reads "raw read → target". Recommended further body edit: correct the "no other module reads GraphField.reference" acceptance note and name the non-reference-type stray-carrier consequence (①.5).
  • Security / permission boundary — no new authorization surface. 403 LOOKUP_NOT_PUBLIC and the server-managed-field refusal precede resolution and are unchanged; the object reachable anonymously (sys_user) was already served under the same envelope for the explicit spelling. NOT MEASURED: live findData over sys_user under guest_portal (needs a booted server; unchanged by this diff).
  • Published-contract change: none in packages/spec; the one hand-written docs sentence the prior review named is now correct. Gate weakening: none. New required gate / hook / ratchet / runtime dependency: none.
  • Check-runs on this head, latest run per check NAME, read at 02:47:41Z (42 runs): every latest run is success or skipped; none failed, none pending. Lint & Repo Gates was in_progress at my first read (02:36Z) and completed success at 02:37:54Z; Check Changeset latest (02:34:38Z) success; Test Core, TypeScript Type Check, Build Core, Dogfood Regression Gate, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard, Spec property liveness all success. Superseded earlier runs of the same names exist on the head and were not used as the reading.
  • NOT MEASURED, stated so nothing is inferred as a pass: the three package suites and typecheck locally (no node_modules on the shared checkout; CI on this head is the reading); dispatch-gates --ran; check-widening-tells covers none of the 13 files; B7's gate this round; the Docs Drift Check's own caveat that its content/docs read came from a checkout carrying uncommitted changes.

Implemented-by: claude/issue-19289-implicit-reference-target-census
Reviewed-by: session_01UDXER3sdqfeVYpEWZs5mZx

VERDICT: PASS


Generated by Claude Code

@os-warren
os-warren marked this pull request as ready for review September 21, 2026 02:57
@os-warren
os-warren added this pull request to the merge queue Sep 21, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 21, 2026
@os-warren
os-warren added this pull request to the merge queue Sep 21, 2026

Copy link
Copy Markdown
Collaborator Author

Dequeued on CI_FAILURE — diagnosed as NOT this PR's, re-queued once, and that re-run is now SPENT

domain:spec seat 2 (座位贴 #18549), os-warren · session_01UDXER3sdqfeVYpEWZs5mZx. The merge queue removed this PR at 2026-09-21T03:14:12Z with reason: CI_FAILURE. This comment is the record that act owes; ⛔ the PR was not touched, no test was skipped, disabled or quarantined, and no empty commit was pushed.

The failing check

Temporal Conformance (live PG + MySQL), job 106199588949, in the merge-queue run 35555936360 on branch gh-readonly-queue/main/pr-19472-d00692f5d44c8f7ecdd57668d41bfc0df9eee616. It is the only failed job in that run; Lint & Repo Gates was still in progress when the queue dropped the PR.

Why it is not this PR's — four readings, each with what would have made it come out otherwise

reading value
the same check on this PR's head 01b387e0 success — 35 check names, latest run per name: 0 failures, 0 pending
the same check on main at d00692f (the exact base the queue build used) success (run 106197283044)
the same check on main at 32b5831 (the prior tip) success ×3 (106193513539, 106198012040, 106199867344)
what the base actually carries d00692f is PR #19479scripts/pm/close-cards.mjs, .claude/settings.json, .github/workflows/lint.yml, package.json. PM tooling.

⇒ green on the head, green on the base, green on the base's predecessor three times over, and the one commit the queue build added is PM tooling. ⛔ That is not a "the job is broken" reading either — a broken job would not be green four times in the same hour.

What the failure names, against what this PR touches. The service-container log carries Cannot create the table \os_lv_sql_driver_11565_row_byte_budget_…`.`os11565_narrow` because the record size will exceed the maximum allowed size for a record— a **SQL-driver row-byte-budget** conformance case. This PR's 13 files are reference-target arbiter swaps inpackages/metadata-protocol, packages/lint, packages/rest, one hand-written docs page and one changeset. ⛔ It touches no driver, no DDL path and nothing temporal. ⭐ The many PG ERROR:` lines in the same teardown block are expected negative-path noise — the conformance suite provokes constraint violations on purpose — so they are ⛔ not the failure and ⛔ not evidence of one.

⛔ "Flake" is not being offered as a root cause. The test bodies plainly ran (statements at 03:02–03:03), so this is not a died-before-the-body case. What the four readings support is the narrower claim the rule allows: an error naming a subsystem this diff does not touch, on a check that is green on both this head and this base. That buys exactly one re-run, and ⛔ nothing more.

No fix exists to port. There is no open PR, revert or fix branch addressing this failure — nothing to carry into this PR, so nothing was carried. ⛔ This PR is not widened.

What was done

Re-queued, ⛔ not re-pushed: PUT .../pulls/19472/ccr/auto_merge {"merge_method":"SQUASH"}{"enabled":true,"merge_method":"squash"}, confirmed on the timelineadded_to_merge_queue 2026-09-21T03:16:27Z — and by the live ref gh-readonly-queue/main/pr-19472-d00692f5…. Preconditions re-verified immediately before, ⛔ not recalled from the first enqueue: head 01b387e0 green on all 35 check names with none pending, mergeable_state: clean, and node scripts/pm/check-clause2-carriers.mjs --pair 19472 = exit 0 with the at-tier PASS record (5754774179) naming this head.

⚠️ The re-run is now SPENT

If Temporal Conformance fails again on the next queue build, that second failure is real, and this seat will root-cause it rather than re-queue a third time. ⛔ No further re-queue on the same signature without a diagnosis first.

Reading taken 2026-09-21T03:17Z.


Generated by Claude Code

Merged via the queue into main with commit 2b321a4 Sep 21, 2026
51 checks passed
@os-warren
os-warren deleted the claude/issue-19289-implicit-reference-target-census branch September 21, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants