Skip to content

chore(spec): quote the props-read sites of three ComponentPropsMap rows, ASSERTED_ANCHOR_FLOOR 7 to 10 - #22038

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20471-pin-anchor-quotes
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20471-pin-anchor-quotes

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20471

Clause-②: no (docblock text and a gate constant: the accept set and the public type surface are unchanged)

#20471 remains open. This PR quotes the load-bearing anchor of three of the six ComponentPropsMap rows the card names. The other three rows (action:group, action:menu, element:repeater) cannot take a quote yet: their records cite lines that do not hold at the pin they assert. That has to be re-measured first, which is outside this card's claimed surface. Measurements below.

What changed

  • packages/spec/src/ui/component.zod.ts: three docblock anchors gain a quoted first line, which check:objectui-pin-citations checks against objectui at the pin (.objectui-sha = a58626c88):
    • action:button: action-button.tsx:215-318 first line type: schema.actionType,, the head of the forwarded: ActionDef literal.
    • action:icon: action-icon.tsx:160-211 first line type: schema.actionType,, the same literal in that renderer.
    • element:definition-list: data-list.tsx:42-47, quoting its first line, the opening line of the readProps call (in the source, const props = readProps followed by the generic's opening bracket and brace).
    • Each assertion spells its path. A bare :215-318 continuation inherits the nearest preceding FULL anchor, which is static-params.ts:91-101 for the two action rows and action-group.tsx:40-42 for the definition list, so it would be checked against the wrong file.
  • packages/spec/scripts/check-objectui-pin-citations.ts: ASSERTED_ANCHOR_FLOOR goes from 7 to 10, exactly the three quotes. Its docblock names the three and why the other three rows carry none. It also replaces the stale "145" worklist count with 418, as --verify-anchors reads it at a58626c88.
  • .changeset/20471-component-row-anchor-quotes.md: a patch changeset for @objectstack/spec. See "Changeset" below.

Which anchor is load-bearing, per row

The reading I used: a row's load-bearing anchor is its props-read site, the one place in objectui where the row's key set is read as a set. For the element rows that is the readProps call, whose type literal is the key set. For the leaf action rows it is the annotated forwarded: ActionDef literal, where the keys handed to the runner are enumerated: 23 of action:button's 28 declared keys and 21 of action:icon's 25, measured from the built schemas. The rest (icon, variant, size, visible, disabled) are first read above the literal, so a line shift there moves the quoted line too. Per-key lines inside or beside that site are not quoted. Quoting them all would be roughly 60 quotes across six rows, the bulk fill triage excluded. One quote also already goes red on any line shift above it in the same file.

Row Load-bearing anchor Holds at a58626c88? Quoted
action:button action-button.tsx:215-318 (runner forward) yes, file byte-identical across the last hop yes
action:icon action-icon.tsx:160-211 (runner forward) yes, byte-identical yes
element:definition-list data-list.tsx:42-47 (readProps) yes yes, but the quoted line is not unique in the file (also :125, the repeater's readProps): it verifies, and drift still goes red, but the relocation report cannot name the new line (ablation C)
action:group actions :303 and member forward :329-382 no, at :300 and :325-374 no
action:menu actions :342 and member forward :264-328 no, at :335 and :261-321 no
element:repeater readProps :97-107 no, at :125-135 (:97-107 is the definition list's registration) no

Every other anchor in the action:button and action:icon records was re-read too, and all hold except one (next table). The definition list's per-key reads :48, :49, :51-53, :63, :66 and :68 hold.

Finding: the records of three rows (and two lines in two more) do not hold at the pin they assert

The section's asserting citation reads .objectui-sha = a58626c88. Its history note for that hop says only the objectName forward moved in action-group.tsx / action-menu.tsx, and that it "is cited below at this pin". Measured at a58626c88 against the previous pin 0abd4f9f8 (git diff, per-line reads):

Row Record cites Read at objectui a58626c88 Since
action:group actions :303, actionRendersAt :304, own visible :290 :300, :301, :287 (-3) 299a2c62
action:group member forward :329-382, its static values :329-335, objectName :381 forward :325-374, objectName :373 (-8). The static-values read was deleted by objectui#11638, so that parenthetical now describes nothing 299a2c62
action:group display :404; label / icon :424 / :408; variant :414 + :456; size :415 + :457; visible :401; host disabled :418; registration inputs :487-528 :396; :416 / :400; :406 + :448; :407 + :449; :393; :410; :479-520 (all -8) 299a2c62
action:menu own visible :235 + :336; icon :240; variant / size :241 / :242 :232 + :329; :237; :238 / :239 299a2c62
action:menu actions :342; member forward :264-328, its static values :264-270, objectName :327; ActionAutoTrigger :363-370; tags :408 :335; forward :261-321, objectName :320, static-values read deleted (objectui#11638); :356-363; :401 299a2c62
action:menu label :389 + :398-399; host disabled :388; registration inputs :435-463 :382 + :391-392; :381; :428-456 (-7) 299a2c62
element:repeater readProps :97-107, every per-key read (:120, :128, :143-146, :152-:155, :181, :186, :191-192, :194-196) and data-list.tsx:119-120 every one 28 lines lower (readProps at :125-135) 0d421041 (pin 31971ff1e, objectui#11168 slice 2); the record went through 7 pin commits, each of which recorded data-list.tsx as byte-identical and its anchors as unmoved
element:repeater data-objectstack/src/index.ts:4782-4793 for serializeOrderBy :4782-4793 is rawFindWithPopulate's pagination. serializeOrderBy is declared at :868 and called from find at :5031 not dated
action:button readStaticParamValues, static-params.ts:91-101 declared at :98 (+7; objectui#11638 rewrote that file) 299a2c62
element:definition-list "The registration's enum publishes the STRINGS '1' / '2' (:82)", "The registration marks it required" :82 is a comment. The columns input publishes the NUMBERS 1 and 2, and items is no longer required: objectui#11168 slice 2 aligned the registration with the row. Prose, not a line shift 0d421041
element:definition-list designer columns control, previews/block-config.ts:307 :320 not dated

Proof that data-list.tsx moved at 0d421041: its blob is 5237dc269 (220 lines, RepeaterRenderer at :96) at dd3f7e1be, db11afd49 and e420df310. It is e54574ef4 (262 lines, RepeaterRenderer at :124) at 31971ff1e and a58626c88. At e420df310 every repeater anchor and the :82 claim read exactly as recorded.

This is the failure #20471 was filed to prevent, already happened: under a pin label that was current, the read points moved and nothing went red. I left these records untouched. The claim's surface is quotes plus the floor, and the dispatch says a non-holding anchor is reported, not re-pointed. The re-point for action:group / action:menu is also not purely mechanical: the two "static values" parentheticals describe a read that no longer exists. How to finish the remaining three rows is the open question in the report on #20471.

Gates (run at ba23f992)

  • pnpm --filter @objectstack/spec check:objectui-pin-citations with objectui readable at the pin: exit 0. The self-test is green including its live round-trip; "10 anchor content assertion(s) verified against objectui at a58626c88; 496 file:line anchor(s) seen".
  • … exec tsx scripts/check-objectui-pin-citations.ts --verify-anchors: exit 0. "10 asserted, 418 objectui anchor(s) still carry no content assertion."
  • The same ordinary run with no objectui checkout (CI's shape): exit 0, printing "10 anchor content assertion(s) NOT VERIFIED — no git checkout". This gate does not verify content in CI by design. A quote is checked when it is written, and at pin bumps with --verify-anchors.
  • Before the floor was raised the gate went red, "You ADDED coverage — set ASSERTED_ANCHOR_FLOOR to 10". So exactly 3 assertions were parsed, and none mismatched.
  • node scripts/pm/dispatch-gates.mjs --commands derived 82 commands at ba23f992 from the merge base. --ran reconciliation: "82 derived, 81 run, 1 NOT-MEASURED, 0 UNRUN", every exit code recorded before any pipe.
    • NOT MEASURED: pnpm check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET). It loads every package's built entry, and 87 workspace packages are unbuilt in this worktree. This diff moves no emitted JS (below). CI runs it on a full build.
    • check:doc-formula-expressions and check:lean-entry-closure first exited 3 for missing builds. After turbo run build of formula, lint and objectql (14 tasks, exit 0) both exited 0.
  • pnpm --filter @objectstack/spec typecheck: exit 0. Its check:scripts-typecheck compiles the edited script (tsc -p tsconfig.scripts.json --listFilesOnly lists it).
  • pnpm --filter @objectstack/spec exec vitest run --project local: exit 0, 619 files, 18485 tests passed.
  • pnpm --filter @objectstack/spec build: exit 0, tree clean afterwards.

Ablation (gate goes red when a quoted line changes)

Each leg ran through scripts/ablation-replace.mjs in WRAP mode. Each anchor hit exactly once, the mutation was proven on disk by blob change, and the restore was proven by blob == HEAD (5cc2ff27) with git diff HEAD empty. No dist sits between the gate and the edited file: it reads src with readFileSync.

  • A, quote altered (type: schema.actionType, to …actionTypeX,): exit 1. "the asserted first line does not match … That line is NOWHERE in …/action-button.tsx at this pin."
  • B, anchor drifted by one line (:215-318 to :216-318, a pin-bump shift): exit 1. "That line IS in the file — at :215, not :216. Either the anchor drifted by -1 lines, or it was wrong when written."
  • C, the same drift on the definition list (:42-47 to :43-47): exit 1. "That line appears 2 times in the file (:42, :125), so it cannot locate the read point on its own." Still red, but without the relocation hint.

Changeset

patch for @objectstack/spec, measured. files[] ships src/**/*.zod.ts verbatim, and after the build the new text is in 2 dist declaration files and 0 runtime JS files. Positive controls: the row docblock hits 2 declaration files, and a .describe() string hits 14 JS files. So the published bytes move, while the runtime surface does not. Local precedent agrees: 15fe567c, a comment-only re-anchor of src/** (0 non-comment lines), shipped a patch changeset. No skip-changeset. The script change alone would not publish (scripts/ is not in files[]).

Acceptance notes

  • The definition list's quote is not unique in its file (see ablation C). Making it unique would mean re-anchoring a record that is correct, so it stays as the gate prescribes: the range's first non-blank line.
  • The member docblock on actionContainerMemberShape is a HISTORICAL record (.objectui-sha pin 2e818d0b51ec), so it got no quote. A quote is checked against the CURRENT pin, which would turn a dated record into a current claim.

Generated by Claude Code

claude added 2 commits October 6, 2026 19:58
…ws and raise ASSERTED_ANCHOR_FLOOR to 10

action:button and action:icon quote the first line of their runner-forward
literal, element:definition-list quotes its readProps call, each re-read at
objectui a58626c88 and spelled with its path so the assertion does not
inherit the nearest preceding anchor's file. The floor rises by exactly
those three. action:group, action:menu and element:repeater get no quote:
their cited lines do not hold at that pin.

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

The edited docblocks ship verbatim in @objectstack/spec (files[] carries
src/**/*.zod.ts), so the comment-only change publishes text.

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

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/ui/component.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/ui/component.zod.ts) — pages documenting those are invisible to this run
  • 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 — 139 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 860152604206718bdc849b09ca9b527e92933f8b → packageMentionDocs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:ui tooling labels Oct 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ba23f992c726c7895ff05dec6875d3ae27b9f172
Local-runs: none

Inputs: card #20471 (body and all eleven comments, the os-dev-report 6025479851 included), PR #22038 (body, three-file list, net diff against main at the head), the 32 check-runs on the head as read at 2026-10-06T21:22Z (read once, not polled), objectui at the pin a58626c88dc8 read with git show, and main (86015260, the PR's base) read from the local checkout. Nothing was built, run or re-run; the one count below is a grep-and-awk reading of files already read.

① Derived judgments

  • Accept set and public type surface: unchanged. Right. The nine changed lines in packages/spec/src/ui/component.zod.ts all sit inside docblocks (every added line is a * comment line); no key, default, enum member, .describe() string, type or export moves. The script edit is one constant and its docblock. What does move is published bytes: @objectstack/spec's files[] ships src/**/*.zod.ts verbatim, so the three docblock edits are released text. Judged in ② below.
  • Three quotes, each true at the pin. Right. Read with git show a58626c88dc8:… in objectui:
    • packages/components/src/renderers/action/action-button.tsx:215 is type: schema.actionType,, the first property line of the const forwarded: ActionDef = { literal that opens at :205; the line is unique in the file. The range :215-318 ends on the objectName forward (:318).
    • packages/components/src/renderers/action/action-icon.tsx:160 is type: schema.actionType,, the same literal (opens :147), unique in the file; :211 is its objectName forward.
    • packages/components/src/renderers/basic/data-list.tsx:42 is the readProps opening line (const props = readProps, then the generic's opening angle bracket and brace) and :47 closes the call. The same line also sits at :125 (RepeaterRenderer's readProps), exactly as the PR's ablation C reports: drift still reds, the relocation hint cannot name one line. Quoting it as-is is the gate's own prescription (the range's first non-blank line), and making it unique would re-anchor a correct record. Right.
    • Each spelled path resolves to exactly one file at the pin (one action-button.tsx, one action-icon.tsx, one data-list.tsx in objectui's 7754-file tree), so the gate's suffix resolution is unambiguous.
  • Syntax is the gate's. Right. Each assertion is a backticked path:range, the words first line, and a backticked quote, which is ASSERTION in check-objectui-pin-citations.ts (:460-463 on main); the wrap onto the next comment line is joined with a space by flatten, which the pattern's whitespace run absorbs. No bare near-miss form.
  • Spelling the path was necessary. Right. A path-less continuation inherits the nearest preceding FULL anchor. On main that is static-params.ts:91-101 (component.zod.ts:3416) for both action rows and action-group.tsx:40-42 (:3902) for the definition list, so a bare :215-318 / :160-211 / :42-47 would have been checked in the wrong file. The PR body's claim is measured true.
  • Floor delta equals the quotes added. Right. main carries 7 assertions (all in src/ui/view.zod.ts:2187-2225); the diff adds exactly 3 (all in component.zod.ts); ASSERTED_ANCHOR_FLOOR goes 7 to 10. The self-test holds the floor as an equality against the live count, and the CI job that runs this gate on a checkout of this repo alone (Type Check · source gates, lint.yml:5930, ordinary mode plus --self-test) is success on the head.
  • The new gate docblock's sentences, one by one:
    • "re-read at pin a58626c88": .objectui-sha on main is a58626c88dc85954bd0af24f16ebb69454c03eee. True.
    • "the forwarded: ActionDef literal of action:button and of action:icon, where the keys each row forwards to the runner are enumerated": the two ranges are those literals' property lists (above). True.
    • "the readProps call of element:definition-list, whose type literal is that row's key set": the literal names items, columns, inline and className; the row declares items, columns and inline (className is a component-level key the row leaves to COMPONENT_LEVEL_GUIDANCE). True for the row's own keys, one word loose on className. Unpublished text (scripts/ is not in files[]): a nit, not a defect.
    • "their cited lines do not hold at that pin" for action:group, action:menu and element:repeater: measured true. action-group.tsx:303 at the pin is const handleExecute = useCallback( (the actions read is :300-301); action-menu.tsx:342 is a comment line (the actions read is :335); data-list.tsx:97-107 is the definition list's registration columns input (RepeaterRenderer's readProps is :125-135). action-button.tsx and action-icon.tsx are byte-identical across the 0abd4f9f8 to a58626c88 hop; action-group.tsx, action-menu.tsx and static-params.ts are not.
    • "418 objectui anchors in packages/spec/src still carry none, read against objectui a58626c88": reproduced from read data with the gate's own rule (every full anchor in .ts / .tsx / .mts under packages/spec/src, kept when its path resolves to exactly one file at the pin, dropped when a content assertion claims the same file, target and range). 496 full anchors at the head (493 on main plus the 3 this diff spells out; the PR's "496 seen" agrees), 427 resolve to one file, 9 of those carry an assertion (the tenth assertion is the :420 continuation in view.zod.ts, not a full anchor), 418 remain, 239 of them in component.zod.ts. True. Replacing the stale "145" with a count tied to a named pin is the right form for a number that moves at every bump.
    • "--verify-anchors prints that worklist": main's gate does (:1240-1266). True.
  • The three shipped docblock sentences ("Forwarded to the runner (action-button.tsx:215-318 first line …)", "Forwarded (action-icon.tsx:160-211 first line …)", "through readProps (data-list.tsx:42-47 first line …, properties first)") are each true at the pin; the pre-existing claims around them (:216, :220, :48, :49, :63, :66, :68, properties first) are not re-judged here.
  • The "load-bearing anchor" reading. Right. One quote per row at the props-read site (the annotated forwarded literal; the readProps type literal) is a defensible reading of triage's "the read points each row's key set rests on": it is where the key set is read as a set, a line shift anywhere above it in the same file also reds (the mismatch report names the relocated line), and quoting every per-key read would be the bulk fill triage excluded (6022454625, 6023252757).
  • Check-runs on the head, as read: 15 success (Auto Label, Part-of PR must not also close its card, The card this PR closes must claim this branch, Type Check · source gates, Type Check · debt ledger, Governed Surface Queue Guard, Flag docs affected by code changes, No other open PR may claim the same single-writer path, No other open PR may claim the same issue, Check PR Size, Check Changeset, filter, Check Documentation Links, Spec property liveness); 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in)); 14 in_progress (Build Core, Test Core 1 to 6, Dogfood Regression Gate 1 to 3, Dogfood Verify CLI, Temporal Conformance, Type Check · consumer gates, Type Check · workspace, Lint & Repo Gates). None failure. The gate families this diff can move (pin citations and the floor equality, changeset, Part-of) are among the completed successes; the in-progress ones are build and test lanes a comment-only diff does not reach. This record does not wait on them: their conclusions are the gate verdicts when they land.

② Semver level

  • patch for @objectstack/spec. Right, and skip-changeset would have been wrong. files[] ships src/**/*.zod.ts, so the edited docblocks are published bytes (and TSDoc reaches the dist declarations); a diff that publishes from a released package takes a changeset. The script edit alone would publish nothing (scripts/ is not in files[]). Precedent on main agrees twice: 15fe567c (docs(spec): re-anchor the last six dead tracker citations in packages/spec/src to the commits that decided them #21642), a comment-only re-anchor of src/**, shipped '@objectstack/spec': patch with a bare no declaration; and .changeset/objectui-pin-citations-a58626c88dc8.md, the last pin bump's citation re-read, is the same shape.
  • The changeset body (.changeset/20471-component-row-anchor-quotes.md) names the three rows, the pin, what now reds, that the other three rows carry no quote yet, and closes "Comment text only: no schema, key, type or export changes". Each sentence is true per ①. It carries the declaration line bare, and no ADR-0087 marker is owed (nothing is breaking). Check Changeset on the head is success.
  • The PR body's declaration reads as no: the line is Clause-②: no (docblock text and a gate constant: the accept set and the public type surface are unchanged), and the fleet's one reader (scripts/pm/clause2-line.mjs, readArmToken) takes the value and leaves a parenthetical that names no arm alone, which Check Changeset confirmed by passing. no is the right answer: no accept-set widening, no public-surface growth, no narrowing.

③ Boundary flags

The dev's report is 6025479851 on #20471. Every deviation, the open question and both out-of-scope findings, answered or escalated:

  • Deviation 1, Part of #20471 rather than a closing keyword: right. The card's landing site names six rows; this head quotes three. A closing keyword would have marked a half-delivered card finished on merge, the exact failure the Part-of Closing-Keyword Guard exists for, and that guard is success on the head. The card stays open, correctly.
  • Deviation 2, a patch changeset over the dev agent's "comment-only does not publish" fast lane: right. AGENTS.md's rule and the measured files[] govern; see ②.
  • Deviation 3, "145" replaced with "418" in the floor's own paragraph: right, inside the claimed file, and the number is established (①).
  • Deviation 4, check:dual-build-cjs-loads not measured locally (87 unbuilt packages): accepted as a declared narrowing. CI hosts that gate on a full build (ci.yml:2319, inside Build Core), in_progress as read. The dev's positive controls (the new text reaches 2 declaration files and 0 runtime JS files) cannot be repeated read-only, so Build Core's conclusion is the verdict of record for it.
  • Deviation 5, the PM's saved gate file used as a lead, 82 commands re-derived at the head and reconciled (81 run, 1 not measured, 0 unrun): fine. Re-deriving at the head is the rule, not a departure from it.
  • Deviation 6, read-only depth-1 object fetches into /home/user/objectui (FETCH_HEAD moved; no ref, branch or worktree touched): acceptable. Reading the pin's objects is what a citation re-read needs; this review read the same objects.
  • open_questions[0], how the remaining three rows (action:group, action:menu, element:repeater) get their quotes: answered A, escalated to the seat to file. Their records cite lines that do not hold at the pin (verified in ①), and a quote goes only beside a line that does. Option A, a sub-issue of [finding] the six ComponentPropsMap rows #20371 added cite about a hundred objectui anchors by line number alone: no quoted first line, so --verify-anchors checks none of their content #20471 that re-measures those three records at the pin (plus action:button's static-params.ts:91-101, where readStaticParamValues is declared at :98 at the pin, and element:definition-list's :82 registration prose, which the pin's registration no longer matches: the columns enum publishes the NUMBERS 1 and 2 at :98-107), re-words the two "static values" parentheticals objectui#11638 emptied, then quotes each row's load-bearing anchor and raises the floor by that count, keeps one PR to one decision and matches the gate's own rule that a moved read point is reported, not re-pointed, under a quote ratchet. B would breach the claimed surface and put about 45 line edits under a review scoped to quotes. C is the route the card's own history records failing four times (6022454625). The filing is the dispatching seat's act, not this record's; the measurement to file from is in the PR body's "Finding" tables.
  • out_of_scope_findings[0], stale read-point records in section 4b (class a, and class b against the section's "each is cited below at this pin"): confirmed and escalated into that same sub-issue. Section 4b's hop note (component.zod.ts:3207-3219 on main) says the a58626c88 hop moved only the objectName forward in action-group.tsx / action-menu.tsx; measured, the actions reads and the member forwards moved too (:303 to :300-301 and :329-382 to :325-374; :342 to :335 and :264-328 to :261-321), and every element:repeater anchor in data-list.tsx sits 28 lines lower than recorded (blob 5237dc269, 220 lines, through e420df310; blob e54574ef4, 262 lines, at 31971ff1e and a58626c88). These docblocks ship in @objectstack/spec, so each release freezes the wrong read points until they are re-measured. Leaving them untouched in this PR is right: the claimed surface is quotes plus the floor.
  • out_of_scope_findings[1], the non-unique data-list.tsx quote and the un-quoted historical member docblock (.objectui-sha pin 2e818d0b51ec): noted, not filed. Agreed. A quote is checked against the CURRENT pin and would turn a dated record into a current claim; the non-unique line still reds on drift.
  • Serial constraint: triage's "an open bump PR that edits component.zod.ts goes first". The claim (6024139898) recorded none open, and the head's No other open PR may claim the same single-writer path is success.

Implemented-by: claude/issue-20471-pin-anchor-quotes
Reviewed-by: session_01GV6oYwgc1kWiUCb1YaprQ7

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 6, 2026 21:45
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 6, 2026 21:45
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit 0af4f66 Oct 6, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20471-pin-anchor-quotes branch October 6, 2026 22:27
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…eater records at the objectui pin and quote their props-read sites, ASSERTED_ANCHOR_FLOOR 10 to 13 (objectstack-ai#22043)

Fixes objectstack-ai#20471

Clause-②: no (docblock text and a gate constant: the accept set and the
public type surface are unchanged)

Stage 2 of objectstack-ai#20471, as the landing record of stage 1 (PR objectstack-ai#22038) scopes
it. All six `ComponentPropsMap` rows the card names now quote their
props-read site, so this PR closes the card.

The `action:group`, `action:menu` and `element:repeater` records cited
objectui lines that did not hold at the pin they assert (`.objectui-sha`
= `a58626c88`). This PR re-measures them at the pin, re-points every
cited line that moved, corrects the prose that was false at the pin, and
then quotes one load-bearing anchor per row. `ASSERTED_ANCHOR_FLOOR`
goes from 10 to 13.

## What changed

- `packages/spec/src/ui/component.zod.ts` (docblocks and one code
comment, no code):
- **Section 4b hop note.** It said only the two `objectName` forwards
moved at the `0abd4f9f8` → `a58626c88` hop. In fact every container
anchor below the deleted `useConfigBagEvaluator()` call moved, and
`static-params.ts` moved too. The note now says so. A new "correction"
sentence records where earlier hop notes certified anchors that had
already drifted (details below). The older dated notes are left as
written.
- **`action:group` and `action:menu` row records:** every moved anchor
is re-pointed (table below). The "static values" parenthetical on each
member forward now describes what is there at the pin: since
objectui#11638, a member's own `properties.params` is no longer read.
- **`element:repeater` row record:** every `data-list.tsx` anchor is
re-pointed (+28), and so are the two `data-objectstack/src/index.ts`
anchors. The `fields` sentence said the renderer's TS type and its
registration description both advertise `label`. Since objectui#11168
slice 2 neither does, and the sentence now says so.
- **`element:definition-list` registration prose:** it said the
registration publishes the strings `'1'` / `'2'` and marks `items`
required. Since objectui#11168 slice 2 it does neither. Both sentences
now give the history and the current lines. The inline comment on the
`columns` literal makes the same correction.
`previews/block-config.ts:307` is re-pointed to `:320`.
- **`action:button`:** `static-params.ts:91-101` is re-pointed to
`:98-108`.
- **The `[objectstack-ai#21855]` member paragraph** above `actionContainerMemberShape`
(patch round `00c6b980`, at the seat's direction, claim revision
`6027563825`): it called `0abd4f9f8` the current pin. Its three anchors
now point at `a58626c88` (group `:325-327`, menu `:261-263`,
`static-params.ts:164-174`), in the asserting spelling, so the gate
re-checks it at the next pin bump. It is an in-place fix: the same
defect class, a mechanical edit, no other claim, and the same gate
family.
- **Three quotes,** one per row, each spelling its path (see "The
quotes" below).
- `packages/spec/scripts/check-objectui-pin-citations.ts`:
`ASSERTED_ANCHOR_FLOOR` goes from 10 to 13, exactly the three quotes.
Its docblock names the three. It also updates the worklist count from
418 to 419, as `--verify-anchors` reads it at `a58626c88` (see
Acceptance notes for why it went up by one).
- `.changeset/20471-component-row-anchor-requote.md`: a `patch`
changeset for `@objectstack/spec`, because `files[]` ships
`src/**/*.zod.ts` and these docblocks are published text, as stage 1
measured.

## Re-pointed anchors

Each row was checked in two ways, read-only, in the local objectui
object store:
- The new line's text was compared with the old line's text at the pin
where the old number last held. They are identical for every row except
the two `paramsPayload` tails that objectui#11638 rewrote.
- The record's claim was re-read against the new line.

| Row | Cited | Old | New (verified line at `a58626c88`) |
|---|---|---|---|
| `action:group` | `actions` read | `:303` | `:300` `const
declaredActions: UIActionSchema[] = schema.actions \|\| [];` |
| | `actionRendersAt` filter | `:304` | `:301` |
| | member forward | `:329-382` | `action-group.tsx:325-374`, QUOTED
(`params` `:325-327`, `objectName` `:381` → `:373`) |
| | `display` | `:404` | `:396` |
| | `label` / `icon` | `:424` / `:408` | `:416` / `:400` |
| | `variant` | `:414`, `:456` | `:406`, `:448` |
| | `size` | `:415`, `:457`, registration `:487-528` | `:407`, `:449`,
`:479-520` (`:124` held) |
| | `visible` | `:290` + `:401` | `:287` + `:393` |
| | host `disabled` (dropdown trigger) | `:418` | `:410` (`:274`, `:159`
held) |
| | held | `:114-171`, `:205-243`, `:81-86`, `action-group.tsx:40-42` |
unchanged |
| `action:menu` | `actions` read | `:342` | `:335` `const actions:
UIActionSchema[] = schema.actions \|\| [];` |
| | members | `:80`, `:108-147`, `:408` | `:80`, `:108-147`, `:401` |
| | `ActionAutoTrigger` | `:363-370` | `:356-363` |
| | member forward | `:264-328` | `action-menu.tsx:261-321`, QUOTED
(`params` `:261-263`, `objectName` `:327` → `:320`) |
| | `label` | `:389`, `:398-399` | `:382`, `:391-392` |
| | `icon` / `variant` / `size` | `:240` / `:241` / `:242` | `:237` /
`:238` / `:239` |
| | `visible` | `:235` + `:336` | `:232` + `:329` |
| | registration `inputs` | `:435-463` | `:428-456` |
| | host `disabled` | `:219`, `:388` | `:219`, `:381` |
| `element:repeater` | `readProps` | `:97-107` |
`data-list.tsx:125-135`, QUOTED |
| | `object` | `:143`, `:155`; `:143-146`, `:181` | `:171`, `:183`;
`:171-174`, `:209` |
| | `filter` / `sort` / `limit` | `:120`, `:152` / `:153` / `:154` |
`:148`, `:180` / `:181` / `:182` |
| | `emptyText` / `divided` | `:181` / `:186` | `:209` / `:214` |
| | `titleField` / `fields` | `:191-192` / `:128`, `:194-196`, `:196` |
`:219-220` / `:156`, `:222-224`, `:224` |
| | `useResolvedFilter` | `data-list.tsx:119-120` |
`data-list.tsx:147-148` |
| | filter arm / `serializeOrderBy` |
`data-objectstack/src/index.ts:4782-4793`, `:760-786` | `:5000-5011`
(`options.filters = translateFilterArray(params.$filter);`), `:868-894`
|
| `element:definition-list` | Studio `columns` control |
`previews/block-config.ts:307` | `:320` |
| | registration `columns` / `items` | `:82` (prose false at the pin) |
`data-list.tsx:99-105` / `:91-97`, prose rewritten |
| `action:button` | `readStaticParamValues` | `static-params.ts:91-101`
| `static-params.ts:98-108` |

## When each anchor moved

These readings use blob ids of each cited file at every pin from
`dd3f7e1be` to `a58626c88`, read in the local objectui object store.
`89cad75d5` and `ab1879721` were fetched read-only at depth 1 for this.

- **`action-group.tsx` / `action-menu.tsx`:** objectui#11638 at the
`0abd4f9f8` → `a58626c88` hop (+10/-18 and +11/-18). Anchors move -3
below the deleted `useConfigBagEvaluator()` call and -8 (group) / -7
(menu) from the member forward on.
- **`static-params.ts`:** byte-identical from `dd3f7e1be` through
`0abd4f9f8`. It gained 7 lines above `readStaticParamValues` at the same
hop.
- **`basic/data-list.tsx`:** changed once, at `e420df310` → `31971ff1e`
(objectui#11168 slice 2 rewrote both list registrations). The repeater
moved by +28. That hop's note does not list the file. Every later note
recorded its anchors as held or re-read in place.
- **`previews/block-config.ts`:** `:307` moved to `:320` at the same
hop.
- **`data-objectstack/src/index.ts`:**
- The `translateFilterArray` arm moved at `db11afd49` (+42), at
`e420df310` and at `ab1879721`.
  - `serializeOrderBy` moved at `e420df310` (+108).
  - Neither moved at the last hop: the file gained 7 lines at its end.

The new correction sentence in section 4b records the three files whose
drift went unrecorded. It does not rewrite the older dated notes.

## The quotes (H3 definition from stage 1: one load-bearing anchor per
row, its props-read site)

- `action:group`: `action-group.tsx:325-374` first line `const
paramsPayload: ActionDef = Array.isArray(action.params)`. This is the
member forward, where every key a member forwards to the runner is
enumerated (`type`, `name`, `label`, `description`, `target`, `openIn`,
`endpoint`, `method`, params, `bodyExtra`, `bodyShape`, `operation`,
`patch`, `confirmText`, `successMessage`, `outcomeMessages`,
`errorMessage`, `refreshAfter`, `locations`, `toast`, `resultDialog`,
`onSuccess`, `objectName`). The range keeps the record's own extent:
from the params payload to the end of the `execute` call. The line is
unique in the file, so a drift is relocated.
- `action:menu`: `action-menu.tsx:261-321`, same first line, same role,
unique in that file.
- `element:repeater`: `data-list.tsx:125-135`, quoting the opening line
of the `readProps` call, the same text stage 1 quoted for the definition
list at `:42` (in the source, `const props = readProps` followed by the
generic's opening bracket and brace). The type literal is the row's key
set. As H3 expected, the line is not unique (`:42`, `:125`), so a drift
goes red without a relocation hint.
- **One quote per row, as the claim says.** Stage 1 described the
containers' props-read site as "the actions read plus the member
forward". The claim and the dispatch both say one quote per row. I chose
the member forward because it is where the member key set is read as a
set, as the `forwarded: ActionDef` literal is for `action:button`. It
also sits below the `actions` read (`:300` / `:335`), so a shift above
either line reds the quote. A quote on the `actions` line would add a
content check of one key read only. That is noted, not done.
- Each quote spells its path. A path-less continuation inherits the
nearest preceding FULL anchor (`inheritedAt` in the gate), which for
these rows would be another file.

## Verification (at `afe6f9df`; the patch round `00c6b980` re-ran the
card gate, the spec build and tests, and the derived gates green, report
`6027887111`)

- **The card's gate, three ways.** All exit codes were captured to a
file before any pipe.
- `OBJECTUI_ROOT=/home/user/objectui … check-objectui-pin-citations.ts
--verify-anchors` = exit 0: `13 asserted, 419 objectui anchor(s) still
carry no content assertion`.
- `pnpm --filter @objectstack/spec check:objectui-pin-citations` beside
the objectui checkout = exit 0: `13 anchor content assertion(s) verified
against objectui at a58626c88; 500 file:line anchor(s) seen`. The
self-test, including its live round-trip, is green.
- The same with no objectui checkout (the CI shape) = exit 0: `13 … NOT
VERIFIED — no git checkout`.
- **Before the floor edit:** the gate was red, exit 1: `You ADDED
coverage — set ASSERTED_ANCHOR_FLOOR to 13`. All 13 assertions verified,
with no mismatch.
- **Ablation, one leg per new quote.** Each leg ran through `node
scripts/ablation-replace.mjs` in WRAP mode, with `--verify-anchors` as
the wrapped command. Each anchor hit x1. The mutation was proven by a
blob change, and the restore by blob == HEAD blob `83976000` with `git
diff HEAD` empty. The final porcelain was clean.
- Leg A, the group range `:325-374` changed to `:326-374`: exit 1, `That
line IS in the file — at :325, not :326. Either the anchor drifted by -1
lines`.
- Leg B, the menu quote `action.params` changed to `action.paramz`: exit
1, `That line is NOWHERE in
packages/components/src/renderers/action/action-menu.tsx at this pin`.
- Leg C, the repeater range `:125-135` changed to `:126-135`: exit 1,
`That line appears 2 times in the file (:42, :125)`.
- **Build, typecheck and tests**, all under the shared lock:
- `pnpm --filter @objectstack/spec build`: VERDICT command-exit 0, and
`git status` was clean afterwards.
- `pnpm --filter @objectstack/spec typecheck`: VERDICT command-exit 0.
`check:scripts-typecheck` compiles the edited script (`--listFilesOnly`:
1 hit).
- `pnpm --filter @objectstack/spec exec vitest run --project local
--maxWorkers=2`: VERDICT command-exit 0, `Test Files 619 passed (619)`,
`Tests 18485 passed | 1 todo`. No test names the edited gate script
(`git grep`: 0 hits). Its own `--self-test`, including the live
round-trip, runs inside the gate.
- **Derived gate families.** `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack` was run with no paths at
`afe6f9df` and derived 82 commands, the same list as at `10006d5d`.
- All 82 were run, with exit codes captured before any pipe. 81 exited
0. `pnpm check:dual-build-cjs-loads` exited 3 (PREREQUISITE NOT MET:
about 77 workspace packages unbuilt), so it is NOT MEASURED.
- That narrowing is declared. This diff moves no emitted JS: the new
docblock text has 0 hits in `packages/spec/dist` JS and 2 in `.d.ts`,
against a positive control of 14 JS hits for a `.describe()` string. CI
runs the gate.
- `--ran` reconciliation: `82 derived, 81 run, 1 NOT-MEASURED, 0 UNRUN`,
exit 0.
- `check:doc-formula-expressions`, `check:lean-entry-closure`,
`check:dts-closure` and `check:exported-any` first refused their
prerequisites while a build was in flight. They exited 0 on the final
pass, after `pnpm turbo run build` of formula, lint and objectql (14
tasks, VERDICT command-exit 0) and the spec rebuild.
- The `check:pm-dispatch-gates` battery (exit 0, `1976 cases pass`) ran
at `10006d5d`. The later commit changes six comment lines in the two
spec files and nothing the battery reads.
- **Lint, as a proven narrowing.** `pnpm lint` (`eslint .
--no-inline-config`) is CI's run. Here:
  - Population: the two touched TypeScript files.
- Count: `npx eslint --no-inline-config --format json` on them reads 2
files, 0 errors, 0 warnings, neither ignored.
- Invariance: `eslint.config.mjs` never enables type-aware linting (no
`parserOptions.project`, stated at its lines 326-328), so this diff
cannot move the verdict on an untouched file.

## Acceptance notes (noted, not filed)

- **The historical `[objectstack-ai#21464]` member docblock** (pin `2e818d0b51ec`)
stays dated and unquoted. Its `[objectstack-ai#21855]` paragraph, which called
`0abd4f9f8` the current pin, was corrected in place in `00c6b980` (see
What changed).
- **`data-list.tsx:99-105` is spelled as a full anchor.** It used to be
the bare `:82`. A bare continuation there would read as
`previews/block-config.ts`. The new full anchor carries no quote, so the
worklist count rises from 418 to 419.
- **The repeater quote is not unique in its file** (H3, as stage 1's
review judged acceptable).
- **Stage 1's changeset**
(`.changeset/20471-component-row-anchor-quotes.md`, not yet released)
says the other three rows "carry no quote yet". This PR adds its own
changeset rather than editing that one.

---

_Generated by [Claude
Code](https://claude.ai/code/session_01GV6oYwgc1kWiUCb1YaprQ7)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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 protocol:ui size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants