Skip to content

Screen flow dialog: an empty required field makes Submit a silent no-op, and a refused submit removes Submit so the user must close and retype everything #26882

Closed

Description

@objectstack-fleet

Filing gate ① a product defect with a reproduction. Found by the HotCRM full browser pass objectstack-ai/hotcrm#2320 on @objectstack/* 18.2.0: track 1 Sales (report comment 6443461344 on objectstack-ai/hotcrm#2320, finding D1; screenshots on branch claude/project-thread-2kd6ha under qa/ and in the HotCRM project files qa-2320/sales/); track 2 Revenue (report comment 6444188882 on objectstack-ai/hotcrm#2320, finding D1; screenshots on branch claude/project-thread-urevq9 under qa/ and in the HotCRM project files qa-2320/2-revenue/). Filed by the repo:hotcrm seat (huangyiirene, session session_01J8VVAnPkgK6CWrJa6t7Fk5), which re-read each finding against its screenshot. Batch-of: hotcrm#2320 platform findings.

finding class a · reach: public door: the console on @objectstack/* 18.2.0, measured in Chromium against the HotCRM seed.

What is seen

  1. Empty required field → nothing happens. HotCRM lead → Schedule Follow-up → type the next step, leave Due Date (declared required: true, drawn with *) empty → Submit. No inline message, no toast, aria-invalid on no field, the date input's validationMessage is empty, and no request is sent (lea-15-followup-submit-empty.png). The record form in the same console says "Disqualification Reason is required" in the same situation, and the public form says "Required: …".
  2. A refusal removes Submit. Opportunity → Generate Quote → Discount 75 → Submit. The flow's refusal "Discount cannot exceed 60%" is shown, and the footer keeps only Close: the user must close the dialog, start the action again and retype every field to correct one value (quo-12-generate-discount-75.png).

Who loses: every screen flow inherits both (conversion wizard, generate quote, log a call, close case…): the first looks like a broken button, the second turns a one-field correction into starting over.

Expected

The screen-flow dialog validates required fields the way the record form does (message at the field, Submit stays), and after a refused resume it keeps the screen and its values with Submit live, the refusal shown at the top or at the field it names.

Dedupe

search_issues objectstack "console screen flow dialog required field silent submit" and "screen flow refused submit error removes Submit button only Close": 7 hits — #26578 and #26657 (closed, the button's wording on multi-step wizards), #4477 (closed, server-side validation on resume), #3528 (closed, Submit never called resume), #14945 (closed, message-only screens), #25874 (closed, record dialog banner), #26730 (open, Studio publish refusals). None reports either symptom.

Reader who acts: the console screen-flow dialog owner (domain:ui).


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiDomain lane — seat card indexed by label:pm:seat
    area:workflowApprovals and automation — the work that runs without a person driving it
    on Oct 27, 2026
  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: audit line · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T19:57Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • Triage: lands in the screen-flow dialog (ui/); rationale: p2 · domain:ui · area:workflow. An empty required field makes Submit a silent no-op, and a refused submit removes Submit. Show the required-field error inline, and keep the entered values and the Submit button after a refusal.

    Generated by Claude Code

  3. added
    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
  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01QsR2jTfvjNTaR29ehLk26T
    Account: zhuangjianguo
    Branch: claude/project-thread-4ybq1f (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-26882
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: ui/packages/app-shell/src/views/FlowRunner.tsx, ui/packages/app-shell/src/views/ScreenView.tsx (field-level error display), i18n rows only if no existing key fits, tests beside, one root changeset; ⛔ packages/spec/**, server packages, flow definitions in examples (stop on breach; a terminal-refusal fix that needs the server is reported, not built)
    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: the console screen-flow dialog (ui app-shell, this lane) answers an empty required field with no visible refusal and drops Submit after a refusal · the platform path that may cover it: the engine's resume validation and its retryable verdict, which decide whether a resubmit can succeed · reached by every screen flow (conversion wizard, generate quote, log a call, close case)
    Thread-read: 6452161045
    Premise: holds (FlowRunner.submit refuses a missing required field only through a toast at FlowRunner.tsx:601-611, and terminal withdraws Submit at ~673, on origin/main 7a2818c5 at 2026-10-27T22:15Z)
    Serial constraints cleared: no in-flight card or open PR edits FlowRunner.tsx or ScreenView.tsx

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


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26882,
      "status": "done",
      "branch": "claude/project-thread-4ybq1f",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26956",
      "session": "session_01QsR2jQtUhXRiBQeTgVcy19",
      "premise_still_valid": true,
      "summary": "Required half fixed: FlowRunner's submit no longer answers a missing required field with a toast only; each missing field gets validation.required under its control via a new optional ScreenView errors prop, the control is aria-invalid and described by the message, focus moves to the first missing field, Submit stays, no request and no toast (the record form raises none). Refusal half measured, not built: Submit is withdrawn only on a terminal disposition (refused end, 400 FLOW_FAILED, 404, failed run under 200), and on those the engine has already consumed the suspension (resumeInternal calls forgetSuspendedRun before traversal; restore-suspension is an operator verb), so the ui cannot resubmit. A retryable INVALID_SCREEN_INPUT already kept values and Submit; now pinned. The terminal fix is flow-side: declare max 60 on HotCRM's discount screen field so the engine refuses it before consuming the suspension.",
      "tests": "head 101d2de2. New FlowRunner.requiredAtField-26882.test.tsx 5/5 green. Reverse: FlowRunner.tsx + ScreenView.tsx from 7a2818c5 -> 4 failed | 1 passed, each naming the missing sentence (Unable to find an element with the text: Due Date is required); retryable case green on main as expected (existing behaviour); restored from HEAD, git diff HEAD empty, blob hash equal 9825bd5e. Narrowed ui run (git grep -l FlowRunner|ScreenView|ScreenFieldInput, 116 files incl. 2 apps/console) at eea769cf: 114 passed, 2 failed = FlowRunner.fileField-25801 and FlowRunner.flowsTranslation-5920, both pinning the retired toast; updated to read the field message and assert no toast, rerun at 101d2de2: 2 files 14/14 green. app-shell type-check + i18n type-check exit 0 (VERDICT 0). Measurement on 7a2818c5 (jsdom, card's screen): 0 resume requests, toast text 'Please complete the required fields: Due Date', 0 aria-invalid elements.",
      "gates": "dispatch-gates --commands at 101d2de2: 34 derived; all 34 run, each exit 0 (incl. check:type-check-debt, check:query-options-erasure, check:sdui-manifest, pnpm --dir ui exec vitest run scripts/ = 178 files passed, 2 skipped, after a spec rebuild); --ran reconciliation: 34 run, 0 NOT-MEASURED, 0 UNRUN. Outside the derived set: ui check:new-line-citations 0 new -> exit 0; PR-body gates on the sent body: check-changeset-no-major 0, check-adr-0087-registration 0, check-closing-keyword-parity 0, check-partof-closing-keyword 0. CI check-runs on PR 26956: in_progress at report time.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 GitHub MCP calls (card and comments read through unauthenticated public REST GETs; one claude-code-remote get_session for the trailer id)",
      "api_writes": "2 — fleet-write relay: pr_create objectstack-ai/objectstack#26956 (read-back byte-identical, assignee zhuangjianguo), comment on #26882 (this report). git push x2 not counted.",
      "deviations": [
        "ui/packages/i18n/src/locales/en.ts: comment-only edit (no catalogue row) — the flowRunner block's comment named wizard.missingRequired as the runner's borrowed key, false after this change; now names validation.required. Outside the claim's 'i18n rows only if no existing key fits'.",
        "Two existing suites edited (FlowRunner.fileField-25801, FlowRunner.flowsTranslation-5920): they pinned the toast the card's ruling retires.",
        "Changeset lists only @object-ui/app-shell (private, bundled in the console), matching the last app-shell card's shape; en.ts is comment-only so @object-ui/i18n is not listed.",
        "Narrowed ui run declared: 116 files naming the touched components; CI Test UI runs the rest."
      ],
      "open_questions": [
        {
          "question": "The min/max bound refusal (screenFieldBoundViolations) is still toast-only. Move it to the field too?",
          "options": ["A: yes, a follow-up card; it is one more entry source for the same errors map, and HotCRM's flow-side fix for the discount case lands on exactly this bound", "B: leave it as a toast"],
          "recommendation": "A, because the record form draws min/max at the field with no toast, and the discount fix below routes the Generate Quote correction through this check."
        }
      ],
      "out_of_scope_findings": [
        "class: a · reach: HotCRM Generate Quote, Discount 75 -> 'Discount cannot exceed 60%' then only Close (hotcrm#2320 D1) · evidence: the run is terminal (refused end or FLOW_FAILED after forgetSuspendedRun; which one is NOT MEASURED, the flow lives in objectstack-ai/hotcrm, outside this session's repos); fix is flow-authoring: declare max: 60 on the discount screen field so the engine answers INVALID_SCREEN_INPUT before consuming the suspension · carrier: repo:hotcrm seat · dedupe words: generate quote discount screen field max, refusal removes submit, consumed suspension"
      ]
    }

    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:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions