Skip to content

[finding] 9 raw .reference reads still answer undefined silently after #18503 routed the carrier through one arbiter — the measured residue of ruling E item 2 #18550

Closed

Description

@os-litant

Split out of #18095 at contract-review time, so the residue keeps a tracker after Fixes #18095 closes that card. ⛔ Not filed by the implementing dev and ⛔ not a defect in PR #18503 — the PR names this residue itself, under "What this PR does not close".

What is left

PR #18503 routed the FieldSchema.reference carrier read through one arbiter (referenceCarrierOf), so a carrier that cannot be read is now refused loudly instead of answering "no target". Ruling letter E item 2 on #18095 asked for that refusal at every reader. The PR delivered it at the arbiter plus the four lint read sites, and left a measured residue of raw reads that still answer undefined silently:

  • packages/objectql/src/engine.ts:13052 and :13491 (cascade delete)
  • packages/rest/src/rest-server.ts:10835
  • packages/metadata-protocol/src/seed-loader.ts:701
  • packages/lint/src/validate-expressions.ts:380
  • packages/lint/src/validate-field-consumers.ts:552
  • packages/lint/src/validate-object-references.ts:297 and :316
  • packages/lint/src/validate-sharing-rule-enforceability.ts:261
  • packages/verify/src/derive.ts:136

⚠️ The count is 9, not the 10 the PR body states. The contract review re-measured it: packages/lint/src/validate-preset-comparands.ts:431 reads verdict.meta?.reference, and meta is the GraphField slice graphFieldOf builds (packages/lint/src/object-graph.ts:307) — so that site is already covered by the arbiter change and is NOT residue. The other ten cited lines were each printed at the merge base and read.

Separately, the review recorded 7 typeof === 'string' narrowing sites (class C1) that produce the same silence spelled differently, neither changed nor listed by the PR under "does not close": plugin-audit/src/audit-writers.ts (×4), rest/src/export-format.ts, cli/src/commands/doctor.ts, spec/src/kernel/functional-completeness.ts. ⚠️ One of those is misclassified in the PR body: spec/src/kernel/functional-completeness.ts:167 is not silence — it reports a non-string reference as an incompleteness finding. Whoever takes this card should re-derive the C1 set rather than inherit it.

The open question this card also carries

⚖️ Does arbiter + lint routing satisfy ruling item 2, or was PR #18503 owed the residue too? The contract review declined to answer this and named it the maintainer's to give. The seat landed #18503 on its PASS with this card filed first, so that whichever way the question goes, the work is tracked rather than lost. If the answer is "the PR was owed it", this card is that work and nothing was lost by landing; ⛔ the seat did not treat landing as the answer.

Why each site needs its own judgement, rather than a sweep

Routing these means touching objectql, rest, metadata-protocol and verify, several on hot runtime paths. Each site needs a decision about absence vs unreadability — the two are deliberately different answers in this design (null, undefined and '' are absence and must never throw; StrictField declares reference nullable). A mechanical sweep that throws on everything falsy would break the absence contract.

⛔ Note for whoever takes it: the schema's own superRefine validators must NOT throw (spec/src/data/field.zod.ts, spec/src/ui/action.zod.ts, spec/src/automation/builtin-node-config.zod.ts) — a throw inside a refinement makes safeParse throw instead of returning {success: false}, which would destroy the loudness at the contract door this whole change leans on.

First step for the dispatched dev

Re-derive the residue on the then-current main before touching anything: the list above was measured at PR #18503's merge base 2f11e2db7, and both the 9 and the C1 set are premises to falsify, not facts to inherit.

Dedupe words: referenceCarrierOf residue, raw .reference read answers undefined, C3 residue 9 sites, absence vs unreadability reference carrier, ruling E item 2 every reader.


Generated by Claude Code

Activity

  1. self-assigned this
    on Sep 18, 2026
  2. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    Claim: PM loop round 10 — taken 2026-09-18T15:28Z.
    Session: session_019srGWGCBBCBHqcDoRZpQRh
    Branch: claude/issue-18550-reference-carrier-residue
    Worktree: objectstack-issue-18550
    Domain: domain:spec
    Seat: domain:spec#3
    File surface (⭐ the nine sites, declared per triage's cross-domain instruction 「承接席须在认领评论申报九处文件面并做在飞求交」): packages/objectql/src/engine.ts, packages/rest/src/rest-server.ts, packages/metadata-protocol/src/seed-loader.ts, packages/lint/src/validate-expressions.ts, packages/lint/src/validate-field-consumers.ts, packages/lint/src/validate-object-references.ts, packages/lint/src/validate-sharing-rule-enforceability.ts, packages/verify/src/derive.ts, plus each one's own tests and one changeset (stop on breach; explain in the report)
    Container & model: L, mode:subagent, model: not quoted — this seat ⛔ will not type a tier string it did not read; dispatched at this session's default judgement tier
    Clause-②: yes
    Thread-read: 5715719168
    Serial constraints cleared: measured, ⛔ not assumed — all 30 open PRs of this repo were listed and each one's file page read in this act; ZERO of them touches any of the eight distinct files above. #19024, #18638, #19059, #19060 and this seat's two other in-flight tranches (#18919, #18944) are included in that scan and are each disjoint. No merge=os-regen path is in this surface.

    In-flight intersection — the whole open board, not a recollection

    Triage required the declaration above and an in-flight intersection. The intersection was run as a scan rather than from memory: 30 open PRs enumerated, each PR's files page read, matched against the eight distinct paths. Zero hits. ⇒ nine sites, no second writer, no serialisation owed.

    The count, re-derived — triage's instruction was 「派发时再数一次,⛔ 不要把 9 或 10 任何一个往前抄」

    This seat re-derived it at file granularity on origin/main@221dabb72; the per-line judgement stays the dev's, because the card is right that each site needs its own absence-vs-unreadability decision:

    file .reference occurrences referenceCarrierOf
    packages/objectql/src/engine.ts 9 0
    packages/rest/src/rest-server.ts 1 0
    packages/metadata-protocol/src/seed-loader.ts 13 0
    packages/lint/src/validate-expressions.ts 3 0
    packages/lint/src/validate-field-consumers.ts 1 0
    packages/lint/src/validate-object-references.ts 6 0
    packages/lint/src/validate-sharing-rule-enforceability.ts 1 0
    packages/verify/src/derive.ts 1 0

    ⭐ Lit control, same instrument: referenceCarrierOf answers 2 hits each in packages/lint/src/data-model-rules.ts, object-graph.ts and validate-security-posture.ts — files PR #18503 did route. ⇒ the eight zeros are readings, ⛔ not a dead grep.

    ⚠️ Two things this re-count does not settle, handed to the dev rather than decided here: (i) .reference occurrences exceed the cited line count at several sites (engine.ts 9 occurrences against 2 cited lines), so which occurrences are carrier reads is the dev's per-line derivation; (ii) the PR body claims four routed lint read sites while the control finds the arbiter in three files — re-derive that too rather than inheriting either number.

    Clause-② — declared yes, and why, since the trigger is not the one PR #18503 had

    PR #18503 declared Clause-②: yes on a concrete ground its body states: referenceCarrierOf was a new published export (「1 added, 0 breaking」). ⇒ that particular trigger is spent — this work adds no export, it calls an arbiter that is already published.

    What remains is the other limb: this routing changes observable behaviour at published runtime doors (packages/rest/src/rest-server.ts, packages/objectql/src/engine.ts cascade delete), turning a silent undefined into a loud refusal. This seat ⛔ will not adjudicate that limb itself — two rules in tension resolve to the stricter one and a note, so the declaration is yes and the PR carries the contract-review carrier.

    ⇒ consequence, stated so it is not a surprise: this seat serves below CONTRACT_REVIEW_TIER, so the verdict on this PR comes from an isolated at-tier review subagent, ⛔ never from this seat's own reading.

    The open question this card carries — ⛔ not answered here, and ⛔ not blocking

    The card asks whether arbiter + lint routing satisfied ruling letter E item 2, or whether PR #18503 was owed the residue too, and records that the contract review named it the maintainer's to give. ⛔ This seat does not answer it, and it does not gate the work: the nine sites are the same silence whichever way attribution falls, triage put the card in the queue, and the card's own reason for existing is that the work stays tracked either way. It goes to the maintainer as an attribution item, ⛔ not as a prerequisite.


    Generated by Claude Code

  3. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    os-dev-report

    {
      "issue": 18550,
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19080",
      "pr_number": 19080,
      "head_sha": "a17094d9482575b014edce04224a0426e4e93712",
      "status": "done",
      "branch": "claude/issue-18550-reference-carrier-residue",
      "session": "session_019srGWGCBBCBHqcDoRZpQRh",
      "premise_still_valid": true,
      "summary": "All ten cited residue reads are routed through the one arbiter (referenceCarrierOf), each with its own absence-vs-unreadability decision and its own pin: objectql planCascadeAtomicity + cascadeDeleteRelations, the REST public-form lookup picker, the seed dependency graph, four lint target readers, and verify's relationTarget. Absence (undefined / null / '') answers undefined at every site and never throws; only a shape the contract does not admit refuses. The card's premise held on origin/main@0ec81857a: all ten cited lines were present with byte-identical text at the stated merge base, two of them moved by +44/+53/+54 lines. One changeset, minor x5, on the same grading and the same 'nothing conformant changes' reasoning PR #18503's own changeset shipped. One hand-written doc page was also falsified by the change and is fixed here (content/docs/ui/forms.mdx, the public-form picker's error table) — found by hand in the drift bot's own declared blind spot, since packages/rest/src/rest-server.ts yields no doc anchor. The 7 release-owned pages are byte-untouched.",
      "count_rederived": {
        "verdict": "Neither 9 nor 10 is reachable at a single granularity. The card's own list is 10 cited LINES in 8 FILES; the list is the fact and the count was the rotting reading.",
        "how_9_is_reached": "Triage's parenthesis mixes granularities: objectql x2 (LINES) + rest + metadata-protocol (FILES) + lint x4 (FILES) + verify = 9. Counting the same list per line gives 10, per file gives 8.",
        "how_PR_18503s_10_is_reached": "Its C3 list holds 11 cited line references in 9 files (the card's 10 plus validate-preset-comparands.ts:431) with one pair merged to 10; remove preset-comparands and the card's 9 follows by the same merge.",
        "per_line": [
          "objectql/src/engine.ts 13052 -> 13096 `const ref = fdef.reference;` (planCascadeAtomicity) — UNREADABILITY. Truthy object carrier passed `if (!ref)` then failed both name comparisons, so the child left the referencing set and 'none' (the one verdict asserting *nothing references this object*) could be returned. Routed.",
          "objectql/src/engine.ts 13491 -> 13544 `const ref = fdef.reference;` (cascadeDeleteRelations) — UNREADABILITY, and the seam with the measured end-to-end consequence. Routed.",
          "rest/src/rest-server.ts 10835 -> 10889 `referenceObject = def?.reference;` — UNREADABILITY. Bare assignment, no narrowing at all: an object carrier was truthy AND forwarded verbatim as query.object into findData. Routed, with the field def hoisted OUT of the metadata fetch's `catch {}` so the refusal is not swallowed into LOOKUP_TARGET_MISSING.",
          "metadata-protocol/src/seed-loader.ts:701 — UNREADABILITY. Truthiness gate plus an `as string` cast that asserted exactly what the gate had not checked. Routed; cast retired; the TYPE gate deliberately stays first so the set of fields read is byte-identical.",
          "lint/src/validate-expressions.ts:380 — UNREADABILITY, C1-shaped (`typeof === 'string' && ref.trim() !== ''`). Routed; the .trim() test kept, so a whitespace-only carrier is still absence.",
          "lint/src/validate-field-consumers.ts:552 — UNREADABILITY, C1-shaped via strName. Routed.",
          "lint/src/validate-object-references.ts:297 — UNREADABILITY, C1-shaped via strName. Routed.",
          "lint/src/validate-object-references.ts:316 — UNREADABILITY, C1-shaped via strName. Routed. CAVEAT: the carrier is ActionParamSchema.reference, one contract by the spec's own docblock ('deliberately mirrors FieldSchema.reference'), but the refusal text names FieldSchema. Widening the message needs packages/spec, outside the declared surface.",
          "lint/src/validate-sharing-rule-enforceability.ts:261 — UNREADABILITY, C1-shaped. Routed.",
          "verify/src/derive.ts:136 — UNREADABILITY, C1-shaped. Routed; the generic 'has no reference target' degradation is the exact trade this reader already refused for a rejected alias."
        ],
        "not_residue_confirmed": "validate-preset-comparands.ts:431 (now :450) reads verdict.meta?.reference, and meta is the GraphField slice graphFieldOf builds — routed by PR #18503. Covered transitively, as are object-graph.ts:383 and :386. The contract review's re-count reproduces.",
        "eleventh_line_found_and_NOT_routed": "objectql/src/engine.ts:9033 (`cd?.reference === parent.name` in buildSummaryIndex) is the same silent skip ('can't resolve the relationship — skip', so a summary field never recomputes). It is ALREADY classified as engine.ts:8989 in PR #18503's C2 list — a boundary that PR drew deliberately — so routing it would widen this card. Reported as a finding, not routed.",
        "four_routed_lint_read_sites_rederived": "Not a reading at any granularity. Measured: 3 FILES hold a referenceCarrierOf call (data-model-rules.ts, object-graph.ts, validate-security-posture.ts), 3 arbiter CALL sites (one per file), and 9 downstream lint READ sites (refOf( x4 in data-model-rules, x3 in validate-security-posture, graphFieldOf( x2 in object-graph). PR #18503's own words are '6 read sites, 4 files' and one of those four files is packages/spec/src/data/field-value.zod.ts — the arbiter itself, not lint. The card's paraphrase counted the arbiter's file as a lint file. The seat's lit control reproduces exactly (2 grep hits per file = 1 import + 1 call)."
      },
      "c1_set_rederived": {
        "card_list_checked": "6 of the card's 7 are genuine silence: plugin-audit/src/audit-writers.ts:472,527,629,693 (x4), rest/src/export-format.ts:170, cli/src/commands/doctor.ts:792.",
        "card_is_right_about_the_misclassification": "spec/src/kernel/functional-completeness.ts:167 (now :177) is NOT silence. It REPORTS: FIELD_RELATIONSHIP_WITHOUT_REFERENCE, severity error. Measured rather than read — registering a fixture with an object carrier printed `[Registry] Object \"task\" registered with 1 functionally-incomplete field(s) ... account: [error] field/relationship-without-reference`. So the C1 set is 6, not 7.",
        "wrong_in_the_other_direction_1": "C1 and the residue are NOT disjoint. SIX of the ten residue lines ARE typeof === 'string' narrowings, inline or through a strName helper that is one (validate-expressions:380, validate-field-consumers:552, validate-object-references:297 and :316, validate-sharing-rule:261, verify/derive:136). The PR body's C1/C3 split reads as two populations; at six of ten sites it is one code shape sorted into two classes. Only four residue lines are a different shape: two truthiness gates (engine.ts x2), one truthiness-plus-cast (seed-loader), one bare assignment (rest-server).",
        "wrong_in_the_other_direction_2": "A SECOND C1-shaped site that REPORTS, omitted by the list: spec/src/automation/builtin-node-config.zod.ts:527 — typeof field.reference === 'string' && .trim() !== '' inside a superRefine, falling through to ctx.addIssue(SCREEN_FIELD_LOOKUP_REFERENCE_REQUIRED). So functional-completeness is not the lone misclassification: there are TWO C1-shaped reporters, and neither may be made to throw (a throw in a refinement makes safeParse throw).",
        "wrong_in_the_other_direction_3": "Four truthiness/equality readers outside the C1 list answer WORSE than undefined (C2 by PR #18503's classification, not touched here): plugin-approvals/src/approval-service.ts:5773 does String(f.reference), inventing the literal target name '[object Object]'; service-analytics/src/plugin.ts:736 returns the unnarrowed carrier; cli/src/commands/doctor.ts:709 and :895 push it into a dependency graph."
      },
      "tests": "REPRO at the representative site (objectql cascade delete), same probe before and after: BEFORE `before-delete acct=1 task=1 / delete RESOLVED true / after-delete acct=0 task=1` (an ORPHANED master_detail row behind a successful delete, nothing logged); AFTER `delete THREW TypeError: ObjectQL.planCascadeAtomicity: `reference` is an object, and FieldSchema declares it as an optional STRING ...` with `after-delete acct=1 task=1` (nothing touched — planCascadeAtomicity runs before the cascade). Two facts the probe forced: the engine's own WRITE path already refuses this shape (insert -> assertReferencesResolve -> referenceTargetOf -> the same arbiter), so the child row had to be written straight through the driver, which is the provenance the arbiter's docblock names; and the registry's completeness check printed field/relationship-without-reference while the cascade stayed silent — reported in one channel, mis-read in another. ABLATION (proves the pins can fail), via scripts/ablation-replace.mjs on the committed tree, NOT sed -i: anchor `const ref = referenceCarrierOf(fdef, 'ObjectQL.planCascadeAtomicity');` x1->x0, replacement `const ref = fdef.reference;` x0->x1, blob 4ca2839cae6b679c2ae16b2b9e1f804235e7c3b8 -> 6121f5c9bf429db31f959af2e43711506db8baf0 (mutation landed, proved by hash and anchor count, not by exit code); the seam-1 pin went RED (1 failed | 5 passed of 6) while seam 2 and all three absence controls stayed green, so the pins are per-site rather than one shared assertion; restored with `git checkout HEAD -- PATH` semantics via --restore, verified independently: blob == HEAD blob (4ca2839cae6b) and `git diff HEAD` empty. SUITES: objectql 300 files/5009 passed; rest 194/3245 passed +1 skipped; metadata-protocol 180/2585 passed +19 skipped; lint 104/3935 passed; verify 15/116 passed. TYPECHECK all five exit 0 including test layers; objectql's shrink-only test-typecheck ledger held at 40 files/234 errors/65 pinned signatures, not raised; check:type-check-debt re-measured all 4 DEBT entries at the final head — 53 raw errors, none above its recorded number. REPO-WIDE LINT: `pnpm lint` (eslint . --no-inline-config) exit 0 at bd1b3dcad in 75s — run, not narrowed, so no narrowing evidence is owed. CONTROL-BYTE SELF-SCAN beyond check:nul-bytes: grep -naP over all 17 changed paths, zero matches.",
      "gate_census": {
        "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, derived from the REAL change set at the final head (18 paths vs merge base 0ec81857a, three-dot), then reconciled with --ran carrying one `COMMAND :: exit CODE` row per family. ⭐ The doc fix ADDED 28 families (65 -> 93), exactly the case the standing rule warns about (the dispatch list is a clue, not a spec) — all 28 were run after the doc commit.",
        "reconciliation": "93 derived, 92 run, 1 NOT-MEASURED, 0 UNRUN (at head a17094d94). Earlier reading before the doc fix: 65 derived, 64 run, 1 NOT-MEASURED.",
        "exit_0": 92,
        "not_measured": [
          "pnpm check:dual-build-cjs-loads :: exit 3 — PREREQUISITE NOT MET, neither a pass nor a finding. Its own words: 'this gate reads built output, and some package has no dist/ ... Run `pnpm build` first. This is NOT a pass: nothing was measured.' 35 packages outside this card's build closure have no dist/; the prerequisite is a repo-wide build, which CI does before this step. Bounding fact measurable from the diff: ZERO package.json, tsup/tsconfig, turbo.json or workflow paths are touched — 17 changed paths, all .changeset/ plus packages/**/src."
        ],
        "fixed_at_the_source_never_at_the_ledger": [
          "check:objectql-double-limit exited 1 on the NEW test file's `find` double (graded limit-blind at line 133). Fixed in the double — the caller's bound applied after the filter, by presence. The baseline was NOT touched; the re-run prints 'baseline key set verified against 0ec8185: no files added'.",
          "check:type-check-debt exited 3 twice on its own prerequisite — first an unbuilt @objectstack/driver-turso, then because the ablation's restore rewrote engine.ts and left dist/ older than source by mtime. Both satisfied by building, then exit 0. No ledger entry was raised on an exit 3, which that gate forbids explicitly."
        ],
        "families_added_by_the_doc_fix": "28, all exit 0: check-doc-frontmatter (+self-test), check-doc-route-spelling --advisory (+self-test), check-docs-section-name (+self-test), check-section-landing-index (+self-test), lint check:doc-formula-expressions, lint check:doc-security-posture, spec check:docs, spec check:empty-state, spec check:liveness, spec check:skill-examples, spec check:strictness-ledger, spec check:variant-docs, spec check:yaml-examples, check:corpus-claim-drift, check:doc-anchors, check:docs-audit-scope, check:docs-redirects, check:docs-single-h1, check:docs-spec-enumerations, check:published-readme-links, check:react-page-adapter-contract, check:role-word, check:skill-identifier-liveness, check:vendor-version-stamps.",
        "ratchets_rerun_at_the_final_head": "pnpm lint, check:type-check-debt, check:type-check-coverage, check:objectql-double-limit, check:test-source-alias, check:cross-package-test-inputs, check:nul-bytes, check-changeset-no-major, check-empty-changeset, check-adr-0087-registration, docs-audit/check-affected-docs — all exit 0 at a17094d94, so no ratchet reading is quoted from a tree older than the last commit."
      },
      "widening_tells": {
        "declared_run": "node scripts/pm/check-widening-tells.mjs --declaration yes --diff pr.diff :: exit 0 — 'the claim declares `Clause-②: yes`, which this gate never blocks — a `yes` already routes to contract review, so a tell on top of it decides nothing.'",
        "why_that_is_not_a_reading": "With `yes` the gate short-circuits and examines NO file, so it produces no rows at all — the absence of NOT MEASURED rows there is the absence of a reading, not a clean one.",
        "diagnostic_run_for_the_per_file_reading": "The same diff with --declaration no, run as a DIAGNOSTIC only (the declaration is and stays `yes`, and it is the seat's): exit 0 — '17 changed file(s) — 0 judged against a declared surface (no widening tell), 17 NOT MEASURED. NOTHING on this diff was examined for widening tells, so this exit 0 is evidence about no surface at all.' Reason given per file: 'no declared surface covers it (17)', listing all 17 changed paths.",
        "not_measured_rows": 17,
        "verdict": "17 NOT MEASURED, 0 judged — NOT a clean reading, reported as such. The gate's four tells cover Zod schema surfaces, closed sets, published export listings and registries; this diff touches none of them. --self-test passes (510 cases).",
        "clause2_pair": "node scripts/pm/check-clause2-carriers.mjs --pair 19080 :: exit 4 (C3) at first read — declaration `yes` with the carrier on NEITHER the card nor the PR and never hung on either. Re-read after the seat's write: exit 0, 'the clause-② declaration is readable in the fixed spelling and both carriers agree'. ⚠️ The widening-tells reading above predates the doc commit; the doc file is likewise on no declared surface, so the 17 NOT MEASURED rows become 18 and the verdict is unchanged.",
        "labels_on_pr_19080_at_report_time": "documentation, size/xl, tests, tooling, needs:contract-review — ⛔ NONE written by this dev, and none corrected. The contract-review carrier was hung by another actor between the two --pair reads and is left exactly as found, on both PR #19080 and card #18550."
      },
      "changeset": "One: .changeset/18550-reference-carrier-residue.md, `minor` on all five moved published packages. Level reasoning: `minor` not `patch` because the declaration is `Clause-②: yes` and check-changeset-no-major's level axis requires at least one moved published package graded minor or above; `minor` not a breaking grade because nothing conformant changes — a non-string `reference` could not be authored, stored or parsed before this release either. Same grading and same 'Upgrading: nothing conformant changes' reasoning as PR #18503's own changeset for the same class, under the same launch-window convention. check-changeset-no-major exit 0; check-adr-0087-registration exit 0 (no breaking declaration, so no disposition marker is owed).",
      "mcp_calls": "0 — no MCP GitHub tool was called, read or write. All GitHub traffic went through the REST proxy with curl.",
      "api_writes": "2 REST writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR #19080), POST /repos/objectstack-ai/objectstack/issues/18550/comments (this os-dev-report). ZERO label writes — the needs:contract-review carrier is the seat's and was not touched. ZERO PATCH of the PR body (dev writes it once, at creation). Plus 6 git pushes to the feature branch (branch-creation probe, implementation, tests+changeset, double-limit fix, derive.ts form, doc fix).",
      "floors": "No gate weakened: no ratchet raised, no floor lowered, no ledger row deleted, no test skipped, disabled or quarantined. The one red gate was fixed at its subject (the test double), and the two exit-3 gates were satisfied by building, never by a ledger edit.",
      "open_questions": [
        {
          "question": "The refusal text at validate-object-references.ts:316 names the wrong schema: the carrier there is ActionParamSchema.reference, but referenceCarrierOf's message says 'FieldSchema declares it as an optional STRING'. Correct behaviour, mis-cited contract.",
          "options": [
            "A — leave it: the spec's own docblock says the ActionParam key 'deliberately mirrors FieldSchema.reference so the same spelling', so one contract genuinely has one reader and one sentence.",
            "B — give referenceCarrierOf an optional declaring-schema label so the reader names the schema it actually read (a packages/spec edit, outside this card's declared file surface)."
          ],
          "recommendation": "A for this PR (done), and B only as its own card if a reader is ever confused in practice — the alternative was editing packages/spec, which this card's surface excludes."
        },
        {
          "question": "Does arbiter + lint routing satisfy ruling letter E item 2, or was PR #18503 owed the residue too? NOT answered here, and delivering this card is not an answer — the contract review named it the maintainer's to give.",
          "options": [
            "(the maintainer's to give — no options proposed by this dev)"
          ],
          "recommendation": "No recommendation offered. Two pieces of EVIDENCE surfaced and are handed over without a verdict: (i) the ten residue lines were a measurable population at the time — each printed at the merge base and read — which bears on whether they were reachable inside that PR's scope; (ii) SIX of the ten are the same code shape PR #18503 sorted into its deliberately-unchanged C1 class, so 'the residue' and 'the boundary' were not two populations but one, sorted twice."
        }
      ],
      "out_of_scope_findings": [
        "to file (class a, reproducible defect) — plugin-approvals/src/approval-service.ts:5773 turns an unreadable `reference` carrier into the literal target object name '[object Object]' via String(f.reference), so resolveLookupFields hands the approval path a target that will never resolve and nothing reports it. Worse than answering undefined: it invents a name. Dedupe words: `String(f.reference) approval lookup`, `[object Object] reference target`, `approval-service resolveLookupFields carrier`, `unreadable reference invents target name`.",
        "to file (class a, reproducible defect) — objectql/src/engine.ts:9033 buildSummaryIndex reads `cd?.reference === parent.name`, so an unreadable carrier compares false, fkField stays unresolved and the loop takes its 'can't resolve the relationship — skip' branch: a declared `summary` field never recomputes, silently. Already recorded as engine.ts:8989 in PR #18503's C2 class, so this is a request to reopen a deliberately-drawn boundary, not a new discovery. Dedupe words: `buildSummaryIndex reference equality`, `summary field never recomputes`, `C2 equality carrier read`, `engine 8989 summary index`.",
        "to file (class a, reproducible defect) — service-analytics/src/plugin.ts:736 returns `field.reference` unnarrowed behind a truthiness gate, so an unreadable carrier becomes a dataset relationship target of a non-string type; cli/src/commands/doctor.ts:709 and :895 push the same value into a dependency graph and a referenced-object set. Same C2 class. Dedupe words: `analytics relationship target unnarrowed`, `doctor dependency graph carrier`, `C2 truthiness reference read`, `dataset relationship non-string target`.",
        "noted, not filed: the `{ reference: X.reference }` wrapper form at the four lint sites and in verify/derive.ts is deliberate, not stylistic drift — the literal `.reference` read must stay at the site or the #5017 receiver meta-test in packages/lint loses its subject (it reads the rule's SOURCE to prove it reads `reference` and never an alias; it went RED on exactly that during this work, and validate-security-posture.ts documents the same reason for the already-routed sites). The three runtime readers pass the field def directly, where the def is the natural argument and no such scan exists. Successor who will touch this: whoever routes the C2 population above.",
        "noted, not filed: the REST public-form lookup picker's refusal is loud in the LOG and merely DISTINGUISHABLE on the wire — measured envelope for the escaping TypeError is `500 {\"error\":\"Internal server error\",\"code\":\"INTERNAL_ERROR\"}` via mapDataError plus logError, versus `500 LOOKUP_TARGET_MISSING` for absence. That is the correct fault-sanitisation posture (a crash's text is never disclosed to the caller), so it is recorded rather than filed. Successor: none — no PR or person is queued on this file's envelope vocabulary.",
        "noted, not filed: the Docs Drift Check's precision-first anchor set has a measured weak spot on this diff — 20 of its 21 hand-written rows came in via the literal `master_detail`, which the diff only MOVED (the seed-loader `if` condition was rewritten), while its eight symbol anchors matched no hand-written page at all and the one genuinely falsified page was invisible to it by its own declaration. That is the bot behaving as documented, not a defect, so it is recorded rather than filed. Successor: none queued; the next author of a rest-server.ts change inherits the same blind spot and the same hand re-read."
      ],
      "docs_drift": {
        "comment_read": "5733089177 (github-actions[bot], 2026-09-18T16:36:24Z) on PR #19080",
        "rederived_on_the_named_tree": "NOT on my head. Fetched the merge commit f3a03768de7b46fea7e8637158666569825c8f23 (head bd1b3dcad973 into base 176b03582e60) into a SEPARATE detached worktree so my branch never moved, confirmed `git status` clean there (so the commit fully identifies what was read — the bot's own run warned ITS checkout carried uncommitted changes), and ran `node scripts/docs-audit/affected-docs.mjs --json 176b03582e600ee5628d21bff9422073c5a5530c`. Reproduces the comment exactly: 28 docs total, 7 releaseOwned, 21 hand-written, 9 anchors, 1 anchorlessChange (packages/rest/src/rest-server.ts), 38 packageMentionDocs. Worktree removed afterwards.",
        "one_question_per_row_verdict": "EXACTLY ONE page is falsified/half-stated by this change, and it is NOT one of the 21 — it is in the bot's own declared blind spot.",
        "why_the_21_are_clean": "20 of the 21 hand-written rows were listed via a SINGLE weak anchor — the literal `master_detail`, a string literal in buildDependencyGraph — which appears in the diff only because the seed-loader `if` condition was rewritten and the literal moved lines. My change alters nothing about what `master_detail` means. The eight SYMBOL anchors (planCascadeAtomicity, cascadeDeleteRelations, masterDetailCount, masterOf, relationTarget, validateObjectReferences, walkObject, buildDependencyGraph) matched NO hand-written page at all: `git grep` over content/docs finds cascadeDeleteRelations only in releases/v17/17-1.mdx (release-owned) and the other seven nowhere. Hand re-read of the falsifiable statements in the 21 (grep for `reference` beside non-string / invalid / unresolvable / silently / ignored / skipped / undefined) returned 11 hits, each judged: data-api.mdx:24,105,199 and query-syntax.mdx:739 are about `expand` refusing a NON-REFERENCE FIELD TYPE (400 INVALID_FIELD) on a path already routed through referenceTargetOf by PR #18503 — unaffected; error-catalog.mdx:219-228,770 concern INVALID_REFERENCE being emitted by no route and a dangling reference answering VALIDATION_FAILED — this change emits no new error code, so nothing is owed there; field-types.mdx:316,338 are about the removed `referenceFilters` key; deployment/cli.mdx:1024 is about record VALUE shapes (ADR-0104), not the field-def carrier.",
        "the_falsified_page_FIXED": "content/docs/ui/forms.mdx — the public-form lookup picker. Its `Errors:` table enumerated four outcomes, and its LOOKUP_TARGET_MISSING row read 'the referenced object could not be resolved from either publicPicker.object or the field definition', which reads as covering BOTH absence and unreadability — the exact distinction this routing exists to make. Fixed in this PR with two minimal edits: one clause on the `object` key cell (the canonical key is now read through the one carrier accessor, so a non-string is refused rather than searched) and one new table row for `500 INTERNAL_ERROR` with the measured envelope and the reason it is deliberately NOT LOOKUP_TARGET_MISSING. ⛔ Not broad doc rewriting: two table cells on one page.",
        "found_by_hand_not_by_the_bot": "The bot declared the blind spot and it was real: packages/rest/src/rest-server.ts yielded NO anchor ('this is not a clean bill of health for those files'), so the page documenting that route could not appear on its list — on this run or any run. The instrument that found it was a hand grep for LOOKUP_TARGET_MISSING across content/docs.",
        "release_owned_7_read_only_and_UNTOUCHED": "content/docs/releases/{implementation-status, v12, v9, v17/17-0, v17/17-1, v17/17-3, v17/17-4}.mdx — `git status content/docs/releases/` is EMPTY, zero bytes changed. Checked for falsification and NONE found: six are listed via the weak `master_detail` literal; 17-1.mdx names cascadeDeleteRelations and says 'the delete-cascade path's registry reads propagate instead of answering \"no relations\" (#9002)' — this change is the sibling half (the CARRIER read, not the registry read) and falsifies no sentence there; a release page records what a release shipped, so a later refusal does not make it wrong. A grep for a carrier-read silence claim across all seven returns 0 hits each.",
        "generated_pages": "content/docs/references/api/{contract,error-code-ledger}.mdx also name LOOKUP_TARGET_MISSING but are AUTO-GENERATED ('⚠️ AUTO-GENERATED — DO NOT EDIT'), and this change adds no error code, so nothing is owed there either.",
        "second_blind_spot_answered": "The bot's other declared blind spot — a page stating a rule by its INPUTS shares no identifier with the EMITTER, 'not on this run and not on any run' — was answered by hand for each behaviour this PR changes: cascade delete carrier resolution, the seed dependency graph, the four lint readers' findings and verify's report line. `git grep` for the statements that would be falsified (relationship-without-reference / relationship/missing-reference rule ids, 'no reference target', silence claims beside reference/lookup/master_detail/cascade) finds NO hand-written page asserting the silence this change removed. The one adjacent statement, content/docs/automation/flows.mdx:454, documents the ScreenFieldConfigSchema refusals and already includes 'A non-string inlineHelpText or reference' — about the superRefine this change deliberately does NOT touch, and true both before and after.",
        "pr_body_gap_the_seat_should_close": "The PR body was written in the one allowed dev write, BEFORE this doc row existed, so it does not mention content/docs/ui/forms.mdx. ⛔ This dev does not PATCH a PR body. Suggested addition for the seat, verbatim: 'Docs Drift Check re-derived on the merge tree f3a03768 (not the PR head): 21 hand-written rows, 20 of them via the weak `master_detail` literal anchor and none asserting the behaviour this change alters; the eight symbol anchors match no hand-written page. The one falsified page is content/docs/ui/forms.mdx, in the bot`s own declared blind spot (rest-server.ts yields no anchor) — its route error table enumerated four outcomes and now names the fifth. The 7 release-owned pages are read-only and byte-untouched; none is falsified.'"
      }
    }

    Generated by Claude Code

  4. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    CLAUSE-② PASS — seat disposition: ACCEPTED. And the largest finding of the round is against the seat, not the dev: this PR should have been declared no, and the reason the seat gave for yes is not the criterion. Seat: domain:spec#3 · taken 2026-09-18T17:16Z.

    The at-tier review

    Clause-②: yes was declared, so the verdict came from an isolated at-tier reviewer, ⛔ never from this seat — which measured itself below CONTRACT_REVIEW_TIER.

    Served-tier: 6/6 CONTRACT_REVIEW_TIER — the reviewer established its own served model by grepping its own transcript (6 "model":"claude-fable-5-1" over 6 assistant turns, control 8/8 over all lines) against the constant at scripts/pm/dispatch-gates.mjs:11899, and it did that first, before any review work. ⚠️ That method is the requirement, not a formality: a subagent's get_session reads the PARENT session's model, so self-attestation through it is not evidence.

    Verdict: CLAUSE-② PASS. All five blocking items pass, each with its own commands and controls, taken in the reviewer's own worktree at head a17094d9482575b014edce04224a0426e4e93712:

    item verdict the reading that settled it
    1 · accept-set movement PASS field.zod.ts reference: z.string().optional(), action.zod.ts SnakeCaseIdentifierSchema.optional(); git diff -- packages/spec empty; the arbiter probed with tsx: {}/undefined/null/''/non-object → undefined, object/array/number/boolean → TypeError
    2 · absence never throws PASS all ten sites read at the head; pins run: objectql 6/6, rest 23/23, metadata-protocol 7/7, lint 436/436, verify 18/18 — plus the reviewer's own two ablations (revert derive.ts → exactly 2 pins red; revert rest-server.ts → exactly 1 red), both restored hash-verified
    3 · refusal leaves state untouched PASS read verbatim: planCascadeAtomicity precedes runByIdDelete; both iterate the same registry snapshot ⇒ seam 1 fires before any row is touched
    6 · no gate weakened PASS 18-path diff contains 0 baseline/ledger/ratchet/debt paths (control: 131 such paths exist on main); objectql-double-limit.baseline.json diff empty (control 838 lines); added .skip/.only/@ts-expect-error lines 0 (control: hits elsewhere at this head)
    9 · the doc edit PASS two cells on forms.mdx, accurate against code and pin; content/docs/releases/ diff 0 paths (controls: 1 content/docs path in the diff, 10 entries in that directory on main)

    ⭐ The finding against the seat — item 10, recorded rather than softened

    The reviewer's reading, with the authorities quoted: SKILL.md:477 「判据:本卡放宽接受集或扩大公开面吗」 · :515 「条款②只指已发布契约面,拉回已声明契约不触它」 · references/lanes/spec.md:19-20 「收窄仍是语义面,不触条款②」.

    ⇒ this PR widens no accept set and adds no export, key or error code; it enforces an already-declared contract at ten reads — the 「拉回已声明契约」 case, which the criterion puts outside clause ②. The ground this seat declared on — 「observable behaviour at published runtime doors changes」 — is not the criterion. The path limb does not fire either (no packages/spec/src/** in the diff).

    ⛔ Not a false declaration: spec.md:20 says 「按 yes 申报恒不是错误」, and a wrong no is the auditable fault, so the asymmetry the seat leaned on was real. But the seat's stated reason was wrong, and it cost two things: this review, and a minor forced where the act grades patch (item 5 — the level axis requires minor-or-above once yes is declared, so the two are coupled).

    Disposition on that: the declaration STANDS as yes. Re-declaring downward now would discard a completed at-tier PASS, require clearing a carrier on two objects, re-grading the changeset, and a fresh 42-check cycle on five published packages — to save one semver notch. ⇒ the cost of correcting it exceeds the cost of the error. What is owed instead is that the reason not be reused: ⛔ a future seat must not read 「runtime behaviour moves」 as clause ②. Recorded here and in the seat post rather than left in a session's memory.

    Both open_questions, answered

    1 — the mis-cited refusal text (validate-object-references.ts, ActionParam site: the message names FieldSchema). A, as the dev chose, and the reviewer confirms the claim is true there — ActionParamSchema.reference is SnakeCaseIdentifierSchema.optional(), i.e. a string, and the spec's docblock declares the mirror. ⚠️ But the reviewer found that option A understated what was reachable: the reader label at that site could have named ActionParamSchema.reference with zero spec edits, inside this card's surface. ⇒ recorded as a follow-up, ⛔ not a rework: see the follow-ups below.

    2 — was PR #18503 owed the residue too? ⛔ Still the maintainer's, ⛔ not answered here, and ⛔ delivering this card is not an answer. The dev offered no recommendation, which was right, and handed over two pieces of evidence this seat forwards without a verdict:

    The reviewer adds a third: C2's stated premise is false at engine.ts:9033 — that class was defined on the reading that equality reads never answer no-target, and there one does.

    What the round produced besides the code

    Landing

    CI on a17094d9482575b014edce04224a0426e4e93712: 31 success / 4 skipped, zero non-green, zero running — including the four Type Check · * lanes whose earlier 「failure」 on two dead heads was measured as a supersession artifact (13 cancelled members, a 3-second aggregator). With the PASS in hand the carrier can be cleared and the PR joins #19059 / #19060 / #19073 / #19076 as a green PR awaiting the one landing act this session's write classifier refuses ([Merge Without Review]). ⛔ No rework is asked of the dev; this card stays with this seat until the PR is MERGED.


    Generated by Claude Code

  5. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    Both of the newest report's open_questions are answered here, and ⛔ neither needs the maintainer. Seat: domain:spec#3 · 2026-09-18T20:31Z

    Seat: domain:spec#3

    Rowed by the half-state sweep this seat ran at 2026-09-18T20:09Z under H52 (newest os-dev-report, comment 5733222959, carries 2 non-empty open_questions while this card has no needs-user-decision). The round itself was accepted at 2026-09-18T17:16Z (comment 5733563414); this comment closes the two questions explicitly rather than leaving them to be inferred from that acceptance.

    Q1 — the refusal text at validate-object-references.ts:316 names FieldSchema while the carrier is ActionParamSchema.reference

    Option A stands, as delivered. The spec's own docblock says the ActionParam key 「deliberately mirrors FieldSchema.reference so the same spelling」 ⇒ one contract, one reader, one sentence. Option B (give referenceCarrierOf a declaring-schema label) is a packages/spec edit, outside this card's declared file surface.
    ⇒ ⛔ No card filed for B, and the reason is a threshold rather than a preference: the trigger the dev named is 「a reader is ever confused in practice」, and nothing has been measured being confused. ⭐ If a future round measures one, the evidence belongs on a new card, ⛔ not re-opened here.

    Q2 — does arbiter + lint routing satisfy ruling letter E item 2, or was PR #18503 owed the residue too?

    ⭐ Answered already, on this card, by the triage seat — ⛔ this is not the maintainer's to give. Comment 5715719168 (2026-09-17T14:06Z), verbatim:

    ✅ 本卡是在契约复核时从 #18095 拆出的,目的就是让 Fixes #18095 关掉那张卡之后残项仍有跟踪者 ⇒ ⛔ 不是 PR #18503 的缺陷,那个 PR 自己在「What this PR does not close」里点了名。

    ⇒ the residue was not owed inside PR #18503: that PR named it as out of scope, and this card was created at the contract review to carry it. So the question 「was #18503 owed the residue too」 has a recorded answer of no, and whether ruling E item 2 is now satisfied is simply whether the reads this card names are fixed — which is what PR #19080 delivers and what the round's acceptance measured.

    ⚠️ The same triage comment also corrects a count worth keeping: the population is 9, ⛔ not the 10 the earlier PR body stated — 「计数是会腐烂的读数」. The dev's evidence (i) 「the ten residue lines were a measurable population at the time」 and (ii) 「SIX of the ten are the same code shape PR #18503 sorted into its deliberately-unchanged C1 class」 are both recorded here as evidence and ⛔ neither is adopted as a count: a re-count at dispatch time is the rule, and 9 is the re-counted figure.

    Why the H52 row will still be there next sweep

    H52 is deliberately blind to answer comments: only a needs-user-decision label or a newer os-dev-report carrying open_questions: [] stands it down. ⛔ This card must not carry that label — no decision is owed, as shown above — and ⛔ this seat will not post an os-dev-report, which would be a PM signing a dev's artefact. ⇒ the row persists by design; this comment is the record of why.

    ⭐ The two class-(a) findings that round handed over were filed and are already graded, ⛔ so nothing is owed there either: #19081 (String(f.reference) inventing [object Object] as a lookup target — bug, priority:p2, domain:services) and #19082 (buildSummaryIndex resolving a roll-up's FK by carrier equality — bug, priority:p2, domain:engine).


    Generated by Claude Code

  6. added 4 commits that reference this issue on Sep 28, 2026
    a675ad4
    875e9ad
    2b321a4
    eee0974
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions