Skip to content

Screen flow wizard: every step's button reads 「提交」 and every step toasts 「已保存 — 下一步」, so a user cannot tell which click submits #26578

Closed

Description

@objectstack-fleet

Filing class: ① user-visible defect — reach: public door, measured in a browser on the 18.1.0 Console (objectstack dev --ui) with objectstack-ai/hotclm main, and the code read on objectstack main 1c632311. Reader: the UI lane, ui/packages/app-shell/src/views/FlowRunner.tsx.

Business reason

A multi-step screen flow is how an app builds a wizard. hotclm's 发起合同 wizard has five flat steps. Its last step asks 「立即提交」 — whether to send the contract to legal or keep it as a draft. Every earlier step's button already says 「提交」, so a requester reasonably believes the contract was submitted on step 1, and the last step's choice reads as noise.

Measured

  • Step 1 「合同信息」 (a flat screen, 12 fields), step 3 「首个版本」, step 5 「提交合同」: the footer is 取消 / 提交 on each. The label is fixed: FlowRunner.tsx:740 renders t('common.submit') for every non-terminal flat screen, and the screen config has no key for it.
  • After each step a success toast 「已保存 — 下一步」 appears (FlowRunner.tsx:530, flowRunner.nextStep), even after a step that saved nothing (step 1 only collects variables; the record is created two steps later).
  • No step indicator: the dialog does not say 「第 2 步,共 5 步」, so the user does not know how far they are. The object-form wizard has stepFallback / progressLabel keys; the flat-screen path draws neither.

Expected

  • A non-final screen's primary button reads 下一步 (or a label the screen declares); only the last screen reads 提交 or the flow's own wording.
  • No success toast between steps, or one only when a step actually wrote a record.
  • A step counter when the run knows it has more screens.

Dedupe: objectstack issues (2,000 most recent, all states) and the archived objectui issues grepped for nextStep, Saved — next, Save & Continue, step indicator, screen flow + submit — objectui#12088 (Submit stays on screen for a long form) and #12234 / #12246 (terminal toast copy) are neighbours; none covers the per-step label or toast.


Filed from the hotclm UI/UX review (2026-10-27); maintainer: 「平台的问题应该提交issue到平台」.

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p2 · domain:ui · area:workflow · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T08:59Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • p2: every wizard step's button reads 「提交」 and toasts 「已保存 — 下一步」. FlowRunner.tsx:740 should say "next" on a step that is not the last and "submit" only on the last, and the toast should follow. If an author-declared step button label is wanted, that is a protocol addition, and it comes back to triage first.

    Generated by Claude Code

  2. added
    area:workflowApprovals and automation — the work that runs without a person driving it
    bugSomething isn't working
    domain:uiDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 27, 2026
  3. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01QsR2jTfvjNTaR29ehLk26T
    Account: zhuangjianguo
    Branch: claude/project-thread-xuv7w9 (platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)
    Worktree: objectstack-issue-26578
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: ui/packages/app-shell/src/views/FlowRunner.tsx, its tests, one root changeset; ⛔ ScreenView.tsx field widgets (#26579) (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate; reason: the maintainer's standing rule 「只认领不需要 fable 的卡」, 2026-10-27T10:39Z), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: FlowRunner.tsx (app-shell, this lane) hard-codes common.submit and the between-steps toast · no platform path covers it · reached by every user of a multi-step screen flow (hotclm 发起合同 wizard)
    Thread-read: 6437749475
    Premise: holds (action, card-reference and work-item surfaces re-read on origin/main 8c0306f5 at 2026-10-27T10:50Z)
    Serial constraints cleared: none: no open PR or in-flight claim on FlowRunner.tsx (scan 2026-10-27T10:50Z)

    Dispatched at 2026-10-27T10:55Z to the card thread on that branch; the order lives in the thread's opening message.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    Posted: 2026-10-27T11:37Z

    {
      "report": "os-dev-report",
      "issue": 26578,
      "card": "objectstack-ai/objectstack#26578",
      "claim": "6440134516",
      "ruling_ref": "6437749475",
      "status": "needs_decision",
      "branch": "claude/project-thread-xuv7w9",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26656",
      "head_sha": "a746e5660f927158534c1d6a74c80e72bc8a2979",
      "commits": [
        "014de59df96093d3ab3b212f6eff8b3e43048b75 fix(app-shell): a screen-flow wizard step toasts \"Saved — next step\" only when it saved a record",
        "a746e5660f927158534c1d6a74c80e72bc8a2979 test(app-shell): the objectui#12234 multi-step pin reads no toast between flat screens"
      ],
      "session": "session_01QsR2jQzKBjxZXgyVnom2V3",
      "premise_still_valid": true,
      "summary": "Toast half landed: FlowRunner's resumeWith takes savedRecord; on a paused answer the flowRunner.nextStep toast fires only for an object-form step (it persisted its record, and onSuccess silences ObjectForm's own confirmation). A flat step advances with no toast; terminal, refused and failed paths unchanged. Button half NOT built: measured that the paused ScreenSpec (packages/spec/src/contracts/automation-service.ts) carries nodeId, title, description, fields, kind, objectName, mode, recordId, defaults, idVariable and no final-step or step-count member, and the screen executor (service-automation builtin/screen-nodes.ts) emits exactly those; a flow can branch after any screen, so the runner cannot know the last screen before the server answers. The only client signal is after the fact (a resume answering paused proves the previous step was not last). Hence no Next label and no step counter; PR body says Part of #26578.",
      "tests": "head a746e566. ui root under os-verify-lock: vitest run --maxWorkers=2 packages/app-shell/src/views/__tests__/FlowRunner -> Test Files 15 passed (15), Tests 94 passed (94), VERDICT command-exit 0. New file FlowRunner.betweenStepsToast-26578.test.tsx (3 pins); FlowRunner.actionSuccessMessage-12234.test.tsx multi-step pin updated from [['已保存 — 下一步'],[ACTION]] to [[ACTION]]. Ablation via scripts/ablation-replace.mjs: anchor 'if (savedRecord) toast.success(' -> 'if (savedRecord || true) toast.success(' (anchor 1->0, blob 1ed20f953095->244560514f7f): Tests 2 failed | 10 passed (flat-step pin and 12234 pin red), restored blob == HEAD, git diff HEAD empty. No dist build needed: tests import ../FlowRunner from src.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 34 commands, each run with exit code captured before any pipe; all 34 exit 0. --ran at a746e566: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. pnpm --filter @object-ui/app-shell type-check (tsc --noEmit + tsc -p tsconfig.test.json, the latter covers __tests__): VERDICT command-exit 0. ui check:new-line-citations: 0 new. PR-body gates simulated via GITHUB_EVENT_PATH before pr_create: closing-keyword-parity, changeset-no-major, adr-0087-registration, empty-changeset all exit 0. CI check-runs on #26656: in_progress at report time.",
      "line_budget": "+190 / -12 over 4 files (changeset 20, FlowRunner.tsx +30/-7, 12234 test +7/-5, new test 133); under the 3000 human-merge threshold",
      "files_changed": [
        ".changeset/26578-flow-wizard-between-steps-toast.md",
        "ui/packages/app-shell/src/views/FlowRunner.tsx",
        "ui/packages/app-shell/src/views/__tests__/FlowRunner.actionSuccessMessage-12234.test.tsx",
        "ui/packages/app-shell/src/views/__tests__/FlowRunner.betweenStepsToast-26578.test.tsx"
      ],
      "deviations": [
        "Card half-closed: PR body says Part of #26578, not a closing keyword, because the Next/Submit label needs a wire signal that does not exist (see open_questions).",
        "File surface: one file beyond FlowRunner's own tests touched, FlowRunner.actionSuccessMessage-12234.test.tsx, whose multi-step pin asserted the old between-steps toast; it is a FlowRunner test, inside the claimed surface.",
        "No locale-pack key added; Clause-② no and Metadata-protocol none copied verbatim from the claim into the PR body and changeset.",
        "First ablation attempt used an undeclared identifier in the replacement (threw instead of toasting) and was discarded as invalid; the reported ablation is the second.",
        "Gate loop started at 014de59d and finished after a746e566 landed (a test-only commit); --ran was reconciled at a746e566 and derived the same 34 commands."
      ],
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — MCP GitHub calls: none (reads by plain REST curl; mcp__claude-code-remote__get_session ×1 for the session id, not GitHub)",
      "api_writes": "2 relay strokes — fleet-write pr_create (run 40497397032: POST pulls, POST issues/26656/assignees), and this os-dev-report comment via post-stamped (POST issues/26578/comments); git push ×2 is not a REST write",
      "open_questions": [
        {
          "question": "How should a non-final wizard step say Next, given the runner cannot know which screen is last?",
          "options": [
            "A: the engine computes, at suspend, whether any screen node is reachable downstream of this one and serves it as a new optional wire member on ScreenSpec (contracts, not an authorable key); the runner shows common.next while one may follow and common.submit otherwise. Contract addition in packages/spec/src/contracts plus service-automation and FlowRunner; owes a contract-tier review.",
            "B: an author-declared per-screen button label key (protocol addition; triage ruled it goes back to triage first).",
            "C: a neutral label on every step (e.g. Continue) with no signal; makes the final step read Continue, which the card's own business case (last step decides submit to legal) argues against.",
            "D: leave Submit on every step; the toast half alone is the fix."
          ],
          "recommendation": "A. Business need: measured on the card (hotclm five-step contract wizard), the label is the headline complaint. Long-term: the engine owns the graph, so it is the only honest producer; a client-side graph walk would duplicate engine semantics. AI-error resistance: a served fact needs no authoring, so no author can mis-declare it, unlike B. Startup focus: one optional wire member with one producer and one consumer, no new authorable surface. Needs a spec-lane card and the contract-tier review, which this seat does not take under the no-Fable rule."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — an object-form step's button reads 'Save & Continue' on every step including the last; same last-step signal gap as open question A; recorded in this report only."
      ],
      "thread": "stays open for review and patch rounds; worktree removed after this report (a patch round re-creates it from the pushed branch)"
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat note · domain:ui#4 · session_01QsR2jTfvjNTaR29ehLk26T · 2026-10-27T11:41Z

    Split: items 1 and 3 of this card (the step button label and the step counter) now live on #26657. The os-dev report (comment 6440951100) measured that the paused ScreenSpec carries no final-step or step-count member. Closing them therefore needs an engine + packages/spec wire addition, which goes to triage, as triage's own grade said it would. This card keeps item 2, the between-steps toast, delivered by PR #26656 (Part of). When that PR merges, the seat closes this card as completed, pointing at #26657.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat close · domain:ui#4 · session_01QsR2jTfvjNTaR29ehLk26T

    PR #26656 merged. It delivers this card's item 2: the between-steps toast now fires only after a step that wrote a record. Items 1 and 3 (the step button label and the step counter) continue on #26657, per the split note 6441002688. Closing as completed.


    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:workflowApprovals and automation — the work that runs without a person driving itbugSomething isn't workingdomain:uiDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions