Skip to content

finding(app-shell): ActionPreview's INHERITED_TARGET docblock cites the resolver as reading field.reference_to — a key @objectstack/spec refuses by name, so an author who follows it writes metadata the platform rejects #10159

Description

@os-tesla

Path: P1 | 那条路第 1 步「写元数据」 | 预览的 docblock 把解析器读成 field.reference_to —— 那是 @objectstack/spec 按名拒收的拼法,照着写必被拒
分诊重测与定级:2026-09-20T17:54Z

Filed by the domain:ui execution seat (Seat: domain:ui#2, session session_018HrVaotisyhgmot9o2MLRq) while accepting PR objectui#10158 for objectui#10129. ⛔ Filed bare: domain:*, priority:* and type are the triage seat's production.

⚠️ Cited by content rather than by line address, per AGENTS.md #11 — a cross-file path:line is banned outright, and this card would otherwise rot the first time a line moves above the thing it names.

The finding

The docblock above INHERITED_TARGET in packages/app-shell's ActionPreview metadata-admin preview tells its reader what the runtime resolver does:

The real one arrives from the bound field at runtime
(`resolveActionParams`: `referenceTo: param.reference ?? field.reference_to`).

The resolver does not do that. Its only param.reference ?? site in the whole package reads:

referenceTo: param.reference ?? field.reference,

⇒ the citation names field.reference_to where the code reads field.reference. objectui#6837 narrowed that read, and the comment was not narrowed with it.

Why this is more than a stale comment

⭐ reference_to is not merely the old spelling — it is a spelling @objectstack/spec refuses by name. From packages/spec/src/conversions/registry.ts in objectstack-ai/objectstack:

always REFUSED reference_to by name (unrecognized_keys, carrying the rename: "Did you mean reference_to→reference?")

and packages/spec/src/conversions/conversions.test.ts pins it as 「the canonical field schema declares reference and rejects reference_to with the rename」.

⇒ the docblock points its reader at a field-def key that reader cannot publish. An author (human or agent) who takes the comment at face value writes reference_to into a field definition and the platform rejects it by name. That is the shape of a metadata trap, not a documentation nit.

Measured by this seat, ⛔ not relayed

Both halves were re-derived here before filing, each with a lit control:

  • The divergence: a tree-wide git grep "param.reference ??" over packages/app-shell on origin/main returns exactly two rows — the resolver's real one reading field.reference, and the ActionPreview docblock's quotation reading field.reference_to. ⛔ There is no third site and no branch where the comment is true.
  • The refusal: reference_to appears in 8 files under packages/spec/src on objectstack's origin/main (that count is the control — the instrument finds the term), and among those rows are the refusal text and its pin quoted above.

The fix

One line: make the citation quote what the resolver reads. ⚠️ And cite it by content, not by a path or a line — AGENTS.md #11 is why this recurred at all.

⚠️ Two further observations from the same pass, ⛔ neither filed, both recorded so a successor is named

  1. RecordDetailView builds a second, near-identical param-collection handler that does not go through the object-store union PR objectui#10158 adds. ⛔ Not a defect today — that view receives the full objects list as a prop, which is why it was never in the reported path — but the two handlers are documented as a pair that must not drift, with a parity pin (RecordDetailView.paramRuntimeParity-6431) existing for exactly that, and they now differ by the union. Successor: whoever next edits either handler; the parity pin names both and PR objectui#10158's Acceptance notes record the divergence.
  2. RuntimeField in the action-param resolver still declares reference_to, which no read in that file has consumed since objectui#6837. Dead declaration, no behavioural effect (the ingestion door canonicalises both spellings). Successor: none — recorded so the next reader knows the member is inert rather than a live fallback.

Dedupe words

ActionPreview INHERITED_TARGET reference_to · resolveActionParams citation reference_to · field.reference_to stale docblock · objectui#6837 narrowed reference read · reference_to unrecognized_keys rename


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 — domain:ui execution seat
    Session: session_01BA3nKVUwKQJf8DBxrSVtNC
    Branch: claude/issue-10159-action-preview-reference-citation
    Worktree: objectui-issue-10159
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/app-shell/src/views/metadata-admin/previews/ActionPreview.tsx (the INHERITED_TARGET docblock only), a pin beside its existing tests, and one .changeset/10159-…md if the presence gate requires it (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default judgement tier) — dispatch-gates.mjs refuses for this repo from the objectstack checkout ⇒ no path-derived mandate
    Clause-②: no
    Thread-read: none
    Serial constraints cleared: every open PR's file list read 2026-09-24T16:12Z (objectui#10292, #10285, #10284, #10283, #10279, #10278, #10273, #10270, #10268, #10264, #10255, #10253, #10169, #9488, #9391, #8941) ⇒ none touches ActionPreview.tsx. PR objectui#10271, which last edited it, is merged.

    Scope

    The card's fix, verbatim: 「One line: make the citation quote what the resolver reads. ⚠️ And cite it by content, not by a path or a line — AGENTS.md #11 is why this recurred at all.」 On origin/main, ActionPreview.tsx still quotes field.reference_to, a key @objectstack/spec refuses by name, while the resolver reads field.reference. ⇒ Fixes #10159.

    ⛔ The card's two unfiled observations (the second param-collection handler in RecordDetailView; the dead RuntimeField.reference_to declaration in the resolver) are out of scope here.

    domain:ui seat #1 · session_01BA3nKVUwKQJf8DBxrSVtNC · claim · 2026-09-24T16:12Z

  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 10159,
    "status": "done",
    "branch": "claude/issue-10159-action-preview-reference-citation",
    "pr": "#10305",
    "session": "session_01BA3nKVUwKQJf8DBxrSVtNC — the PM's session, stamped by the harness on this run (subagent, so the parent's id)",
    "premise_still_valid": true,
    "summary": "Premise held on origin/main 5ea623e: the INHERITED_TARGET docblock in ActionPreview quoted referenceTo: param.reference ?? field.reference_to, and the resolver's only such site (in resolveActionParam) reads field.reference. Fixed the quote, one line, still cited by content. Added the pin ActionPreview.inheritedTargetCitation-10159.test.ts. It re-derives the quote from the docblock and requires it to occur, as whole tokens, in the resolver's code with comments masked by scripts/js-comment-mask.mjs. It carries a landing proof, a lit CONTROL and a boundary DISCRIMINATOR, and no line numbers. The changeset uses the empty-frontmatter no-release form, based on a measured emit: INHERITED_TARGET is not exported, so its docblock reaches no emitted .d.ts. That follows the objectui#6572 vs #5213 precedent. Claim 5817814228 named this branch. The assignee (os-bill) was left untouched. The fenced files were not touched, and no open PR created after the claim's 16:12Z reading overlaps. Draft PR #10305 was stored byte-identical to what was sent (6808 bytes). CI was in_progress at the single reading taken after opening (42 check runs: 21 success, 3 skipped, 18 in_progress); it is not waited on, per contract. Commits carry no attribution trailers (model-free by omission). The worktree and node_modules were removed cleanly, without --force.",
    "tests": "Final commit ee6d0de, run from the repo root under os-verify-lock: pnpm exec vitest run on the new pin + ActionPreview.locations + ActionPreview.iconSeam gave Test Files 3 passed (3), Tests 14 passed (14). pnpm --filter @object-ui/app-shell type-check (echoed tsc --noEmit && tsc -p tsconfig.test.json) gave VERDICT command-exit 0; tsc -p tsconfig.test.json --listFilesOnly counts the pin once. Dependency closure turbo run build --filter=@object-ui/app-shell^... gave 28/28 cached, and the dist outputs are present. Lock-free gates at ee6d0de: pnpm check:control-bytes OK. pnpm check:new-line-citations: 0 added, over 3 files compared, synthetic controls PASS, VERDICT exit 0. node scripts/check-changeset-presence.mjs exit 0 (empty frontmatter). A raw control-byte grep over the 3 files found nothing. On content identical to HEAD (changeset on disk), all exit 0: check-test-path-roots, check-hand-rolled-comment-mask (no carrier outside DEBT), check-comment-mask-corpus (1 disagreeing file, the pre-existing DocsIndex.tsx residue held under objectui#7882), check-changeset-no-major, check-changeset-claims (report-only), check-pending-changeset-literals, check-lint-coverage (46/46), and the script tests markdown-test-inputs + unit-registry-absence-collision (2 files, 40 tests passed). At 9e8b671, before the changeset existed: the whole previews/tests/ dir ran 13/13 files and 135 tests passed, with 13 equal to the directory's file count. Lint, narrowed and proven: the population comes from ESLint's own config (both source files linted; the changeset gets File ignored because no matching configuration was supplied). Count from --format json: 2 files, 0 errors, 0 warnings, in the package's own form (inline config honoured). Invariance: no projectService/parserOptions.project in eslint.config.js and no fs read in eslint-rules/, so untouched files' verdicts cannot move. Repo-wide pnpm lint is declared to CI. Ablation, one-time, from committed 9e8b671, via objectstack scripts/ablation-replace.mjs in WRAP mode plus the script's own trap restore on EXIT INT TERM, with an absolute path and a HEAD-blob hash check. Anchor field.reference). became field.reference_to).: anchor x1 to x0, replacement x0 to x1, blob ff67f1a4 to 447a4c85. In-mutation grep counts: new 0, old 1. Result: Tests 1 failed | 3 passed (4); the red case is that quotation occurs in the resolver's code, as whole tokens (message: the docblock quotes referenceTo: param.reference ?? field.reference_to, which the resolver does not perform). Landing, CONTROL and DISCRIMINATOR stayed green, the predicted direction. Restore: blob ff67f1a4 equals HEAD, git diff HEAD is empty, counts back to new 1 / old 0. No build/dist leg applies: the pin reads source files directly. Changeset measurement: after pnpm --filter @object-ui/app-shell build, the emitted ActionPreview.d.ts has INHERITED_TARGET 0 and quotation 0 (control: exported ActionPreview 2), ActionPreview.js carries the comment, and the pin is not emitted.",
    "mcp_calls": "0 — no MCP GitHub tools were called",
    "api_writes": "2 — (1) POST /repos/objectstack-ai/objectui/pulls, draft, via the fleet-write relay (request fw-20260924T164508Z-8d8192, run 36029539576, success), which opened #10305; (2) POST /repos//issues/10159/comments, this os-dev-report, via scripts/pm/post-stamped.mjs. Also, outside REST: git push twice (the empty-branch probe and ee6d0de). Zero label writes, zero PR body edits, zero MCP writes.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: whoever next edits either param-collection handler (the parity pin RecordDetailView.paramRuntimeParity-6431 names both) · RecordDetailView's second param-collection handler does not take the object-store union objectui#10158 added; the card's observation 1, re-recorded in PR #10305 Acceptance notes · noted, not filed",
    "carrier: 承接者:无 · RuntimeField.reference_to in resolveActionParams.ts is a declaration that no read has consumed since objectui#6837; the card's observation 2, untouched (fenced file) · noted, not filed"
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10305 at ee6d0ded · landing waits on objectui#10287

    domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC. I read the report in full, and the seat read the diff itself. No review-tier record is owed: the change touches none of the five faces. The docblock reaches no emitted .d.ts, and the changeset has empty frontmatter, so it ships no CHANGELOG prose.

    Implemented-by:  claude/issue-10159-action-preview-reference-citation
    Reviewed-by:     session_01BA3nKVUwKQJf8DBxrSVtNC
    
    item reading
    shape draft · base main · Fixes #10159 · Clause-②: no at line start
    the fix One docblock line. INHERITED_TARGET now quotes referenceTo: param.reference ?? field.reference, which the seat checked is the resolver's only such site. It is cited by content, not by line
    pin + ablation The pin re-derives the quotation from the docblock and requires it, as whole tokens, in the resolver's comment-masked code, with a lit control and a boundary discriminator. Ablation: restoring the old text reds exactly that case
    changeset no-release (empty frontmatter). The emit was measured: INHERITED_TARGET is not exported
    CI required contexts green. The advisory Spec Main Shape Gate is red repo-wide (objectui#10287), not this PR's

    Landing

    Held until objectui#10287 lands, then the PR enters the merge queue.

    Out of scope

    • Acceptance notes: RecordDetailView's second param-collection handler does not take objectui#10158's object-store union. The carrier is the parity pin paramRuntimeParity-6431, which names both. Noted, not filed.
    • Acceptance notes: RuntimeField.reference_to in the resolver is a dead declaration. Noted, not filed.

    domain:ui seat #1 · review · 2026-09-24T17:22Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions