Skip to content

feat(spec)!: action:button / action:icon refuse endpoint with ActionSchema's rename to target, read from one table (#21005) - #21122

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-21005-action-row-endpoint-refused
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-21005-action-row-endpoint-refused

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21005

Clause-②: yes (narrowing)

What changes

action:button / action:icon (ComponentPropsMap) no longer declare endpoint. They refuse it with the rename ActionSchema already prints, read from ONE table, so the action and the two blocks that run it give one verdict for one concept.

  • One table, no second string. packages/spec/src/ui/action-target-aliases.ts holds ACTION_TARGET_ALIASES (url / endpoint / path / href mapped to target). ActionSchema's strictObject aliases spread it in place of its inline row, and the rows' shared ACTION_NODE_ALIASES spreads the same object. The message comes from the one strictUnknownKeyError renderer. Measured on source after the change: the action gives "Did you mean endpoint → target?", and action:button and action:icon give the same clause. The module is reached by relative import only (the filter-rule-array.ts precedent). Exported from action.zod.ts, it would have ridden the ui barrel into the published API surface.
  • Same defect class, same table: the rows used to answer path with the edit-distance guess patch. patch is the declarative write's field values, z.unknown(), and it parses. Reading the whole target row of the table fixes that too: all four aliases now rename to target on the action and on both rows.
  • ADR-0087 D2 conversion action-block-endpoint-to-target (protocol 18, MAJOR_18_CONVERSIONS order 52, retired from the load path, retiredAfter: '17.5.0'). Its shape follows field-reference-to-alias: the schema refuses the key through an alias, and the entry is retired from day one. On action:button / action:icon blocks whose actionType is api it renames endpoint to target, using renameKey's house precedence: a redundant twin is dropped, and a disagreeing pair is kept. It reaches every position mapPageComponents visits. Sites with no lossless rewrite are left byte-identical and reported through context.reportTodo: no actionType (the runner's legacy fallback calls an endpoint with no type as an API call, and a target with no type calls nothing), another actionType, a non-string endpoint, or a disagreeing pair.
  • D3 entry action-block-endpoint-spelling-retired (one entry file plus gen:migration-registry), and a STEP18_RATIONALE fragment (order 55).
  • Generated artefacts: the two ui/Action{Button,Icon}Props:endpoint lines leave authorable-surface/ui.json, and the component.mdx reference loses its two endpoint rows. check:authorable-surface proves the deletion itself with proof 2 (the def is not reachable from the 31 metadata-type roots). spec-changes.json and the upgrade guide do not move, because both fold majors only up to the current protocol major, 17.

Mechanism hypotheses, measured

  • H1 (census first): zero producers. At objectstack 2821e9f15b, git grep over every tracked file found no action:button / action:icon node that authors endpoint. The checked set was examples, templates, platform pages and fixtures. The only hits were the spec row test's key-set list and comments. At objectui 5262f7dd, there is no examples/apps directory. examples/ and every authored endpoint: outside the renderers are tests, and objectui's registration never published endpoint as a designer input. So nothing in either tree needed a fixture rewrite. The one in-repo pin naming the key, the whole-key-set test in component-action-element-rows-20371.test.ts, drops it from the declared set.
  • H2 (one table): holds. See the first bullet. The pins compare the row's rename clause against the clause ActionSchema prints, not against a hand-copied string.
  • H3 (precedent): field-reference-to-alias (alias refusal plus a D2 entry retired from day one plus a D3 entry plus a rationale fragment). The page-block walking and renameKey precedence follow page-card-body-to-children and record-picker-display-field-to-label-field. The api-only scope follows inline-action-api-params-to-body-extra. check-adr-0087-registration passes with registered action-block-endpoint-to-target, action-block-endpoint-spelling-retired.
  • H4 (doors): partly refuted. The schema alone reaches os validate / os build / os lint, and only as the advisory props gate. Measured through validateComponentProps on a stack with { type: 'action:button', properties: { actionType: 'api', endpoint } }: one warning component-props-unknown-key at properties.endpoint, carrying "Did you mean endpoint → target?". The target control gives no finding. defineStack accepts the page either way: PageComponent.properties is an open bag. The metadata save path does not judge properties by type. That is the posture recorded for every row of this map; this card does not change it.

Tests

  • New packages/spec/src/ui/component-action-row-endpoint-21005.test.ts: both rows refuse endpoint (unrecognized_keys, key endpoint), and their rename clause equals ActionSchema's. target parses as the control. endpoint is absent from both shapes. Every alias in the shared table renames identically on the action and on both rows.
  • New packages/spec/src/conversions/action-block-endpoint-to-target.test.ts: the stored-row seam rewrites a stored endpoint to target on both blocks, and the result parses against the row. Twin and disagreeing-pair handling. The three TODO classes, each byte-identical with a reason. An endpoint on another block type is left alone. Copy-on-write identity, idempotence, and that the authoring funnel does not replay the entry.
  • component-action-element-rows-20371.test.ts: the whole-key-set pin drops endpoint, with a comment naming why.
  • Reverse verification against the rebuilt dist/*.d.ts: a typed ActionButtonProps / ActionIconProps literal carrying endpoint fails tsc with TS2353 on both lines, and the target line compiles. The temp file was removed and the tree is clean.

Readings, gate commands and exit codes are in the dev report on #21005.

Acceptance notes

  • AGENTS.md's Post-Task step 3 says removing an authorable key also needs a retiredKey() tombstone. Triage's direction on this card is the alias prescription read from one table, with no second message string, so this PR follows the direction. The alias carries the FROM → TO, and tsc still refuses the key, as TS2353 without the [REMOVED] mark. The authorable-surface gate accepts the deletion on its own proof 2. Recorded here rather than chosen silently.
  • The props gate's generic hint for an unknown key reads "Remove endpoint, or declare it on action:button's props schema if the component honours it". When the message carries a rename, the second half points the other way. Noted, not filed (validate-component-props.ts; carrier: none).
  • objectui follow-up (not in this PR): once objectui installs a spec carrying this change, its endpoint entries booked to objectui#11168 strike. registry-inputs-spec-parity.test.ts counts unpublishedKeys: 3 (the two endpoint entries and undoable); that should become 1. The two renderers' endpoint: schema.endpoint forwards in action-button.tsx / action-icon.tsx lose their spec counterpart. The runner's deprecated endpoint dialect in ActionRunner.executeAPI / execute's legacy fallback stays objectui's to retire. The seat files that coordination child after landing.

Generated by Claude Code

claude added 8 commits October 1, 2026 05:18
…ema's target rename, read from one table

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
…rd (proof 2: def unreachable from metadata roots)

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
…tep-18 rationale, pins

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
…ment follows main's newest

Claude-Session: https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation protocol:ui tests labels Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 25 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/automation/flows.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES), actionType (literal, a string literal in ACTION_NODE_ALIASES))
  • content/docs/data-modeling/field-types.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/data-modeling/fields.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/data-modeling/formulas.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/data-modeling/schema-design.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/data-modeling/validation-rules.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/deployment/environment-variables.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/deployment/validating-metadata.mdx (via actionType (literal, a string literal in ACTION_NODE_ALIASES))
  • content/docs/protocol/objectui/concept.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/protocol/objectui/layout-dsl.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/ui/pages.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/ui/views.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))

⛔ 8 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/implementation-status.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/releases/v12.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/releases/v15.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/releases/v16.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES), actionType (literal, a string literal in ACTION_NODE_ALIASES))
  • content/docs/releases/v17/17-0.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES), actionType (literal, a string literal in ACTION_NODE_ALIASES))
  • content/docs/releases/v17/17-2.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/releases/v17/17-4.mdx (via visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))
  • content/docs/releases/v17/17-5.mdx (via retiredAfter (symbol, a field of const object actionBlockEndpointToTarget), retiredFromLoadPath (symbol, a field of const object actionBlockEndpointToTarget), visibleWhen (symbol, a field of const object ACTION_NODE_ALIASES))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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

Coarse fallback — 137 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 99398542b328eb06be8dbd781200f06a5df608b0 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 95d207c6f033d7626f86435740fc3e6336c70bdd — the merge of head dcaabdbbb6cb0348653b5f2273b8dedfaf87263e into base 99398542b328eb06be8dbd781200f06a5df608b0, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 95d207c6f033d7626f86435740fc3e6336c70bdd && git checkout 95d207c6f033d7626f86435740fc3e6336c70bdd
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 99398542b328eb06be8dbd781200f06a5df608b0 dcaabdbbb6cb0348653b5f2273b8dedfaf87263e && git checkout -B drift-repro 99398542b328eb06be8dbd781200f06a5df608b0 && git merge --no-ff dcaabdbbb6cb0348653b5f2273b8dedfaf87263e

node scripts/docs-audit/affected-docs.mjs --json 99398542b328eb06be8dbd781200f06a5df608b0

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 99398542b328eb06be8dbd781200f06a5df608b0 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

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

Card #21005 · PR #21122 · written 2026-10-01T09:26Z. Inputs: the card body and its five comments (triage 5923326115, serial note 5923644301, claim 5925158541, dev report 5928425328, claim amendment 5928458619), the PR body, its 12-file list, the net diff against the merge base 9c8b65aa23, the head's files read through the fetched ref, and the check-runs on the head. objectui's runner, console handler and button renderer were read over REST at its current tree d0fba91aa0 to judge the conversion's condition; nothing was built, run or re-run.

① Derived judgments

  1. Accept-set narrowing, the card's fix. ActionButtonPropsSchema and ActionIconPropsSchema each lose the member endpoint: z.string().optional(). Both rows are strictObject, so an authored endpoint is now refused as unrecognized_keys. Named right: this is the card's direction and triage's first pin.
  2. One table, no second string. The new internal module ui/action-target-aliases.ts holds ACTION_TARGET_ALIASES (url, endpoint, path, href, each to target). actionObject() in action.zod.ts spreads it in place of the identical inline row it carried before, and the rows' ACTION_NODE_ALIASES spreads the same object. The same four pairs on the action side means ActionSchema and InlineActionSchema change no accept set and no message; the rows' prescription is now produced by the one strictUnknownKeyError renderer. The pin component-action-row-endpoint-21005.test.ts compares the row's rename clause with the clause ActionSchema prints at test time, not with a copied string. Named right.
  3. url / path / href on the rows: a message change, not an accept-set change. Neither row declared any of the three (only target is declared, read at the head), so they were already refused; what moves is the prescription, from the distance fallback (path answered patch, a declared key that parses) to the rename. Both halves of the alias-integrity audit hold: the four keys are undeclared on both rows and target is declared on both. ACTION_NODE_ALIASES is spread by exactly the two rows (component.zod.ts at the head, lines 3015 and 3102), so no third row's message moves. Named right.
  4. No public-surface widening. action-target-aliases.ts is absent from the ui barrel (ui/index.ts lists each export * by file; line 43 is export * from './action.zod', which is why exporting the table from action.zod.ts would have published it). @objectstack/spec's exports map carries ./ui and no wildcard subpath, and its files list ships src/**/*.zod.ts only, so the module is neither addressable nor shipped as source. Named right; the dev's reason for the landing site holds.
  5. The D2 conversion action-block-endpoint-to-target. Protocol 18, retiredFromLoadPath: true, retiredAfter: '17.5.0' (the published release the card measured), order 52 in MAJOR_18_CONVERSIONS, which is the list's previous maximum plus one with no duplicate. It walks mapPageComponents, filters on the two block types, renames only where actionType === 'api', and uses renameKey from walk.ts (read at the head: an equal twin is dropped, a disagreeing pair returns null and here becomes a TODO). The fixture's four notices (the measured defect, the twin, the nested icon, the slotted header) match expectedNotices: 4; the disagreeing pair, the type-less block, the other block type and the canonical block are byte-identical in after. The shape is the one ADR-0087's pre-GA policy demands: a retired-from-load-path conversion when lossless plus one D3 entry per retirement family, in the same release. Named right.
  6. The D3 entry and the rationale fragment. 18.action-block-endpoint-spelling-retired.ts is projected into the regenerated migrations/registry.ts in alphabetical position; the STEP18_RATIONALE fragment carries order 55, the list's previous maximum plus one, no duplicate (the dev moved it from 54 after a merge landed 54). check:migration-registry is a derived gate and the head's Lint & Repo Gates run is green. Named right.
  7. Generated artefacts. authorable-surface/ui.json loses the two ActionButtonProps:endpoint / ActionIconProps:endpoint lines, adjudicated by the gate's own proof 2 (the def is not reachable from the metadata-type roots), and component.mdx loses the two endpoint rows and nothing else. Named right.
  8. The existing pin. component-action-element-rows-20371.test.ts asserts both rows' key sets whole; dropping endpoint from FORWARDED is the only change the narrowing requires there, and the comment names why. Named right.
  9. The conversion's own test pins the stored-row seam replaying the retired entry, the authoring funnel not replaying it, the three TODO classes left byte-identical with a reason, copy-on-write identity and idempotence. Named right.

No accept-set or public-surface change beyond these nine was found in the diff.

② Semver level

.changeset/21005-action-row-endpoint-refused.md declares '@objectstack/spec': minor with the **BREAKING** banner, the line Clause-②: yes (narrowing), a FROM → TO table, the one-line fix, the standard os migrate meta --from 17 sentence, and the ADR-0087 disposition as the HTML-comment marker in the form registered naming both new ids (action-block-endpoint-to-target, action-block-endpoint-spelling-retired), which is the form the ADR's 2026-08-13 addendum lists. The PR body carries the same Clause-②: yes (narrowing) line. This matches AGENTS.md Post-Task step 3 (yes takes at least minor, (narrowing) is BREAKING, migration stated) and ADR-0087's pre-GA level rule (a metadata-facing retirement ships minor with the banner and the disposition), and check-changeset-no-major is the gate that enforces the launch-window convention. The diff publishes from @objectstack/spec only, plus content/docs. The level and the declaration match what the diff publishes.

Clause-②: yes (narrowing)

③ Boundary flags

The seat's four points.

  1. Tombstone or alias. No tombstone is owed on this card. Four reasons, in order of weight. First, the two mechanisms exclude each other by the house's own audit: a retiredKey() keeps endpoint in the rows' shape, and alias-integrity requires an alias key to be one the shape rejects (an alias runs only from the unrecognized_keys path), so the shared table's endpoint entry would be unreachable on the rows and the audit would fail; a tombstone string beside ActionSchema's rename is also the second message triage forbade. Second, the semantics fit aliases, not a tombstone: strict-object.ts defines an alias as a different word for the same intent, and endpoint was a second spelling of a key both rows still declare (target, described on the rows as "the URL, script name, flow name or API endpoint, per actionType"), added by feat(spec): ComponentPropsMap rows for action:button/group/menu/icon and element:definition-list/repeater #20420 from the renderer's forward list; it is not a concept retired with no replacement. Third, what the tombstone would add to the parse message (the [REMOVED] mark and the os migrate meta sentence) is carried by the changeset, the D3 entry and the upgrade guide, while the FROM → TO is already in the parse message; and an alias does not age out the way a tombstone does, so a consumer jumping majors still reads it. Fourth, the deletion was adjudicated by the authorable-surface gate on proof 2, a gate verdict rather than PR prose, and no liveness row existed for either key (packages/spec/liveness/*.json has none), so no ledger row is orphaned. The triage ruling governs the fix shape; the dev was right to follow it and right to name the conflict. Two residuals for the seat, neither blocking and neither for this PR: AGENTS.md step 3 and the retirement skill's route table have no row for "a duplicate spelling of a still-declared key, refused through aliases", so this ruling is worth recording there; and the tsc channel here is the excess-property check (TS2353 on a literal), weaker than a never tombstone, since a value spread from a wider type passes tsc and meets the parse.
  2. The whole target row. Inside the card. Triage's wording is "read from one table"; the table's target row is four keys, and reading one of them would have meant a second object, a copy. The three extra keys change no accept set (judgment 3), the change is in the same two rows and the same file, it is declared in the PR body and in the claim amendment 5928458619, and the one-table pin covers all four. Right.
  3. The conversion's api condition. Verified on objectui's runner. ActionRunner.execute derives actionType = action.type || action.actionType || action.name || ''; with no built-in executor for it the dispatch falls through navigate, then api || endpoint to executeAPI, then onClick, then executeActionSchema, which returns "not executable" for an action with no api, endpoint, redirect, reload or close. executeAPI resolves api || endpoint || target. The button renderer forwards type: schema.actionType with no fallback and name: schema.name. So a type-less block with endpoint calls its endpoint today and the same block with target alone errors out: an unconditional rename is lossy there, and the api-only rename with a TODO is the right shape. The other-type TODO is right too: the url executor reads target || redirect, flow reads target || name, script reads target, and none reads endpoint, so moving the value would change what the action does. The console's api handler reads action.target || action.name (useConsoleActionRuntime.tsx), which confirms the card's premise and that the rename is lossless for api. Right.
  4. The reach of the refusal, as described. PageComponentSchema.properties is z.record(z.string(), z.unknown()) (page.zod.ts:324), an open bag; packages/metadata-protocol/src references neither ComponentPropsMap nor validateComponentProps, so the save path does not judge properties by type; the props gate is the validateComponentProps rule in lint's authoring-rules.ts, and runAuthoringRules is called from the CLI's validate.ts, compile.ts (the build command) and lint.ts, so the three commands the changeset names are the three that reach it. The changeset says exactly this: a page is never refused for the key, the three commands report a warning at properties.endpoint carrying the rename, a typed input fails tsc, and the out-of-repo consumer population is not measured. The PR body's H4 reads the same. Honest.

The dev's deviations. (1) The landing site: answered by judgment 4, right. (2) Scope: answered by point 2. (3) Conversion shape: answered by point 3. (4) H4 partly refuted: answered by point 4. (5) The AGENTS.md conflict: answered by point 1. (6) Two merges and the fragment's move from 54 to 55: order 55 is unique and the list's maximum plus one, and the head's gates are green. (7) Consumer suites narrowed locally: the check-runs on the head are the gate verdicts (below), all green; the local narrowing is not a reading this record relies on.

open_questions: none filed; nothing to answer.

out_of_scope_findings: both agreed out of this card. The lint hint's second clause ("or declare it on the props schema") pointing against a carried rename is a packages/lint finding the seat may file; proof 2's wording versus the advisory lint door is an observation on the gate's own definition. Neither blocks.

Check-runs on the head, read at 2026-10-01T09:23Z: 35 runs, 33 success, 2 skipped, none failed, none in progress. The two skipped are Console Pin Gate (path-filtered: the diff moves none of its build inputs; and the pinned renderer types its schema as objectui's own UIActionSchema, not spec's ActionButtonProps, so the removed key breaks no sibling import and Post-Task step 4 holds) and Packed-tarball smoke (opt-in) (opt-in by name). Green among them: Build Core, Test Core 1 to 6, Type Check (workspace, source gates, consumer gates, debt ledger), Lint & Repo Gates, Check Changeset, Spec property liveness, Governed Surface Queue Guard, Dogfood Regression Gate 1 to 3, Dogfood Verify CLI, Temporal Conformance, Build Docs, Check Documentation Links, and the claim and card guards.

Implemented-by: claude/issue-21005-action-row-endpoint-refused
Reviewed-by: session_017VaLJnYwhPsanVCe9dMCJU

VERDICT: PASS


Generated by Claude Code

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/l tests tooling

Projects

None yet

2 participants