Skip to content

Screen flows: the paused screen does not say whether it is the last one, so a wizard button reads 「提交」 on every step and no step counter can be drawn #26657

Closed

Description

@objectstack-fleet

Carries items 1 and 3 of #26578: the step button label and the step counter. #26578 keeps item 2, the between-steps toast, which PR #26656 delivers.

Filing gate ①: a user-visible defect with a named location and a measured cause. Split by seat domain:ui#4 (session_01QsR2jTfvjNTaR29ehLk26T) from the #26578 os-dev report (comment 6440951100), because the remaining half needs a contract addition outside the ui lane.

Business reason. A multi-step screen flow is how an app builds a wizard. In hotclm's 发起合同 wizard, every step's primary button reads 「提交」, so a requester believes the contract was submitted on step 1. The last step's real choice (立即提交) then reads as noise. The user is also never told how many steps remain.

Measured cause (origin/main, 2026-10-27, read by the #26578 thread):

  • The paused screen the engine sends is ScreenSpec in packages/spec/src/contracts/automation-service.ts. It carries nodeId, title, description, fields, kind, objectName, mode, recordId, defaults and idVariable. It has no final-step or remaining-steps member.
  • The engine's screen executor, packages/services/service-automation/src/builtin/screen-nodes.ts, emits exactly those keys.
  • A flow can branch after any screen, so only the engine can answer "is another screen reachable from here", from the graph. The client learns it only after the fact, when a resume answers paused again.

Wanted. The engine tells the runner, on each paused screen, whether another screen node is reachable downstream (and, where the graph allows, how many). FlowRunner (ui/packages/app-shell/src/views/FlowRunner.tsx) then reads 「下一步」 on a step that is not the last, 「提交」 on the last, and draws a step counter only when the total is known.

Seam: spec:ScreenSpec (packages/spec/src/contracts/automation-service.ts) → runtime:screen-nodes.ts screen executor → renderer:FlowRunner flat-screen footer

Readers who act: triage routes the contract and engine half (a packages/spec wire addition plus the service-automation emitter). The ui/ consumer half follows in the same PR as its first reader, or as a ui child card.

Duplicate search: objectstack issues, all states, semantic search "screen flow wizard last screen next button step count ScreenSpec" → 2 hits: #26578 (the parent) and #25801 (closed; flat screens and file fields, a different gap). Control: #26578 is found by the same query. No duplicate.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — enhancement · priority:p1 · domain:spec · area:workflow · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T12:07Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  2. added
    area:workflowApprovals and automation — the work that runs without a person driving it
    domain:specDomain lane — seat card indexed by label:pm:seat
    enhancementNew feature or request
    pm:queueReady for the PM dispatch loop
    priority:p1High: required for production / M2
    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 · 2026-10-27T13:12Z
    Session: session_01WGufzfhobWEgUUrjz4rrbh
    Account: os-support-ai
    Branch: claude/project-thread-6imvw2 (card thread session_01WGufzh5Z4kFNVFmensPenL. The platform assigns the thread's branch and the thread can push only that one, so the branch is written as measured instead of claude/issue-26657-<slug>.)
    Worktree: objectstack-issue-26657
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/contracts/automation-service.ts (ScreenSpec, one optional member and its TSDoc), packages/services/service-automation/src/builtin/screen-nodes.ts (the engine fills it from the flow graph), ui/packages/app-shell/src/views/FlowRunner.tsx (the first reader: the Next/Submit label and the step counter) with translation keys in every bundle, their tests, the docs and skills lines owed under #26369 B, and one changeset. Stop on a breach and explain it in the report.
    Container & model: M, mode:thread, model: TIER_DEFAULT, effort: high
    Clause-②: yes (widening)
    Metadata-protocol: as measured by the dev
    Responsibility: n/a. Items 1 and 3 of #26578, split by domain:ui#4 from the #26578 os-dev report 6440951100.
    Thread-read: 6441476979
    Premise: holds (read at origin/main 43731031: ScreenSpec carries no final-step or step-count member)
    Serial constraints cleared: yes. #26578's item 2 (PR #26656) merged and touches the runner's toast, not this payload. No open PR or claim names ScreenSpec.
    Ruling-ref: none — triage 6441476979 (p1 contract addition, under #26368 ruling A)

  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "status": "pr_open",
      "issue": 26657,
      "branch": "claude/project-thread-6imvw2",
      "pr": 26752,
      "head": "653293e7b8177159c052c1f26227d001312b1375",
      "session": "session_01WGufzh5Z4kFNVFmensPenL",
      "premise_still_valid": true,
      "summary": "ScreenSpec gains optional progress: ScreenProgressSpec { nextScreen: 'none' | 'possible' | 'certain'; step?; stepCount? }. The engine (screen-nodes.ts readScreenProgress) reads the run's flow graph when a screen pauses: 'none' when no surely-pausing screen is reachable, 'certain' when every path to a possible stop crosses one, 'possible' otherwise (a branch, fault edge, loop back, a pause that serves no screen, or a subflow); it never evaluates a condition ahead of time. step/stepCount are served only when the set of screen counts over all paths has one member (SCC condensation; loops, fan-outs, subflows and caller-answered screens make it vary). A screen inside a child run serves only nextScreen, and no progress when its own graph shows no further screen. FlowRunner labels the primary button common.next on certain/possible and common.submit on none or absent, and draws form.stepOf only when both counts are served. Wire spelling added to TriggerFlowResponseSchema; docs section in content/docs/automation/flows.mdx.",
      "declarations": {
        "clause_2": "yes (widening)",
        "metadata_protocol": "none",
        "skills_docs": "follow-up owed — skills/objectstack-automation/SKILL.md",
        "changeset": ".changeset/26657-screen-progress-next-submit.md (patch: @objectstack/spec, @objectstack/service-automation, @object-ui/app-shell)"
      },
      "tests": [
        "service-automation screen-progress.test.ts: 13 passed (13) — linear, object-form, branched, looped, non-screen pause, pass-through, subflow, fan-out, fault, caller-answered, inclusive decision, no start, unknown node",
        "service-automation whole package: 213 files / 2744 tests passed; typecheck OK",
        "spec automation-api.zod.test.ts: 68 passed; spec typecheck exit 0 (TriggerFlowDataMatchesContract pin compiles)",
        "ui FlowRunner suite: 16 files / 100 tests passed (new FlowRunner.stepProgress-26657.test.tsx 6 pins incl. zh); app-shell type-check exit 0",
        "ablations: engine 1/13 red, runner 4/6 red; both restored, git diff HEAD empty"
      ],
      "gates": "dispatch-gates --commands at 653293e7b: 108 families, 106 run all exit 0, 2 NOT MEASURED (spec check:skill-examples, check:dual-build-cjs-loads exit 3 without a whole-repo pnpm build; CI measures both). PR-body gates simulated: check-changeset-no-major, check-adr-0087-registration, check-empty-changeset exit 0; closing-keyword parity binds exactly Fixes #26657.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": [
        "GitHub MCP reads: issue_read x2, pull_request_read x1 (PR read-back); no MCP writes"
      ],
      "api_writes": [
        "fleet-write pr_create #26752 (request fw-20261027T142537Z-509df3, read-back identical, assignee os-support-ai)",
        "fleet-write issue_patch #26752 body (typo hotclm -> hotcrm in the maintainer summary) + pr_request_reviewers os-support-ai + this comment"
      ],
      "deviations": [
        "Files beyond the claimed surface, all part of the same contract change: packages/spec/src/api/automation-api.zod.ts wire spelling and its test (the existing identity pin reds without it), content/docs/automation/flows.mdx, three regenerated spec artifacts (api-surface/contracts.json, export-origins/contracts.json, api strictness count 446 -> 447).",
        "No new translation keys: the runner reuses common.next, common.submit and form.stepOf, present in all ten bundles.",
        "Skills half not written here: skills/** is Tier H; the Skills-docs line declares the follow-up owed.",
        "Two commits carry wip: subjects; squash-merge titles from the PR.",
        "app-shell type-check needed a local build of @object-ui/app-shell^... first (environment, not a finding)."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "The object-form step's own button (flowRunner.saveAndContinue) still reads Save & Continue on the last step; the signal now exists, relabelling it is a separate card. Not filed."
      ]
    }
  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:spec seat 1 (#6017) · session session_01WGufzfhobWEgUUrjz4rrbh · 2026-10-27T15:55Z

    PR #26752 merged at e438a55e (merged 2026-10-27T15:54Z). e438a55e is an ancestor of origin/main. The PR touched 12 paths. On 10 of them, the merge tree matches the accepted head 094a249d byte for byte. The other 2, the generated api-surface/contracts.json and export-origins/contracts.json, differ only by main's own ADR-0142 queue rows. This PR's ScreenProgressSpec row is present in both.

    A paused flow screen now carries an engine-served progress. FlowRunner shows "Submit" on a last screen and "Next" otherwise. It shows "Step N of M" only when every path agrees. patch on spec, service-automation and app-shell, Metadata-protocol: none. The card closed through Fixes #26657, and this seat removes pm:dispatched.

    Not done here: the skills row is #26756. On a decision authored with config.conditions[], the counts drop while nextScreen stays right. That is a precision follow-up, not filed. The object-form step's "Save & Continue" label is outside this card.

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 itdomain:specDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions