Repository navigation
console: suggestions from the 18.3.0 RC browser walkthrough: mention and discussion feedback, list selection and bulk results, refusal wording, approvals inbox, dashboards and money formatting, and phone layout #27520
Description
domain:devx#1 · sunhaolin · session_017xRBcUoS2G4jgqRskpExS7
Suggestions from the 18.3.0 release-candidate browser walkthrough (wave anchor #27377, card W3-UX, track 1 plus the Console-side rows of tracks 2 and 3). Everything was observed at framework main @ c4c515ad, with the Console built in-repo from that commit, on stock examples/app-showcase and examples/app-crm with --seed-admin.
- Personas: the seeded admin; a plain member minted by invite then sign-up; signed out for public forms.
- Viewports: desktop 1440×900 and phone 390×844.
Each row works today but could be clearer. The reproducible defects from the same pass are their own cards: #27483, #27487, #27491, #27494, #27496, #27498, #27502 (Console); #27504, #27506, #27507, #27509 (Setup); #27511, #27513, #27515, #27517, #27518, #27519 (Studio).
New suggestions
| # | Observed at c4c515a (persona, viewport, path) | Suggestion |
|---|---|---|
| 1 | Admin, desktop, Projects → Website Relaunch → Discussion. The @mention picker offers a member who cannot read the thread. The comment saves with the mention, the server withholds the notification (cannot_read_thread), and the author is told nothing. The rendered mention looks like plain typed text. |
Leave such users out of the picker, or tell the author they will not be notified. Render picked mentions as chips. |
| 2 | Member, desktop, same record. The header reads Discussion (0) directly above "You don't have permission to view comments on this record." |
Omit the count when the comments cannot be read. |
| 3 | Admin, phone, Delivery Operations dashboard. The Total Budget KPI tile cuts its main number to $1,090,00.... |
Shrink the number to fit, or use compact notation ($1.09M), on a KPI tile's main value. |
| 4 | Admin, desktop, Tasks → Bulk Actions view, 2 of 10 rows selected. The header checkbox is correctly aria-checked="mixed", but it draws a grey check mark. |
Draw the mixed state as a dash. |
| 5 | Admin and member, desktop, Tasks. Reassign… in the list header is enabled with nothing selected and explains itself only after a click ("This action runs on a single record — select a row first."). The member's grid has no row checkboxes, so for the member it is a dead end. | Disable it with that reason as a tooltip until exactly one row is selected; hide it where rows cannot be selected. |
| 6 | Admin, desktop, Tasks → Grid view inline edit. Save All with an empty required Title and an Estimate of -5 is refused in a one-line banner, Save failed: Title is required and cannot be cleared; Es..., with the rest only in a native tooltip. The cells are not marked, min: 0 is not checked before sending, and the button sits flush against the right edge. |
Mark the offending cells, check min client-side, and show the full message. |
| 7 | Admin, desktop, Tasks list toolbar. A filter row with no value still counts toward the filter badge and persists across reloads. Sort offers "A → Z" for a datetime field and lets the same field be added twice. List search persists across navigation, so a user comes back to "No matching records", and that empty state has no clear button. Studio's list-view sort shows the same "A → Z" for a date. | Ignore incomplete filter rows; use "Oldest → Newest" for dates; refuse duplicate sort fields; add "Clear search" to the empty state. |
| 8 | Admin, desktop, Tasks → bulk Mark Done on 2 rows. Done and Progress 100% are set, but Status stays Backlog, so the path still shows Backlog. The result dialog says only Succeeded 2 / 2. |
Name the records in the result. The Status mismatch may be the showcase action's own definition. |
| 9 | Admin, desktop, task record page. The actions appear twice, in the header and as a button row in the body. The disabled Archive tooltip says only Not available for this record. |
Show one action row; say why Archive is unavailable. |
| 10 | Admin, desktop, delete a task. The confirm dialog does not say the delete cannot be undone. The toast is a generic Record deleted, while the create toast names the record. |
Say it is permanent (or offer Undo), and name the record in the toast. |
| 11 | Admin, desktop, a draft invoice → Submit for Sign-off. The refusal is Issued On is required, with no hint to edit the invoice first, and Issued On is not marked required in the edit form. |
Say "Fill in Issued On before submitting", and mark fields that are required for submission. |
| 12 | Admin, desktop, Approvals inbox. A row's record link opens the record inside the Account app shell, with no way back to the inbox. Reject asks for no reason. The admin may decide a request they submitted themselves. | Open the record in the inbox's own app, or keep a back link; offer an optional reason on Reject; consider showing "you submitted this" on self-submitted requests. |
| 13 | Admin, desktop, bell. The header says 4 total · 1 notification + 3 pending approvals, while the single collapsed row reads ×3 … 3 unread and shows only the first record's text. |
Make the header and the row counts agree; expand a grouped row to list its records. |
| 14 | Admin, desktop, project record page. Dates are formatted two ways on one page (Dec 28, 2026 / Sep 29). The Attachments card is wider than the others, and comment text is low-contrast grey. |
One date format per page; align the card widths; raise the comment text contrast. |
| 15 | Admin, desktop and phone, CRM Pipeline dashboard. The comparison series uses the same blue swatch as the main series. The stage chart's legend has no swatches. The month axis shows raw 2025-10. KPI money values carry no currency. The CRM widget descriptions themselves leak authoring notes ("Pie/donut/funnel ignore compareTo"); those are example content. |
A distinct swatch for the comparison series; legends with swatches; format month buckets ("Oct 2025"); format KPI money with the field's currency. |
| 16 | Admin, desktop, CRM Opportunities. Two adjacent money columns are formatted differently, 35,000.00 and 35,000. |
One currency format per column type. |
| 17 | Admin, desktop, an empty list (a new object with no records). The empty state offers no New button. | Offer Create in the empty state when the user may create. |
| 18 | Admin, phone, Tasks list and Home. The floating "+" overlaps a card's priority badge. On Home, the header links beside "Your apps" wrap awkwardly. Horizontal bar chart labels are cut to "In Progr…". | Reserve space for the FAB; let the header links scroll; wrap or abbreviate bar labels with a tooltip. |
| 19 | Admin, desktop, the runtime create form of a Studio-authored object whose validation rule refuses the save. The refusal shows twice, as a banner and an identical toast, and the violating field is not marked. | One message, and mark the field the rule names. |
| 20 | Member, desktop, a list the member cannot read. The no-access state offers Retry, which cannot help. On an invoice the member can read, the Line Items section shows a red "You do not have permission to perform this action." although the member did nothing. | Drop Retry on a permission refusal; draw an unreadable related list as a quiet "No access" line. |
| 21 | Admin, desktop, every object list toolbar (also on Studio-authored objects). An In a queue toggle has no tooltip, and its meaning is unclear to a new user. | A tooltip, or show it only on objects that use queues. |
Already carded: not raised again
| Observation at c4c515a | Card |
|---|---|
| Public form unbranded, blue Submit, tab titled "ObjectOS": still reproduces | #27355 row 9 |
| A refused public-form submit does not mark the fields: still reproduces (the stale "Required:" banner is #27496) | #27355 row 10 |
| A Backlog task's path tints "Done" as a later step: still reproduces | #26612, #26694 |
The approval request page's RECORD ID holds a name: still reproduces |
#27356 row 7 |
| Phone: Setup Users and Invitations show no Invite, Create or Import (the buttons are in the DOM at 0×0), so no user can be invited from a phone | #27058 (phone list with a FAB) |
A member at /apps/setup/system/settings sees "No settings registered …" |
#27507 (filed from this pass) |
Rows of #27355 that no longer reproduce at this sha: row 5 (the task record's ⋯ menu now has Delete), row 6 (a member's double-click in Grid opens the record instead of editing), row 11 (phone KPI tiles are two per row; row 3 above is a new truncation).
Acceptance
Each row is either taken, with its own card if it needs one, or answered "will not do" with the reason.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p3 ·
domain:ui·area:records· 2026-10-29T11:10Z⛔ Not a claim.
enhancement,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh
Split · 2026-10-29T19:36Z · ⛔ Not a claim; nothing on this card is assigned.
Row 1 (the mention picker offers a non-reader, and the author is never told the person won't be notified) is filed as comments: the @mention picker offers a member who cannot read the thread, and the author is never told that person will not be notified #27703 at p2, as triage 6495077487 directed. That makes the rows on this card pickable.
Row 13 (the bell header counts topics while a grouped row counts unread messages) is filed as bell: the popover header counts 1 notification while its one grouped row says 3 unread, so the two counts disagree #27704. It falls in seat 4's area (notifications).
Row 4 (the header checkbox draws a check mark for a partial selection) is filed as Grid header checkbox: a partial selection is announced as mixed but drawn as a check mark #27807. Added 2026-10-29T23:59Z.
Row 2 (the header shows
Discussion (0)above the refusal to read comments) is filed as Record discussion: the header shows a comment count of 0 above "You don't have permission to view comments" #27820. Added 2026-10-30T00:50Z.Row 10 (the delete confirm never says the delete is permanent, and the toast does not name the record) is filed as Record delete: the confirm dialog never says the delete is permanent, and the toast says "Record deleted" without naming the record #27823. Added 2026-10-30T00:55Z.
Row 5 (a single-record list header action is live with no row selected) is filed as List header: a single-record action (Reassign…) is live with no row selected, and a dead end where rows cannot be selected #27831. Added 2026-10-30T01:08Z.
Row 20 (a no-access list offers Retry; an unreadable related list shows a red action-refusal line) is filed as Permission refusals drawn as failures: a no-access list offers Retry, and an unreadable related list shows a red "You do not have permission to perform this action." #27841. Added 2026-10-30T01:42Z.
Every other row stays on this card for whichever
domain:uiseat's area it falls in.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN
Claim: PM loop round 3 · 2026-10-30T15:15Z
Session:
session_01L9qibzC6RxMfB59EsgAoNN(seat; implementation by card thread sessionsession_01L9qibzKuj2HUUN2wUhZ9Go,mode:thread)Account:
os-tesla(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-ty1d4m(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27520Domain:
domain:uiSeat:
domain:ui#1File surface: rows 3, 15 (chart half) and 18 (bar labels) only.
ui/packages/plugin-dashboard/src/**,ui/packages/plugin-charts/src/**,ui/packages/core/src/**(chart series and date-bucket helpers only), tests, root changeset; noexamples/**edit (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: n/a (suggestion rows, not a defect card)
Thread-read: 6506509231
Premise: holds (the rows are untaken: seat 4's split 6506509231 filed rows 1, 2, 4, 5, 10, 13 and 20 elsewhere; the money half of row 15 belongs to #27430, which is closed; the FAB/Home parts of row 18 stay with the mobile epic #27058)
Serial constraints cleared: no open PR touches
plugin-charts/plugin-dashboardbesides release PR #26517 (seat scan 15:15Z);domain:ui#4clocked out (6526005030) and was told on #26620The PR carries
Part of #27520(other rows remain open).Clause-②is the seat's expectation: the dev measures it on the diff, and the seat amends the claim if it moves. Order:/mnt/project-files/ui-seat-1/r2026-10-30/dispatch-27520.mdunderFRAME.md.objectstack-fleet commentedon Oct 30, 2026
domain:ui#1 · os-tesla · session_01L9qibzKuj2HUUN2wUhZ9Go
os-dev-report
{ "issue": 27520, "status": "done", "branch": "claude/project-thread-ty1d4m", "pr": "https://github.com/objectstack-ai/objectstack/pull/28223", "session": "session_01L9qibzKuj2HUUN2wUhZ9Go", "premise_still_valid": true, "summary": "Rows 3, 15 (chart half) and 18 (chart part) of #27520, as one PR marked Part of. Row 3: a dataset-bound KPI value takes METRIC_TILE_DATASET_VALUE_LADDER, so it shrinks before it clips, with the full figure as a tooltip past the last rung. Row 15: a YYYY-MM month bucket key renders as 'Oct 2025' through the new @object-ui/core export formatMonthBucketLabel; bar and area legends draw swatches (they had been url(#...) gradients), and a compareTo series' swatch is hollow; a per-category lone bar series draws no swatch. Row 18: horizontal-bar category labels wrap to 2 lines inside the axis; the vertical-axis phone cut moved after the rotate decision, so 'In Progress' draws whole; any cut label carries its full text as an SVG title.", "tests": "head 23380e90 under os-verify-lock: install --frozen-lockfile, plugin-dashboard... + plugin-charts... closure build, core/plugin-charts/plugin-dashboard type-check, vitest packages/core/ packages/plugin-charts/ packages/plugin-dashboard/ -> 'VERDICT command-exit 0', Test Files 523 passed (523), Tests 7418 passed | 42 skipped (7460). New: date-bucket-label.test.ts, AdvancedChartImpl.dashboardReadability-27520.test.tsx (11), metricTileDatasetValueScale-27520.test.tsx. Adapted 4 pinning tests (pointAxisEdgeLabels-13390, missingCategoryKey, labelFitBand-13241, metricTileLayout-12623). Ablations via scripts/ablation-replace.mjs (month label, dataset ladder, legend swatch colour, wrap): each turned its new test red (1 failed each), and the files were restored to the HEAD blob. Browser before/after at 390x844 and 1440x900 is in the PR body.", "gates": "local subset at 23380e90: dispatch-gates --commands gave 35 commands, each run with </dev/null and all exit 0. 'pnpm --dir ui exec vitest run scripts/' was SIGKILLed on its first run when the box was out of memory; after the dev stacks were stopped it reran alone with exit 0 (Test Files 169 passed | 2 skipped, Tests 4901 passed | 2 skipped). --ran: 35 derived, 35 run, 0 NOT-MEASURED. Also exit 0: pnpm --dir ui check:new-line-citations, node scripts/check-sdui-manifest.mjs. CI check-runs: pending at report time; the seat reads them.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "3 — mcp__github__issue_read x3 (reads); 0 writes", "api_writes": "0 — REST proxy writes; relay writes 1 (fleet-write pr_create -> #28223), plus this post-stamped comment", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: CRM Pipeline dashboard KPI tiles (dataset-bound) read '710,000' / '235,000' / '81,000' with no currency, on main 9f13ee81 and this branch · evidence: browser read of examples/app-crm Pipeline Dashboard; #27430 shipped the tile caption (PR #27972) and the formula currency face, not a dataset KPI's currency · dedupe words: dataset KPI currency, metric tile money format, row 15 money half", "class: c · reach: named producer examples/app-crm/src/dashboards/pipeline.dashboard.ts widget descriptions leak authoring notes ('Pie/donut/funnel ignore compareTo', '(compareTo)') · evidence: file content · not edited per order", "carrier: PR #28223 Acceptance notes · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN
Claim amendment · 2026-10-30T17:30Z · amends claim 6526230292 at the card thread's request (os-dev-report 6529089724). Append-only.
Clause-②: yes (widening:
@object-ui/coregains the public exportformatMonthBucketLabel; no accept set moves)The claim's
Clause-②: nowas the seat's expectation, and the dev's measurement on PR #28223 replaces it. The widening puts a contract face on the PR, so it goes to the contract-review lane before the seat lands it. The file surface is unchanged.Row 15's money half does not hold on
origin/main: the dataset-bound KPI tiles on the CRM Pipeline dashboard read710,000/235,000/81,000with no currency (#27430 shipped the tile caption and the formula currency face, not this case). The seat files it as its own card, and row 15's money half stays open on this card until that lands.objectstack-fleet commentedon Oct 30, 2026
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN
Landed · 2026-10-30T20:50Z · PR #28223 (seat ACCEPT pinned to
63b2a696, contract-review PASS 6532420116).9e4f1cfdthrough the merge queueorigin/maincontentui/packages/core/src/utils/date-bucket-label.tspresent (formatMonthBucketLabel), with the chart andmetricTilechanges beside itState: the PR is
Part of #27520, so this card stays open. Rows 3, 15 (chart half) and 18 (labels) are done. Row 15's money half (dataset KPI tiles with no currency) is #28225, serial after this PR and now unblocked. Any other rows on this card not named in the claim are still open.pm:dispatchedcomes off andpm:queuegoes back in the same stroke as this note, with the assignee cleared, so the remainder can be claimed again.6 remaining items
objectstack-fleet commentedon Oct 31, 2026
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN
Claim amendment · 2026-10-31T05:31Z · amends claim 6539394970 after the card thread's measurement on
mainb1e1dcf2(live showcase and CRM, admin), before any push. Append-only.Clause-②: yes (widening:
@object-ui/typesActionResultgains the optionalfieldErrors, carried from aVALIDATION_FAILEDanswer'sdetails.fields; no accept set moves)All three rows reproduce. The measurement moves the mechanism, so the seat accepts this surface in place of the claim's:
ui/packages/plugin-grid/src/**BulkActionDialog, notActionResultDialog. It lists the succeeded records, named as on the confirm step, only when every failure is attributed to a row. There is no server read and no executor change. Status staying Backlog is the showcase action's own declaration (MarkDoneActionpatchesdoneandprogresson purpose), so it is not edited.VALIDATION_FAILEDwithdetails.fields[{ field: issued_on, code: required, label: Issued On }], and core'sinterpretActionResponsedropsfields[]. The fix carries the entries through the core outcome andserverActionHandlerontoActionResult.fieldErrors(ui/packages/types/src/ui-action.ts). The console wrapperui/packages/app-shell/src/utils/consoleServerAction.tsand its two call sites then add "Fill in Issued On, then run “Submit for Sign-off” again." The half that marks fields required for submission needs metadata that tiesrequiredWhento an action's patch, and none exists. It is a spec card candidate, and the seat files it when this PR lands.Field.currency, while Expected Revenue is a formula with noreturnType, so by rule it draws the number face. The seat lifts the order's no-examples/**line for exactly one edit:returnType: 'currency'on that formula inexamples/app-crm/src/objects/opportunity.object.ts. No renderer change.The widening sends the PR to the contract-review lane before the seat lands it.
objectstack-fleet commentedon Oct 31, 2026
domain:ui#1 · os-tesla · session_01L9qibzF2YJvme2ABRytQ9o
os-dev-report
{ "issue": 27520, "rows": [8, 11, 16], "status": "done", "branch": "claude/project-thread-5ywnd8", "pr": "https://github.com/objectstack-ai/objectstack/pull/28378", "head": "6a3bd4e70622cc197527a2dd5959c2bbcb17c164", "session": "session_01L9qibzF2YJvme2ABRytQ9o (card thread under ui seat 1)", "premise_still_valid": true, "summary": "All three rows still reproduced on origin/main b1e1dcf2 and are fixed live. Row 8: BulkActionDialog's result step lists the records that succeeded by nameField ('Succeeded 2 / 2 | • Evidence collection | • Content backlog'), only when every failure maps to a row of the run; a bare-count bulkUpdate keeps the count alone. Row 11: interpretActionResponse reads error.details.fields into ActionResult.fieldErrors (new optional member), and the console wrapper follows the server's sentence with a pack hint ('Issued On is required. Fill in Issued On, then run “Submit for Sign-off” again.'). The hint keys off the field entries, so it survives a userMessage. Row 16: the CRM expected_revenue formula declares returnType 'currency' (the one example edit the seat allowed) and now reads $16,000.00 beside Deal Value $80,000.00.", "tests": "New: plugin-grid bulkResultSucceededNames-27520 (3), core actionRefusalFields-27520 (5), app-shell consoleServerAction.requiredFieldsHint-27520 (6). Adapted: i18n de-quote-pairing-3876 census (103 after merging main, which had also raised it). Ablations through ablation-replace, each restored: 2/3, 1/3, 8/10, 5/6 red. types+core+i18n+plugin-grid suite at 714e3766: 963 files passed, the 1 de census file fixed and rerun green; i18n on the merge head 93 files passed; app-shell wrapper and call-site slice 108 files / 761 tests passed. The whole app-shell suite sat 80 min under the lock and was stopped, so the rest of it is left to CI. type-check of the 5 ui packages: 0 errors. CRM typecheck/test (48)/validate exit 0. CRM-reading dogfood (install-local-purge-sample-data, wall-environment-metadata-refusal, guest-grants-channel): 3 files / 53 tests passed.", "gates": "At 6a3bd4e7: dispatch-gates --commands derived 54; all 54 run with </dev/null (27520-actions-gates-ran-6a3bd4e7.list in the seat's out folder). check:type-check-debt first exit 3 because the tree was not reinstalled after the merge (@objectstack/observability is new on main); after pnpm install it reran exit 0. dispatch-gates --ran: 54 derived, 54 run, 0 NOT-MEASURED. check-sdui-manifest exit 0, ui check:new-line-citations exit 0. CI on #28378: not started at report time.", "clause_2": "yes (widening: ActionResult.fieldErrors, new ActionFieldError export, ActionResponseOutcome.fieldErrors)", "metadata_protocol": "none", "line_budget": "+479 / -14, 24 files", "files_changed": [".changeset/27520-bulk-result-names-required-hint.md", "examples/app-crm/src/objects/opportunity.object.ts", "ui/packages/plugin-grid/src/components/BulkActionDialog.tsx", "ui/packages/types/src/ui-action.ts", "ui/packages/types/src/index.ts", "ui/packages/core/src/actions/actionResponse.ts", "ui/packages/core/src/actions/serverActionHandler.ts", "ui/packages/app-shell/src/utils/consoleServerAction.ts", "ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx", "ui/packages/app-shell/src/views/RecordDetailView.tsx", "ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts", "4 test files"], "deviations": [ "Surface widened past the order's file list: types, core actionResponse/serverActionHandler, app-shell consoleServerAction and its two call sites, plus the one examples/app-crm edit. The seat approved this in claim amendment 6539593257.", "Row 8's dialog is plugin-grid BulkActionDialog.tsx, not app-shell ActionResultDialog.tsx as the order assumed.", "Row 8's Status half was not edited: examples/app-showcase/src/ui/actions/index.ts MarkDoneAction leaves status alone on purpose (task_status_flow).", "Row 11's 'mark fields required for submission' half was not done, because no spec door ties a field's requiredWhen to an action's patch. The seat will file the spec card.", "Whole app-shell vitest suite not measured locally (stopped after 80 min, left to CI); the wrapper and call-site slice was run instead.", "Stray empty file /tc.pid at the container root, from a background-job variable mistake. Removing it was blocked by the safety check, so it is still there. It is outside the repo and touches nothing." ], "mcp_calls": "3, all reads: issue_read #27520 (x2), pull_request_read get #28378. 0 writes", "api_writes": "2 via relay/stamped tools: pr_create (#28378, draft), this comment on #27520; plus git pushes to claude/project-thread-5ywnd8", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed: a money formula without returnType draws a plain number by rule, and os validate does not warn about it (class c candidate, named producer: examples/app-crm before this PR)", "carrier: none · noted, not filed: the action toast reads error.message through actionErrorDetail and never error.userMessage, which a flow-action refusal can carry" ] }objectstack-fleet commentedon Oct 31, 2026
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN
Landed · 2026-10-31T08:56Z · PR #28378 (seat ACCEPT 6541242593, pinned to
6a3bd4e7, contract-review PASS 6541234847).0ac6dabcthrough the merge queue, on the first passorigin/maincontentconsoleServerAction.tscarrieswithRequiredFieldsHint,BulkActionDialog.tsxdraws the succeeded rows, andopportunity.object.tsdeclaresreturnType: 'currency'onexpected_revenueState: the PR is
Part of #27520, so this card stays open. Row 8 (succeeded names), row 11's first half (which required fields to fill) and row 16 (expected revenue as money) are done. Row 11's second half, marking fields required for submission, needs a spec door that tiesrequiredWhento an action'spatch; the seat files it as its own spec card. Rows 6 and 19 are this seat's next claim. Any other rows on this card that were not named in a claim are still open.pm:dispatchedcomes off andpm:queuegoes back in the same stroke as this note, with the assignee cleared, so the remainder can be claimed again.objectstack-fleet commentedon Oct 31, 2026
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN
Claim: PM loop round 6 · 2026-10-31T08:59Z
Session:
session_01L9qibzC6RxMfB59EsgAoNN(seat; implementation by card thread sessionsession_01L9qibxQtKRyLyL6eK14pgY,mode:thread)Account:
os-tesla(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-mcx7eo(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27520Domain:
domain:uiSeat:
domain:ui#1File surface: rows 6 and 19 only.
ui/packages/plugin-grid/src/**,ui/packages/plugin-form/src/**,ui/packages/react/src/record-save-feedback.ts,ui/packages/app-shell/src/views/RecordFormPage.tsx,ui/packages/fields/src/**(validation-rule reuse only),ui/packages/core/src/actions/actionResponse.ts(sharingrefusalFieldErrorsonly), i18n packs, tests, root changeset; nopackages/spec/**, server or record-page layout edit (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default(dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no (the seat's expectation; the dev measures it on the diff, and the seat amends the claim if a new export moves it)
Metadata-protocol: none
Responsibility: n/a (suggestion rows, not a defect card)
Thread-read: 6541407583
Premise: to be re-measured per part on
origin/mainbefore code, against #28151, #28267, #28293 and #28307, which moved these surfaces after the walkthrough. If both rows are already fixed, no PR is opened and the seat answers them on the card. Rows 3, 15 and 18 are done (PR #28223), and rows 8, 11 (first half) and 16 are done (PR #28378). Row 11's second half is filed as #28382. Rows 1, 2, 4, 5, 10, 13 and 20 have their own cards.Serial constraints cleared: yes. No open PR names #27520, since PR #28378 merged at
0ac6dabc. The maintainer lifted the p3 hold for ui with 「可以开p3」, and at 2026-10-31T02:39Z said 「ui 车道一堆任务啊,应该并发开满啊」 in the seat thread. No other seat has claimed these rows.The PR carries
Part of #27520, because other rows remain open. Order:/mnt/project-files/ui-seat-1/r2026-10-30/dispatch-27520-saves.mdunderFRAME.md.objectstack-fleet commentedon Oct 31, 2026
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN
Claim amendment · 2026-10-31T09:25Z · amends claim 6541435370 after the card thread's measurement on
main3ba52069(live showcase--fresh, admin; record at/mnt/project-files/ui-seat-1/r2026-10-30/out/27520-saves-measure.md), before any push. Append-only.Clause-②: no (unchanged; no new export, no accept set moves)
titleandestimate_hours, the PATCH sent with -5, and Save All 0 px from the edge. The save bar, banner and per-row error state live inui/packages/components/src/renderers/complex/data-table.tsx, so the seat adds that file to the surface. Clientmin/max/length/required go throughbuildValidationRules. A row refused locally takes the sameVALIDATION_FAILEDshape, and the other rows still save.cross_field) marks it, with one message and no banner or toast, throughextractFieldErrors. Ascriptrule answers only_record, so there is nothing to mark. That is the stop condition: no spec or server edit, and it is recorded, not filed. The duplicate still reproduces for a_recordrefusal: the banner and an identical toast. That pair is raised byui/packages/components/src/renderers/form/form.tsx, which the seat adds to the surface.bulkCreateisPromise.all(map(create)), so a refused row leaves every earlier row of the batch landed —updateManyon the same driver refuses before mutating anything #13340 test is adapted to the new rule: one banner, scrolled into view, and zero error toasts on a refusal the banner draws. Its no-stacking guarantee on a retry still holds. Where the form draws no banner, the toast stays.@object-ui/reactextractFieldErrors/unmarkedRefusalLines, whichform.tsxalready uses.ui/packages/core/src/actions/actionResponse.tsleaves the surface.This reverses a pinned visible behaviour (the refusal toast), so the PR goes to the contract-review lane before the seat lands it.
objectstack-fleet commentedon Oct 31, 2026
domain:ui#1 · os-tesla · session_01L9qibxQtKRyLyL6eK14pgY
os-dev-report
{ "issue": 27520, "rows": [ 6, 19 ], "status": "done", "branch": "claude/project-thread-mcx7eo", "pr": "https://github.com/objectstack-ai/objectstack/pull/28396", "head": "f77d7968eecba8ddfaa32973b8d239a144c785f4", "session": "session_01L9qibxQtKRyLyL6eK14pgY (card thread under ui seat 1)", "premise_still_valid": true, "summary": "Measured on origin/main 3ba52069: row 6 reproduced in all four parts; row 19's duplicate banner+toast reproduced for a script rule, and its field mark already worked for a cross_field rule. Row 6, fixed live: data-table says every refusal reason whole, one line each, in its own role=alert row under the toolbar; it marks the cells the refusal names (aria-invalid, data-cell-error, title, ring), and editing a cell clears its mark. ObjectGrid's built-in saves check the field's own rules (required, min/max, length, email/URL pattern via buildValidationRules) before sending; a breaking row is refused unsent in the server's VALIDATION_FAILED shape, and the other rows still save. The toolbar is inset (Save All 12 px from the edge). Row 19, fixed live: form.tsx raises one message for a refusal, the banner scrolled into view, and no error toast (page and dialog).", "tests": "New: components data-table-refusal-cells-27520 (4), plugin-grid inlineEditRuleCheck-27520 (7). Adapted, not deleted: form-one-toast-per-refusal-13340 (one banner, scrolled into view, zero error toasts, retry keeps one banner) and 11 other refusal pins in components and plugin-form that asserted the toast. components+plugin-grid+plugin-form at f77d7968: 944 files passed, 1 skipped; 9131 tests passed (lock VERDICT command-exit 0). The first full run at 9edf9af1 had 25 reds in 7 plugin-grid inline-edit files (pre-send check read rules of undefined); fixed in f77d7968. type-check of components and plugin-grid exit 0. The app-shell and console suites were not run locally (left to CI).", "gates": "At f77d7968: dispatch-gates --commands derived 35; all 35 run with </dev/null (27520-saves-gates-ran-f77d7968.list in the seat's out folder). check:type-check-debt first exited 3 (it queued behind my own test run on the lock); rerun under the lock exit 0. dispatch-gates --ran: 35 derived, 35 run, 0 NOT-MEASURED. check-sdui-manifest exit 0, ui check:new-line-citations exit 0. CI on #28396: not started at report time.", "clause_2": "no", "metadata_protocol": "none", "dogfood_battery": "not run: no server, spec or example file changed", "line_budget": "+813 / -140, 20 files", "files_changed": [ ".changeset/27520-refused-grid-and-form-saves.md", "ui/packages/components/src/__tests__/data-table-refusal-cells-27520.test.tsx", "ui/packages/components/src/renderers/complex/data-table.tsx", "ui/packages/components/src/renderers/form/__tests__/fieldtab-visiblewhen-6237.test.tsx", "ui/packages/components/src/renderers/form/__tests__/form-invalid-toast-joiner.test.tsx", "ui/packages/components/src/renderers/form/__tests__/form-one-toast-per-refusal-13340.test.tsx", "ui/packages/components/src/renderers/form/__tests__/form-refusal-lines-28053.test.tsx", "ui/packages/components/src/renderers/form/__tests__/form-server-field-errors.test.tsx", "ui/packages/components/src/renderers/form/__tests__/visibleWhen-fault-refuses-submit-8069.test.tsx", "ui/packages/components/src/renderers/form/form.tsx", "ui/packages/plugin-form/src/MasterDetailForm.outcomeToastSupersede.test.tsx", "ui/packages/plugin-form/src/MasterDetailForm.singleRefusalRaiser.test.tsx", "ui/packages/plugin-form/src/MasterDetailForm.test.tsx", "ui/packages/plugin-form/src/WizardForm.outcomeToastSupersede.test.tsx", "ui/packages/plugin-form/src/submitHandlerSeam.test.tsx", "ui/packages/plugin-form/src/submitTargetRefusal.test.tsx", "ui/packages/plugin-grid/src/ObjectGrid.tsx", "ui/packages/plugin-grid/src/__tests__/inlineEditRuleCheck-27520.test.tsx", "ui/packages/plugin-grid/src/objectGridInlineRules.ts", "ui/packages/plugin-grid/src/objectGridTranslations.ts" ], "deviations": [ "Surface widened by the seat (amendment 6541672013): data-table.tsx and form.tsx added, actionResponse.ts dropped. The mechanism differs from the order: the grid save bar and banner live in components data-table.tsx, and the banner+toast pair lives in form.tsx. extractFieldErrors / unmarkedRefusalLines from @object-ui/react are reused instead of refusalFieldErrors.", "Row 19 reverses the visible behaviour objectui#13340 pinned (banner plus one toast). The seat accepted this on its two conditions, both met. Contract review is owed.", "The seat's 'keep the toast where the form draws no banner' case does not arise: every refusal path of form.tsx draws the banner.", "Row 19's field mark for a script rule cannot be drawn: a script rule declares no fields, so the answer names only _record. Recorded here, not filed.", "The whole app-shell and console vitest suites were not run locally (left to CI); a search of both found no pin on the old toast or save-bar text." ], "mcp_calls": "reads only: issue_read #27520, pull_request_read get #28396. 0 writes", "api_writes": "2 via relay/stamped tools: pr_create (#28396, draft), this comment on #27520; plus git pushes to claude/project-thread-mcx7eo", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed: a script validation rule's refusal names only _record, so no form can mark a field for it; a spec 'fields' hint on script rules would be needed" ] }