Skip to content

finding: showcase RecalcSelectionAction TSDoc still narrates the pre-objectui#3142 "missing/empty locations → every location" rule #7420

Description

@os-help

Observation-class finding, tripped over while executing #7323 (inverting the same stale claim in docs/qa/platform-checklist/areas/records-forms.json). Filed unassigned, no pm:queue — for triage to grade. Nothing a product user hits; it is stale explanatory prose in an example app.

What

examples/app-showcase/src/ui/actions/index.ts, the TSDoc above RecalcSelectionAction (around L138-149 on origin/main 1788e19), states the pre-objectui#3142 placement rule as current fact:

locations still has to be declared, even though the selection bar entry comes from the view. Omitting it does NOT mean "nowhere": the action:bar renderer treats a missing/empty locations as "every location" (objectui action-bar.tsx), so a locations-less action also lands on the LIST TOOLBAR — where there is no selection, so the dispatch posts no _selectedIds and the endpoint rejects it. Declaring record_more keeps the single-record entry somewhere it works (the endpoint's per-record branch, via recordIdParam) and off the toolbar. See objectui#3142.

Omitting locations does now mean "nowhere". objectui#3142 collapsed four disagreeing renderers onto a single membership predicate, actionRendersAt (objectui packages/types/src/ui-action.ts), so an undeclared or empty locations matches no location at all.

Evidence, in this repo

packages/lint/src/validate-action-locations.ts is built on the opposite rule, and its docblock states it explicitly:

The renderer half is now unambiguous: objectui#3142 collapsed four disagreeing renderers onto one predicate — an action renders at a location only if it DECLARES that location. Before that, action:bar and the record header showed an undeclared action everywhere, which is what made this shape look alive; it is measurably inert as of objectui 17.1.

The whole action-no-placement rule exists because a locations-less action renders nowhere. So the lint package and this fixture comment currently say opposite things about the same behavior.

Why it is worth a card

The comment is not decorative — it is cited as a source by the QA checklist item records-forms.action-location-matrix, and it is very plausibly where that item's now-corrected "empty-locations means EVERY location" probe came from in the first place. A runner or author who follows the source link lands back on the stale rule after #7323 fixed the downstream copy.

The declaration the comment explains is correct either way: locations: ['record_more'] is right, and RecalcSelectionAction needs no change. Only the reason given has inverted — record_more is an explicit single-record placement (which incidentally keeps it off the toolbar), not an opt-out of an everywhere-default.

Worth checking in the same pass: the file's header TSDoc and NewTaskAction's comment both discuss locations: [] as "a declaration in its own right, not an omission", which reads correctly post-#3142 — the defect looks localized to the RecalcSelectionAction block, but the sweep should confirm rather than assume.

Deliberately not fixed in PR for #7323

#7323's dispatch pinned the file surface to docs/qa/platform-checklist/areas/records-forms.json with a stop-on-breach clause, and Prime Directive #10 keeps unrelated fixes out of that PR. That PR instead annotates the source entry so the stale prose is flagged in place until this card is worked.

Scope

Comment-only edit to examples/app-showcase/src/ui/actions/index.ts (plus the sweep above). No behavior change, no spec change.

Activity

  1. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings triage: promoted → pm:queue (kept domain:devx).

    Premise verified on origin/main @ 8a9c079 — the TSDoc above RecalcSelectionAction in examples/app-showcase/src/ui/actions/index.ts still narrates the pre-objectui#3142 rule ("missing/empty locations → every location"), while packages/lint/src/validate-action-locations.ts's docblock states the inverted, current rule (renders only where DECLARED) — the repo currently documents both behaviors as fact, in prose that the QA checklist item records-forms.action-location-matrix cites as a source.

    Promotion rationale — same class as ui#4102 (prose actively misdescribing a made behavior change, promoted 10:20Z round): the comment is load-bearing (QA checklist source), the correction is fully specified by the lint package's own docblock, and the fix is a one-comment rewrite. Cheap, and every day it stands it teaches the stale rule to the next fixture author. No target:v17 (example-app prose).

    Routing note — examples/** belongs to the subsystem it exercises; the stale claim is about action placement (an objectui rendering contract), but the fix lands entirely in this repo's example prose + is QA-checklist-adjacent, so domain:devx (already on the card) stands.

    Dedup — #7323 (the checklist-side inversion, executed) is the sibling this was found from; no open card owns the showcase TSDoc.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. self-assigned this
    on Aug 11, 2026
  3. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    Claim: PM loop round 1 (devx seat, 2026-08-11 term)
    Session: session_01GZKbx4xyF7U5WXj6ch49BM
    Branch: claude/issue-7420-recalc-tsdoc
    Worktree: objectstack-issue-7420
    Domain: domain:devx
    File surface: examples/app-showcase/src/ui/actions/index.ts only (comment-only edit + the in-file confirmation sweep the card asks for; stop on breach)
    Container & model: S, mode:subagent, model: opus — S-sized but the correctness face is prose semantics (not gate-checked), so not sonnet-mechanical; adopting the predecessor's recorded sizing (seat post #6023, 2026-08-11 04:37Z ledger).
    Serial constraints cleared: none — no in-flight claim or open PR touches this file (checked at claim time).


    Generated by Claude Code

  4. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator
    {
      "issue": 7420,
      "status": "done",
      "branch": "claude/issue-7420-recalc-tsdoc",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/7540",
      "premise_still_valid": true,
      "summary": "Premise re-verified stale on origin/main @ 1530870: the TSDoc above RecalcSelectionAction (L141-148) still stated the pre-objectui#3142 rule as fact - 'the action:bar renderer treats a missing/empty locations as every location', with record_more framed as an opt-out of that everywhere-default. Rewrote the block to mirror packages/lint/src/validate-action-locations.ts's own docblock: placement is declared, not inherited (actionRendersAt is a membership test, so missing or empty locations matches NO location), locations: [] is that same nowhere said deliberately (cross-ref to NewTaskAction), and record_more is an explicit single-record placement feeding the endpoint's per-record branch via recordIdParam - with staying off the list toolbar preserved as a consequence of naming one location rather than the reason for it. PM assumption 1 CONFIRMED: the declaration itself (locations: ['record_more'], recordIdParam: 'recordId') is correct either way and is unchanged; the view-side half of the comment (the aggregate bulkActionDefs entry in task.view.ts) is also still accurate. PM assumption 2 CONFIRMED, not assumed - see the sweep below. Comment lines only; the file is byte-identical to origin/main after stripping comments.",
      "tests": "node scripts/check-nul-bytes.mjs -> 'OK (scanned 7000 text file(s) -- 7000 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).' | targeted self-scan grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' on the edited file -> exit 1 (no matches). | pnpm --workspace-concurrency=2 --filter '@objectstack/example-showcase^...' build -> dependency closure built first in the fresh worktree (packages/cli build: Done). | pnpm --workspace-concurrency=2 --filter @objectstack/example-showcase typecheck -> 'tsc --noEmit', TYPECHECK_EXIT=0 (the expected no-op, run to prove zero behavior change). | pnpm --filter @objectstack/example-showcase test -- --maxWorkers=2 -> 'Test Files 17 passed (17) / Tests 168 passed (168)'. | Comment-only proven two ways: (a) git diff -U0 filtered for non-comment changed lines -> none found; (b) strip comments + normalize whitespace, compare to git show origin/main:<file> -> 'code-identical after comment strip: true (bytes 6235 vs 6235)'. | All heavy steps ran under flock /tmp/os-heavy-verify.lock with NODE_OPTIONS=--max-old-space-size=4096. | CI: not polled - reporting at draft-PR time per contract; gate status in_progress, PM owns convergence.",
      "open_questions": [],
      "out_of_scope_findings": [
        "IN-FILE SWEEP RESULT (the card's assumption 2, confirmed rather than assumed): the defect is localized to the RecalcSelectionAction block. The file header TSDoc ('locations: []' as 'a declaration in its own right, not an omission'), NewTaskAction ('[]', 'nowhere, deliberately', plus the #6888 global_nav retirement note), PortfolioSnapshotAction (same headless framing for the object-less specimen) and MarkDoneAction ('the engine location-filters even explicitly-named actions') all read CORRECTLY post-objectui#3142 and were left unchanged. A grep across examples/ for 'every location' / 'everywhere-default' / 'missing/empty locations' returns exactly one hit: the block this PR rewrites.",
        "NOT FILED, for the PM to fold in or dispatch - deliberately not a new issue because it is a consequence of this PR landing rather than a pre-existing defect, and it lives outside this card's pinned file surface (stop-on-breach): docs/qa/platform-checklist/areas/records-forms.json, item records-forms.action-location-matrix, second 'source' entry carries the inline NOTE 'RecalcSelectionAction's TSDoc there still narrates the pre-objectui#3142 rule - stale prose, filed separately'. #7323 added that as an explicit temporary marker 'until this card is worked'; it becomes false the moment PR #7540 merges. One-line deletion in that source string.",
        "PROCESS CONFLICT surfaced rather than silently resolved (agent definition vs card): my agent definition makes applying skip-changeset my own step with a labels read-back; this card's clauses assign it to the PM at acceptance. I did NOT apply it. Beyond the card's instruction, the read-back precondition is unavailable to me here - unauthenticated REST returns 'GitHub access is not enabled for this session', and pull_request_read get does not return a labels array - so a bare whole-set PUT of ['skip-changeset'] would risk wiping the bots' size/documentation labels, which is exactly the harm the read-back exists to prevent. Flagging so the PM applies it knowingly; PR #7540 writes no changeset by design (example-app prose, nothing releases)."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  5. huangyiirene commented on Aug 11, 2026

    @huangyiirene
    Collaborator

    ACCEPT — PR #7540 (draft, CI converging).

    Review record: changed-file list is exactly the card's pinned surface (examples/app-showcase/src/ui/actions/index.ts, one file); every changed line in the diff is a block-comment line, and the dev proved comment-only mechanically (byte-identical after comment strip, 6235 bytes both sides). The rewrite mirrors packages/lint/src/validate-action-locations.ts's docblock rather than inventing semantics, and stays a sensible citation target for the QA checklist item. The card's confirmation sweep was executed, not assumed: the file's other three action comments + header all read correctly post-objectui#3142; repo-wide grep for the stale claim returns exactly the one rewritten block. skip-changeset applied by the PM (read-merge-write, preserving the bot's size/s).

    Follow-up filed as its own card (linkage chore, not a rider): the now-obsolete inline NOTE in docs/qa/platform-checklist/areas/records-forms.json's records-forms.action-location-matrix source entry — blocked by this PR's merge; see the new issue referencing #7540.

    Landing: PM flips ready + arms auto-merge once the gate-bearing jobs (ESLint, TypeScript Type Check) conclude success.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions