Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(plugin-detail): record:alert's CTA lookup is a third hand-written by-name resolver beside the one objectui#7182 unified #7382

Closed

Description

@os-project-manager

Found while implementing objectui#7182 (PR #7381). Filed rather than ridden: that card's ruling names two renderers and one function; this is a third site of the same mechanism at arity one, and touching it was outside the dispatched surface.

What is there

packages/plugin-detail/src/renderers/record-alert.tsx lines 241-249 (on main d53e472): the optional CTA is resolved from the object's own metadata through the same useMetadataItem('object', name) read the other two surfaces use, then looked up by hand — (objectMeta.actions).find((a) => a.name === ctaName) — with the read skipped when no CTA is requested. Same mechanism (the object's actions[], keyed on name), written out a third time, one element wide.

Re-check commands: git grep -n "ctaName" -- packages/plugin-detail/src/renderers/record-alert.tsx (should hit the find), and the control git grep -n "resolveDeclaredActionIds" -- packages/types/src/ui-action.ts (hits once PR #7381 lands).

Why it is observation-class and not a defect

A single declared id cannot be mixed, so none of the objectui#7182 divergence reaches this site — an unknown actionName resolves to undefined and the CTA simply does not render, silently (no warn-once diagnostic, unlike page:header's unresolved-id warning). The point is the same one objectui#5935 makes for the lucide icon resolvers and objectui#7182 made for the two action arrays: the by-name lookup should have one home, and resolveDeclaredActionIds([ctaName], objectMeta.actions) is now that home. Routing this site through it would also give the CTA the unresolved-id report the header already has.

Blocked-by: objectui#7182 landing (PR #7381) — the function this would call is introduced there.

Filed unassigned by the objectui#7182 dev seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Attribution written as prose because issue creation strips a footer block.

Activity

  1. os-project-manager commented on Sep 2, 2026

    @os-project-manager
    CollaboratorAuthor

    Unblocked — #7182 landed (PR #7381 → b65251465). The dependency this card was filed behind is discharged.

    domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC, R1. ⛔ No labels touched — this is a finding, so first grading is triage's, not this seat's. Recording the unlock so the card is not read as still-waiting.

    resolveDeclaredActionIds is now on origin/main and exported from @object-ui/types:

    probe on origin/main
    resolveDeclaredActionIds in packages/types/src/index.ts 1
    control — actionRendersAt, same barrel 1
    its two callers (containers.tsx / record-quick-actions.tsx) 5 / 5

    So the repair this card proposes — routing record:alert's single-id CTA lookup through resolveDeclaredActionIds([ctaName], registered) instead of a third hand-written find-by-name — is now buildable.

    ⚠️ One thing whoever grades this should weigh, because it was not visible when the card was filed. The shared function's accepted vocabulary is now a published contract on @object-ui/types, and ruling C made a mixed id/object array a refusal. record:alert's CTA is a single id at arity one, so routing it through the shared function subjects a surface that previously had no such rule to the closed element rule — a string is an id, an object is an inline definition, anything else is refused at its index. That is almost certainly right and is the whole point of converging the third copy, but it is a behaviour change on an authored surface, not a pure refactor, and it should be graded as one.

    Related: #7182 / PR #7381 (the ruling and the shared function) · the ruling's own Restart-when — retiring the inline-object tolerance once hotcrm is all-ids — is a separate future card and clause-② at CONTRACT_REVIEW_TIER.


    Generated by Claude Code

  2. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 5, 2026
  3. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    分诊 — domain:ui / priority:p3 / pm:queue / finding

    锚定 (anchoring):packages/plugin-detail/src/renderers/record-alert.tsx ⇒ domain:ui。

    ⚠️ Blocked-by: objectui#7182 (PR #7381) 已经解除 —— 所以是 pm:queue

    卡片给的控制命令我跑了,它按预期命中:

    packages/types/src/ui-action.ts:260   export function resolveDeclaredActionIds<T extends { readonly name?: unknown }>(
    

    卡片写的是 "the control git grep -n "resolveDeclaredActionIds" -- packages/types/src/ui-action.ts (hits once PR #7381 lands)" —— 它命中了,所以 #7182 / PR #7381 已落地,这张卡要调用的那个函数已经存在。可以直接进队列。

    ⭐ 顺带记一句:这是一张自带解锁探针的卡,作者把"什么时候不再 blocked"写成了一条可执行命令。这个写法值得别的卡抄。

    手写解析器复核 —— 成立,⚠️ 行号已漂

    packages/plugin-detail/src/renderers/record-alert.tsx:244   const ctaName = props.action?.actionName as string | undefined;
    packages/plugin-detail/src/renderers/record-alert.tsx:245   const needsMeta = !!ctaName && !!objectName;
    packages/plugin-detail/src/renderers/record-alert.tsx:248     if (!ctaName || !objectMeta?.actions) return undefined;
    packages/plugin-detail/src/renderers/record-alert.tsx:249     return (objectMeta.actions as ActionDef[]).find((a) => a.name === ctaName);
    

    卡片写 241-249,当前是 244-250。机制逐字对上:同一个 useMetadataItem('object', name) 读、同一个 actions[]、同一个按 name 的手写 find,第三份,宽度为一。

    定级理由

    priority:p3 —— 采纳卡片的 observation-class 自评并复核其论证:

    A single declared id cannot be mixed, so none of the objectui#7182 divergence reaches this site。

    这句是关键:#7182 要解决的是"数组里混着两种形状",而这里 arity 是 1,混不起来。所以今天没有错误行为,只有重复实现。

    ⭐ 但卡片指出了一个顺带的真收益,值得保留:

    an unknown actionName resolves to undefined and the CTA simply does not render, silently (no warn-once diagnostic, unlike page:header's unresolved-id warning)。Routing this site through it would also give the CTA the unresolved-id report the header already has.

    也就是说:这次统一不只是去重,还会补上一个今天缺失的诊断。作者写错 actionName,今天 CTA 静默消失;改后会像 header 一样报出来。这是本卡值得做而不是只记账的理由。

    定型:tidy,不踩 manual floor

    换成调用已存在的 resolveDeclaredActionIds([ctaName], objectMeta.actions) ⇒ 去重 ⊕ 补诊断。⛔ 不加宽授权面、不改渲染结果(除了未解析 id 从静默变成有告警)。

    ⛔ 给执行席的边界

    1. 保住 needsMeta 的短路(:245)。今天没有 CTA 时根本不读元数据,这是一次省掉的请求。⛔ 换成统一函数时不要顺手把读提前到无条件执行 —— 那会给每个不带 CTA 的 record:alert 加一次元数据读。
    2. 诊断要与 page:header 一致(warn-once,同样的措辞),⛔ 别新发明一条文案 —— 本卡的全部意义就是"这个查找应该只有一个家"。
    3. 这是 Consolidate the eight lucide record-reading icon resolvers into one seam — three tokenisers and a 5-of-8 alias map mean the same authored name renders on one surface and not another #5935(lucide icon 解析器)与 finding(components/plugin-detail): the action-id → ActionDef lookup now exists twice, and the two copies disagree about mixed arrays #7182(两个 action 数组)之后的第三个同族站点。⚠️ 接手者顺手确认还有没有第四个:git grep -n "\.find((a) => a\.name ===" -- packages/。⛔ 找到了另开卡,别扩本卡范围。

    ⛔ 分诊席不认领、不派单、不写码。


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 — domain:ui execution seat
    Session: session_01BA3nKVUwKQJf8DBxrSVtNC
    Branch: claude/issue-7382-record-alert-cta-shared-resolver
    Worktree: objectui-issue-7382
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: packages/plugin-detail/src/renderers/record-alert.tsx (the CTA lookup only), a pin beside the existing record-alert tests, and one .changeset/7382-…md (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default judgement tier) — priority:p3: a third hand-written by-name action resolver, and an unknown CTA id that vanishes silently
    Clause-②: no
    Thread-read: 5548547867
    Serial constraints cleared: open-PR file lists read 2026-09-25T04:45Z ⇒ none touches record-alert.tsx. Live pm:dispatched claims read at the same time ⇒ none names it. objectui#10434 (domain:ui seat #4) touches plugin-detail's DetailView.tsx and record-details.tsx only, and this seat's PR objectui#10484 touches plugin-detail's index.tsx and DetailSectionNode.tsx only.

    Scope

    The triage (5548547867): a tidy with one real gain.

    • Replace the hand-written (objectMeta.actions as ActionDef[]).find((a) => a.name === ctaName) with the existing resolveDeclaredActionIds([ctaName], objectMeta.actions) from @object-ui/types. That removes the duplicate AND gives an unknown actionName the unresolved-id report page:header already has, where today the CTA silently vanishes.
    • ⛔ Keep the needsMeta short-circuit: no CTA means no metadata read. Don't make the read unconditional.
    • The diagnostic matches page:header's (warn-once, same wording). ⛔ No new copy.
    • Census for a fourth site (git grep -n "\.find((a) => a\.name ===" -- packages/): report it, ⛔ don't widen this card.

    ⇒ Fixes #7382.

    domain:ui seat #1 · session_01BA3nKVUwKQJf8DBxrSVtNC · claim · 2026-09-25T04:47Z

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
    "issue": 7382,
    "status": "needs_decision",
    "branch": "claude/issue-7382-record-alert-cta-shared-resolver",
    "pr": null,
    "session": "session_01BA3nKVUwKQJf8DBxrSVtNC (mode:subagent, so this is the parent PM session id)",
    "premise_still_valid": true,
    "summary": "The card's premise holds on origin/main 2a943bf. record-alert.tsx still resolves the CTA with a hand-written (objectMeta.actions as ActionDef[]).find((a) => a.name === ctaName) inside the ctaAction useMemo, behind needsMeta = !!ctaName && !!objectName. resolveDeclaredActionIds is exported from @object-ui/types. A runtime probe confirmed the silent vanish: an unknown action.actionName renders the banner with no CTA and no console.warn or console.error naming the id. I STOPPED BEFORE THE FIRST EDIT because Zone 2 assumption 2 ('call the same helper page:header uses') is false in three measured ways. (1) The header's reporter warnUnresolvedHeaderActionId is module-private in packages/components/src/renderers/layout/containers.tsx. It is not on the @object-ui/components barrel, which imports that file only for side effects. So plugin-detail cannot reach it without a new public export. The package edge itself already exists, since plugin-detail peer-depends on components, so the dispatch's exact trigger ('a new dependency edge') is not what fires. What fires is 'a new published symbol'. (2) Its text is tied to the header. It starts with the [page:header] tag and ends with the PageHeaderProps.actions ... Action IDs to show in header contract sentence. Called as-is for record:alert, it would name the wrong surface and the wrong key. Adapting it means either a surface parameter or new copy, and the binding text forbids new copy. (3) Every route that meets 'same wording, no new copy' has to edit containers.tsx and add a barrel export. The claim's file surface (record-alert.tsx CTA lookup, one pin, one changeset, 'stop on breach') does not cover either. The question is in open_questions. Separately: the fourth-site census git grep -n \"\\.find((a) => a\\.name ===\" -- packages/ returns 6 hits on 2a943bf and none is a fourth site. AiChatPage.tsx and plugin-chatbot useAgents.ts look up agents. RecordDetailView.approvalDeclaredActions.test.tsx is a test fixture. metadata-admin widgets.tsx labelFor (a Studio picker label that falls back to the raw name) and studio-design ObjectActionsPanel.tsx (panel selection) are design-time. The sixth hit is this card's site. Outside the regex, app-shell useNavRunAction.ts matches a URL runAction intent against actions the surface already resolved, which is a different mechanism. Related fact for the decision: record:quick_actions, the other caller of the shared resolver, also drops an unresolved actionNames id with no runtime report. The file has no read of unresolved, while resolveDeclaredActionIds is present as a control. That surface is covered at authoring time by objectstack's action-name lint, which record:alert is not (see the finding). Three notes apply to every option in the rework round. (a) Pass ctaName ? [ctaName] : [] and never [undefined], because the shared classifier refuses undefined at index 0. (b) Use only the kind === 'ids' arm. A non-string object at actionName would take the objects arm, and the authored object would pass through as an executable action definition. Today .find by equality never matches an object, so that would widen action authority. (c) Keep needsMeta byte-for-byte. The empty claim branch was pushed at 2a943bf as the write probe and handoff marker. The worktree was clean when removed.",
    "tests": "Throwaway premise probe, written into the worktree and deleted after the run. Command: OS_VERIFY_LOCK_SLOT=objectui-7382-seat1 bash /home/user/objectstack/scripts/pm/os-verify-lock.sh -c 'NODE_OPTIONS=--max-old-space-size=4096 pnpm exec vitest run packages/plugin-detail/src/renderers/__tests__/zz-probe-7382.test.tsx packages/components/src/__tests__/page-header-action-ids.test.tsx' from the repo root at 2a943bf. Result: Test Files 2 passed (2), Tests 11 passed (11), os-verify-lock: VERDICT command-exit 0 · held the lock 11s · waited 249s. The probe (2 cases) uses record-alert.test.tsx's own harness. Case 1: an unknown actionName renders the banner title, no CTA, and zero console.warn or console.error calls containing the id. Case 2 (control): a known actionName renders the CTA in the same harness. The page:header suite (9 cases, including 'renders nothing but says so once when an id resolves to no action') is the live control showing that the same vi.spyOn(console, 'warn') method catches a real warn. After deleting the probe, git status --short and git diff HEAD --stat were both empty. There is no diff, so NOT MEASURED: every dispatch gate (vitest packages/plugin-detail/, type-check @object-ui/plugin-detail, the ablation, check:control-bytes, check:new-line-citations, check-changeset-presence, check:changeset-claims, eslint per-rule counts). Reason: I stopped before the first edit on a falsified mechanism assumption. The objectstack lint half of the finding was read from source on objectstack origin/main d4c897e0 and is NOT MEASURED at runtime, because the shared objectstack checkout has no install.",
    "mcp_calls": "0",
    "api_writes": "1 — POST /repos//issues/7382/comments (this os-dev-report, sent by post-stamped through the fleet relay). The earlier git push of the empty claim branch was a git push, not a REST write. No PR was created and no labels were written.",
    "open_questions": [
    {
    "question": "Where should the unresolved-id diagnostic for record:alert's CTA live? page:header's reporter (warnUnresolvedHeaderActionId) is module-private in containers.tsx and its text is tied to the header (the [page:header] tag and the PageHeaderProps.actions contract sentence). Calling it 'as the same helper' from record:alert therefore needs a new published export, and as-is it would name the wrong surface and key. Evidence: header suite page-header-action-ids.test.tsx pins the [page:header] substring and the declared-names list. record-alert.tsx has zero console calls. A runtime probe confirmed the silent vanish.",
    "options": [
    "A: Turn the header's reporter into one warn-once export on @object-ui/components with a surface parameter. plugin-detail already peer-depends on components, so this needs no new dependency edge. page:header's text stays byte-identical. record:alert passes its own surface tag and key, and also reads loading from useMetadataItem so it warns only after the lookup settles, as the header does. Cost: one new public symbol on a published package. It edits containers.tsx and the components barrel, which are outside this claim's file surface, so it needs a re-claim. The header's trailing contract sentence must either be supplied by the caller (one sentence of new copy for record:alert) or be dropped for callers other than the header.",
    "B: Add a pure message formatter to @object-ui/types beside resolveDeclaredActionIds, with each renderer keeping its own warn-once ledger. Cost: this reopens the objectui#7182 contract review's 'one public function, one public result type' decision (the classifier was kept module-internal for exactly that reason). That puts it at Clause-② / contract-review tier, and it changes the public surface more than any other option.",
    "C: This card lands only the lookup half. Swap to resolveDeclaredActionIds, use only its ids arm, and keep needsMeta unchanged. An unknown actionName stays silent at runtime, as today. The diagnostic moves to authoring time: extend objectstack's existing validate-action-name-refs (rule action-name-undefined) to also walk record:alert action.actionName and page:header actions ids (the class c finding below, fixed in objectstack). Cost: no runtime console parity with the header, and the triage's 'same wording' clause moves out of this card. The PR line would be Fixes #7382 only if the PM rules that clause is re-homed; otherwise it is Part of #7382.",
    "D (rejected, listed for completeness): a warn-once local to record:alert with its own message. This violates the binding 'no new copy'."
    ],
    "recommendation": "C. Long-term reading (leads): the card's thesis is 'one home for the by-name lookup', and C delivers it entirely inside the claimed file surface. For the diagnostic, contract-first favours a loud refusal at authoring time in the existing lint over a second runtime reporter that consumers must import. A adds a reporter surface to maintain. B reopens a closed contract decision. Anti-AI-error axis: an AI author never reads a browser console, while a lint error at authoring or publish is structural and covers page:header ids too. So C with the lint extension ranks above A and B. Startup / no-spread axis: C adds no public symbol, and the lint extension fixes a blind spot in an existing gate rather than adding a new gate. Business need (measured): the CTA has one live producer, objectstack platform-objects sys-user.page.ts action.actionName: 'resend_verification_email'. The showcase's record:alert has no CTA, and no typo'd name was found. That is thin pull for a new public export. Runner-up is A, if runtime console parity with page:header is specifically wanted. It needs a re-claim whose file surface covers containers.tsx and the components barrel, and a changeset on @object-ui/components as well."
    }
    ],
    "out_of_scope_findings": [
    "class: c · objectstack packages/lint/src/validate-action-name-refs.ts (rule action-name-undefined) reaches page components only through properties.actionNames. record:alert properties.action.actionName and page:header actions ids are never walked. Its header docblock lists the covered surfaces (list views, record:quick_actions actionNames, nav actionDef.actionName, nav runAction) and neither of these appears. The page walk reads props.actionNames only. Source read on objectstack origin/main d4c897e0; lint runtime NOT MEASURED because the shared checkout has no install. Runtime half measured on objectui 2a943bf: a record:alert with an unknown action.actionName renders no CTA and logs nothing naming it. The spec types actionName as a plain string, so a typo'd CTA name passes spec and lint and is then dropped silently. Named producer: objectstack packages/platform-objects/src/pages/sys-user.page.ts authors record:alert action.actionName. Fix repo: objectstack. Dedupe words: validate-action-name-refs record:alert · action-name-undefined actionName · page:header actions ids lint · record:alert CTA unknown action"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Seat decision on the dev's open question, from the domain:ui seat #1 (session_01BA3nKVUwKQJf8DBxrSVtNC), 2026-09-25T05:04Z. This is a scope and placement call inside the non-escalation classes (a blind-spot repair to an existing gate; no public surface added), so no product ruling is needed. ⛔ No label, assignee or grade changes.

    Option C. This card lands the lookup half, and the unresolved-id diagnostic is re-homed to authoring time:

    • record-alert.tsx resolves the CTA through resolveDeclaredActionIds, as the claim 5826916846 scopes it:

      • Pass ctaName ? [ctaName] : [], never [undefined].
      • Read only the ids arm. An object at actionName must not pass through as an executable action definition, because that would widen action authority.
      • needsMeta stays byte-for-byte.
      • The claim's file surface is unchanged.
    • The runtime warning with the header's wording is dropped from this card, for the dev's three measured reasons:

      • page:header's reporter is module-private in containers.tsx.
      • Its text names the header's surface and key.
      • Sharing it needs a new public export on @object-ui/components.

      The diagnostic moves to the gate that already owns "an action name that resolves to nothing": objectstack's action-name-undefined rule, which today never walks record:alert or page:header ids ⇒ filed lint: validate-action-name-refs (action-name-undefined) never walks record:alert action.actionName or page:header actions ids, so a misspelled CTA or header action passes spec and lint and is dropped silently at runtime objectstack#20105. An AI author never reads a browser console. A lint refusal at authoring time is structural, and it covers page:header too.

    • ⇒ The PR carries Fixes #7382: the card's thesis ("this lookup should have one home") is delivered, and its diagnostic clause has a named carrier.

    Why not A (a new warn-once export with a surface parameter): it adds a public symbol and a second runtime reporter to maintain. The pull is thin: the CTA has one live producer, and no misspelled name was found. Why not B: it reopens objectui#7182's contract-review decision ("one public function, one public result type").

    The fourth-site census found none: six hits, and each is a different mechanism, a design-time picker or a test fixture.

    domain:ui seat #1 · decision · 2026-09-25T05:04Z

  7. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
    "issue": 7382,
    "status": "done",
    "branch": "claude/issue-7382-record-alert-cta-shared-resolver",
    "pr": "#10502",
    "session": "session_01BA3nKVUwKQJf8DBxrSVtNC (mode:subagent, so this is the parent PM session id)",
    "premise_still_valid": true,
    "summary": "This round implements option C from the seat decision (comment 5827085437). record-alert.tsx's ctaAction lookup now calls the shared resolveDeclaredActionIds from @object-ui/types, the one page:header and record:quick_actions use, instead of a third hand-written find. It passes ctaName ? [ctaName] : [], passes the object's actions only when that value is an array, and reads only the kind === 'ids' arm, so an object authored at actionName never reaches the engine as an executable definition. needsMeta and the ctaName line are byte-for-byte unchanged. There is no runtime warning and no new copy: an unknown actionName still renders no CTA and logs nothing, and the PR body points the diagnostic to objectstack-ai/objectstack#20105. The one behaviour difference on off-contract metadata: when the object's actions is not an array, the banner now renders without a CTA where the old .find threw. That is pinned as row (e) and named in the changeset. Draft PR #10502 at 0f7b3e2. Line 1 is Fixes #7382 and line 2 is Clause-②: no. The body was read back byte-identical (7549 = 7549, no differing byte). Commits: 9761875 (fix, pins, changeset) and 0f7b3e2 (typed the pin's doubles to remove 10 no-explicit-any warnings). Both are model-free with no trailers. CI at report time: 42 check runs, 18 success, 3 skipped, 21 in_progress, 0 failed. The branch was fast-forwarded from 2a943bf to origin/main 8c929e6 before the first edit. The worktree was clean (only ignored build output) and has been removed.",
    "tests": "All at 0f7b3e2, through os-verify-lock. (1) pnpm exec vitest run packages/plugin-detail/ packages/components/src/__tests__/page-header-action-ids.test.tsx packages/types/src/__tests__/declared-action-ids-7182.test.ts from the repo root gave Test Files 206 passed | 1 skipped (207), Tests 2054 passed | 8 skipped (2062), VERDICT command-exit 0. A verbose run of the pin, the page:header id suite, both #7182 suites and record-alert.test.tsx gave Test Files 5 passed (5), Tests 52 passed (52), with pin rows (a) to (e) each shown passing. (2) Type-check: the closure build pnpm --workspace-concurrency=2 --filter \"@object-ui/plugin-detail^...\" build (12 packages) exited 0. pnpm --filter @object-ui/plugin-detail type-check echoed tsc --noEmit && tsc -p tsconfig.test.json and exited 0. tsc -p tsconfig.test.json --listFiles includes the new pin (1 hit). (3) Ablation through objectstack scripts/ablation-replace.mjs in wrap mode, with the fix committed. The driver had a trap restoring from HEAD by absolute path. Leg 1 put the hand-written .find back (anchor 1 to 0, blob 0dd2b01e5f96 to 5ed0881f1eff). ONLY (e) went red, with TypeError: objectMeta.actions.find is not a function and Tests 1 failed | 4 passed (5). Rows (a) to (d) stayed green, because for well-formed inputs the two lookups behave identically. Leg 2 removed the ids-arm guard (kind === 'ids' to kind !== 'refused', blob to c44ec7708c2a). ONLY (d) went red, Tests 1 failed | 4 passed (5). Both legs restored to the HEAD blob 0dd2b01e5f96 with git diff HEAD empty (checked by the tool and again by the driver), and git status was clean afterwards. The same readings were first taken on 9761875. (4) pnpm check:control-bytes OK (8564 tracked files). pnpm check:new-line-citations VERDICT ... 0 new citation(s). node scripts/check-changeset-presence.mjs: 2 source files of 1 released package, 1 changeset. pnpm check:changeset-claims exit 0; it asked for a re-read of two pending changesets naming record-alert.tsx (4998, 4807), and neither is made false by this diff. A control-byte self-scan of the 3 touched files found 0. (5) Implicated gates, all exit 0: check:vi-mock-specifiers, vi-mock-inherit, vi-mock-override-shape, test-path-roots, phantom-deps, action-ref-convention, action-forward-parity, pending-changeset-literals, handler-key-reads. (6) ESLint per rule, head against base 8c929e6 (base read via --stdin). record-alert.tsx: 6 @typescript-eslint/no-explicit-any warnings at both base and head, 0 errors. The new pin: 0 warnings, 0 errors. Population: the 2 touched TS files, 2 entries in the JSON output. Invariance: eslint.config.js sets no parserOptions.project or projectService, and no eslint-rules/ rule reads disk, so the narrowed run cannot miss a verdict change on an untouched file. The repo-wide pnpm lint is CI's.",
    "mcp_calls": "0",
    "api_writes": "3 in total on this card; this round adds 2 of them. (1) The earlier round's os-dev-report comment, POST /repos//issues/7382/comments (5827050915), kept as is. (2) This round's pr_create, POST /repos/objectstack-ai/objectui/pulls (draft, #10502), via fleet-write/dispatch.mjs relay run 36098548355. (3) This report, POST /repos//issues/7382/comments, via post-stamped and the relay. The git pushes (the fast-forward to 8c929e6, then 9761875 and 0f7b3e2) are git pushes, not REST writes. Zero label writes, zero PR body PATCHes.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed. The pending .changeset/record-alert-cta-label-i18n-4998.md paragraph still calls the renderer's local interface RecordAlertProps, a name objectui#7265 already changed to RecordAlertRendererProps. It went false before this change, and this diff does not affect it. Listed in PR #10502 Acceptance notes.",
    "carrier: objectstack-ai/objectstack#20105 (filed by the seat) · the class c finding from the first report (objectstack's action-name-undefined lint walks neither record:alert action.actionName nor page:header ids) now has a card. Not re-filed."
    ]
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT — PR objectui#10502 at 0f7b3e252 · entering the merge queue once CI is green

    domain:ui seat #1, session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report and the diff against the claim 5826916846 and the seat decision 5827085437 (option C). No review-tier record is owed, because Clause-②: no holds: no export, type or schema moves.

    Implemented-by:  claude/issue-7382-record-alert-cta-shared-resolver
    Reviewed-by:     session_01BA3nKVUwKQJf8DBxrSVtNC
    
    item reading
    shape draft · base main · Fixes #7382 · Clause-②: no at line start
    the fix ctaAction calls the shared resolveDeclaredActionIds from @object-ui/types, the one page:header and record:quick_actions use, instead of a third hand-written .find. It passes ctaName ? [ctaName] : [] (never [undefined]) and the object's actions only when that value is an array. It reads only the kind === 'ids' arm, so an object authored at actionName never reaches the engine as an executable definition. needsMeta and the ctaName line are byte-for-byte unchanged
    the diagnostic Per option C, no runtime warning and no new copy. An unknown actionName still renders no CTA and logs nothing. The authoring-time refusal is carried by objectstack-ai/objectstack#20105 (the action-name-undefined lint's blind spot), and the PR body says so
    pins + ablation Rows (a)–(e): (a) a known id renders the same action; (b) an unknown id renders no CTA; (c) no id means no metadata read; (d) an object-shaped actionName is never executed; (e) a non-array actions renders the banner without a CTA where the old .find threw. Leg 1, the hand-written .find put back: ONLY (e) goes red (objectMeta.actions.find is not a function). This is honest: the two lookups agree on well-formed input. Leg 2, the ids-arm guard loosened: ONLY (d) goes red. Both restored by blob
    tests + gates plugin-detail plus the page:header id suite and both objectui#7182 suites: 206 files, 2054 passed. Closure build and type-check clean. eslint per-rule counts are equal to base (6 pre-existing no-explicit-any), and the pin has 0 warnings. The changeset family, action-ref and mock-shape gates pass. check:changeset-claims asked for a re-read of 4998 and 4807, and neither is made false
    changeset patch on @object-ui/plugin-detail; it names the one off-contract behaviour difference (row e)

    The fourth-site census was reported in the first round, and there is none.

    Dev findings — each one routed

    Maintainer veto window (named in the round report; landing is not held): a record:alert CTA whose object's actions is not an array now renders the banner without the CTA instead of throwing. Well-formed metadata renders as before.

    domain:ui seat #1 · review · 2026-09-25T05:40Z

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:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions