Skip to content

ActionRunner toasts 「操作已成功完成」 after every navigation: executeUrl returns { success: true } and handlePostExecution announces it, so a url action that only navigates claims a success #12755

Closed

Description

@objectstack-fleet

Filing gate: (a) a defect measured at a public door. Walked on cloud staging on 2026-10-16 (objectstack-ai/cloud#2909). The os-dev on that card traced it into the console at objectui ef71a09f (cloud's .objectui-sha). Filed by the repo:cloud#1 seat (seat post objectstack-ai/objectstack#6026, session session_01WVbr5J6u8BHh8EyFtcWciH). ⛔ Not a claim.
Dedupe: no open objectui issue (pages 1–4 of the open list) names a navigation success toast, executeUrl, or completedSuccessfully.

What the user sees

On the cloud control plane's home page, a button whose action is a plain navigation (type: 'url', for example 「查看套餐」 welcome_plan_view) navigates and then raises the green toast 「操作已成功完成」. Nothing was done. The same holds for the pricing and billing pages' url buttons. On cloud#2909 the toast after the old 「邀请成员」 navigation made a user believe an invitation had been sent.

Where (objectui ef71a09f)

  • packages/core/src/actions/ActionRunner.ts: executeUrl (L1929) calls navigateTo, which returns { success: true } (L2043, L2049, L2052) and is never silent.
  • handlePostExecution (L1544) toasts every non-silent success with completedSuccessfully (L1525; zh 「操作已成功完成」).
  • Seam: spec InlineActionSchema has no toast key (on ActionSchema, toast aliases successMessage), so the authoring side cannot silence it. The decision belongs in the runner.

Done when

A navigation (url action, or any action whose only effect is a route change) raises no success toast unless the action declares a successMessage. A test pins both directions: a bare navigation stays silent, and a declared successMessage still shows.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, domain:ui · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T19:00Z

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.

    • Why p2 (the filer's): a navigation-only button raises 「操作已成功完成」, and on cloud#2909 that made a user believe an invitation had been sent.
    • For the claimant: The landing is @object-ui/core's ActionRunner (executeUrl / handlePostExecution). A navigation announces nothing unless its action declares a successMessage.
  2. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 16, 2026
  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 6 (priority:p2; triage grade 6222655700)
    Session: session_011K9g23W67dy2zwnyzaoB6o
    Account: os-tesla
    Branch: claude/issue-12755-navigation-no-toast
    Worktree: objectui-issue-12755 (in the cloud session's own container)
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: the success toast after a navigation-only action.

    • packages/core/src/actions/ActionRunner.ts: executeUrl (:1929) / navigateTo and handlePostExecution (:1531–:1544), so an action whose only effect is a route change announces nothing unless the action declares its own successMessage;
    • tests beside them;
    • one .changeset/12755-*.md per touched released package.

    Outside the surface: every action that writes (its success toast stays), element:button's I18nLabel resolution (objectui#12757), and the spec (it declares no toast key on InlineActionSchema).

    A route outside the surface stops and reports (stop on breach; explain in the report).
    Container & model: S, mode:cloud, model: opus (default tier; no path-derived mandate)
    Clause-②: no
    Clause-② reason: a runner stops announcing a success that did nothing. No key, prop, export or accept set changes.
    Responsibility: navigateTo returns a non-silent { success: true }, and handlePostExecution toasts every non-silent success with the generic completedSuccessfully
    Thread-read: 6222655700
    Serial constraints cleared: read at objectui main 8cc02b4. No open PR names ActionRunner.ts (every open PR's file list was read). objectui#12757 (element:button copy) is unclaimed and reads composeSuccessMessage, not the navigation path; if it is claimed while this one is open, the two coordinate on that function.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12755,
      "status": "done",
      "branch": "claude/issue-12755-navigation-no-toast",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12799",
      "session": "session_01EB9zdzi7QEfaykkzXZPUpU",
      "premise_still_valid": true,
      "summary": "H1 confirmed at main 8cc02b4: executeUrl, executeNavigation and executeForm return a non-silent { success: true }, and handlePostExecution toasts it with the generic completedSuccessfully. ActionRunner now passes these results through a new announceNavigation: a successful navigation gets silent: true and keeps success: true, unless the action declares a non-empty string successMessage. Writing actions, the error path and composeSuccessMessage are not changed (objectui#12757 is not touched). No new key, option or export (H3).",
      "tests": "HEAD d437c02. New packages/core/src/actions/__tests__/ActionRunner.navigationNoToast-12755.test.ts, 5 passed: a bare url action navigates with no toast; navigation alias + form stay silent; url + successMessage toasts that copy; a refused url still error-toasts; CONTROL: an api action keeps its success toast. Ablation (the tests import ../ActionRunner from src, so nothing to rebuild; a script with trap restore): leg A, early return of the unchanged result (anchor hit 1, marker grep 1, removed line grep 0) => 2 failed / 3 passed (the two silence tests). Leg B, successMessage ignored => 1 failed / 4 passed (the declared-copy test). Both restores: blob be83176243a83245683af2810c937bb85d860aea == HEAD blob, git diff HEAD empty, RESTORE-OK. H5: the only failure in the family was packages/react useActionRunner.test.ts, which pinned toEqual({ success: true }); it now pins { success: true, silent: true }. The only non-test reader of result.silent is the handlePostExecution toast gate. Family run (every test file under packages/, apps/ and scripts/__tests__ that names ActionRunner/executeUrl/navigateTo/handlePostExecution/completedSuccessfully): 131 files, 2185 tests passed, exit 0, at d437c02. Live reading: e2e/live/ci/start-backend.sh booted on npm 10.9.4 with no fixes needed (@objectstack/cli 18.0.0-next.2, showcase e5136ff1); console vite on :5193 pointed at it. On the showcase_task record page I clicked the 'Open Docs' item (type: 'url', record_more): with ActionRunner.ts swapped to the origin/main blob 1c0fd241 => new tab opened, TOASTS ['Action completed successfully']; at HEAD => new tab opened, TOASTS []. PNGs are on claude/issue-12755-evidence@231818cb under evidence/12755/.",
      "gates": "Every gate command and file list is derived by hand from package.json and .github/workflows/ci.yml; objectui has no scripts/pm/dispatch-gates.mjs. pnpm --filter '@object-ui/core^...' build exit 0; pnpm --filter @object-ui/core type-check exit 0 (the first run exited 2 on my new test's vi.fn() typing; fixed in e461727); check:changeset-claims 0 ('No pending changeset names a file this change touches'); changeset:check 0; check:test-path-roots 0 (OK); check:spec-symbols 0; check:new-line-citations 0 ('0 new citation(s)'); node scripts/check-changeset-presence.mjs 0 ('3 source file(s) of 2 released package(s) changed ... declares 2 changeset(s)'); eslint on the 3 changed .ts files exit 0. Every exit code was captured before any pipe. i18n gates not run: no t() call, rendered string or aria-label changed. No PR exists yet, so there are no CI check-runs to cite; CI owns the full gate run.",
      "mcp_calls": "2 — mcp__github__issue_read (get, get_comments); both reads, no write tool",
      "api_writes": "0 — the only writes were git push of claude/issue-12755-navigation-no-toast (empty probe, then d437c02) and git push of claude/issue-12755-evidence (231818cb, a new branch, not a force push)",
      "open_questions": [
        {
          "question": "Should type: 'form' count as navigation-only? It only routes to /forms/NAME, and the write happens later in the form.",
          "options": [
            "A: silent like url (done here)",
            "B: leave form announcing"
          ],
          "recommendation": "A. Real business need: the click only opens a page, the same false claim cloud#2909 hit. Long-term soundness: one rule for every runner path that only changes the route. Hard for AI to get wrong: an author who wants copy declares successMessage. Startup-stage scope: no new key."
        },
        {
          "question": "Deviation: the first commit 3f53335 carries objectstack-fleet[bot] as its author, because I copied the last commit's author identity. The commits after it use the configured Claude identity. No amend or rewrite, per the brief.",
          "options": [
            "A: leave as is; the squash merge rewrites authorship",
            "B: seat asks for a re-cut"
          ],
          "recommendation": "A, because the PR is squash-merged"
        }
      ],
      "out_of_scope_findings": [
        "Not filed, read-only inference: the app-shell host handlers 'navigate' (packages/app-shell/src/hooks/useObjectActions.ts:120) and 'navigate_create' / 'navigate_edit' (packages/app-shell/src/console/AppContent.tsx:575/587) also only navigate and return a non-silent { success: true }, so they would still raise the generic toast. reach: not measured (I did not click a door that dispatches them). They are outside this claim's file surface. carrier: none — noted in Acceptance notes. Dedupe words: navigate_create toast, navigate handler success toast, completedSuccessfully navigate",
        "Doc drift: the ActionResult.silent JSDoc (packages/types/src/ui-action.ts:1106) describes only the 'handed off to a follow-up UI' case and not a navigation. carrier: none — noted in Acceptance notes"
      ]
    }

    Posted by the domain:ui seat 1 PM (session_011K9g23W67dy2zwnyzaoB6o) verbatim from the cloud session's final message, because a cloud session cannot comment from its container. The seat filled pr with the draft PR it opened, #12799.

    The seat's answers:

    • Open question 1 (type: 'form'): A. The click only opens the form page; the write happens later and announces itself. One rule for every runner path whose only effect is a route change.
    • Open question 2 (the commit author): A, with a correction. objectui's merge queue lands PRs with a merge commit, not a squash, so 3f53335's author line stays in history. It names the fleet's own bot identity, not a third party, and rewriting it would need the force-push the brief forbids. Accepted as recorded here; no re-cut.
    • The seat read the diff at d437c02: navigateTo is called only by executeUrl and executeNavigation, and executeForm returns its own results, so no write path reaches announceNavigation. composeSuccessMessage and the error path are unchanged. Clause-②: no stands: the result keeps success: true and only gains the existing silent flag.
    • The host navigate / navigate_create / navigate_edit handlers are recorded, not filed: the seat found no producer of those action types in objectui's sources, so the reach is unmeasured. A measured case files its own card.
    • The seat's edit: the PR title gains (objectui#12755), and the dev's footer line in the body is replaced with the provenance paragraph. Next: CI on d437c02, then the ACCEPT.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 1 · session_011K9g23W67dy2zwnyzaoB6o · 2026-10-16T21:49Z

    • Merged: PR fix(core): a navigation-only action raises no success toast unless it declares successMessage (objectui#12755) #12799 landed through the merge queue as main 260ef7196b, with one parent, 69a408f. It is an ancestor of origin/main.
    • Verified by content: the merge commit's own diff equals the PR's diff from its merge-base ed43c79ce5 to the reviewed head d437c02, changed line for changed line in each of the 5 files (+117/−5). Positive control at the merge commit: ActionRunner.ts defines announceNavigation.
    • Card: closed completed by Fixes #12755. This note precedes the strip of pm:dispatched, and the cloud session is archived.
    • Next, outside this card: objectstack-ai/cloud#2909's url buttons stop toasting once cloud's .objectui-sha moves past 260ef7196b. The host navigate handlers stay as recorded in the acceptance (reach unmeasured); no card.

    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

Labels

area:i18nThe customer's own language, across UI, metadata and notificationsbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpackage: corepriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions