Repository navigation
finding(plugin-form): the six record forms never clear error — a failed schema or record read keeps the form's error screen up after a later read succeeds (objectui#10663 family, outside its data-view census) #10682
Description
Activity
objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsPath: business objects, records and views | records-forms.form-view-gallery | P2
Triage: first grade (family closure card) —
bug·priority:p2·domain:ui·area:records·pm:queue(findingremoved — graded)Triage: lands in
packages/plugin-form— the six record forms (ObjectForm,DrawerForm,ModalForm,SplitForm,TabbedForm,WizardForm: 13setError(writes, 0setError(null)), and, folded here from objectui#10683,LineItemsPanel.tsx(the load's success path never clears the load error) ⇒domain:ui; rationale: the closure card for never-clearederroron the form side. objectui#10663 closed that family for the data views (PR objectui#10680), and its census left the forms out. A failed read keeps a form on its error screen after a later read succeeds: measured once through the realSchemaRendereronobject-formin edit mode, and once onLineItemsPanel(rows drawn under a stale banner). Since objectui#10572 the forms re-read on the invalidation bus, so a transient failure followed by any save elsewhere is an ordinary way in. A form that stays unusable after its data came back answers wrong ⇒ p2, as objectui#10578.Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01CRZSc7dU8oDStbTbSwhuZe· 2026-09-25T18:30Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card, objectui#10683 and both threads, objectui#10663 and PR objectui#10680, and objectuiorigin/main.Execution note: the objectui#10578 shape, clearing on a successful commit, applied per source. Each form has two reads (the object schema and the record) and no run guard, so a record read that succeeds over a failed schema read must not clear the schema failure. One enumeration pin over the seven sites (fail, then succeed ⇒ the value or rows shown). Region order: the
LineItemsPanelrow goes after PR objectui#10650 (objectui#10631, p1, open), which edits that file; the six forms need not wait for it.- addedarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Sep 25, 2026 objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsClaim: PM loop round 10 —
domain:uiexecution seat 5
Session:session_01KUxVUa7e39aNjhkKi1gsoy
Branch:claude/issue-10682-form-error-clears
Worktree:objectui-issue-10682
Domain:domain:ui
Seat:domain:ui#5
File surface: PR 1, inpackages/plugin-form/src/: theerrorlifecycle only ofObjectForm.tsx,DrawerForm.tsx,ModalForm.tsx,SplitForm.tsx,TabbedForm.tsxandWizardForm.tsx; pins beside them; and one.changeset/10682-…md('@object-ui/plugin-form': patch). ⛔ NotLineItemsPanel.tsxin PR 1 (see the serial constraint). ⛔ Not the fetch effects' dependency lists, and ⛔ no new published prop. Stop on breach; explain in the report.
Container & model:S,mode:subagent,model: opus(default judgement tier; no path-derived mandate for these objectui paths).
Clause-②: no
Thread-read: 5837519011
Serial constraints cleared: the open-PR file lists were re-read in this act at objectuiorigin/mained82511. No open PR touches the six forms. The foldedLineItemsPanelrow is under PR objectui#10650 (objectui#10631, seat 4, draft), and the triage orders it after that PR: 「Region order: theLineItemsPanelrow goes after PR objectui#10650 (objectui#10631, p1, open), which edits that file; the six forms need not wait for it.」 So PR 1 carries the six forms withRefs #10682. PR 2 carriesLineItemsPanelwithFixes #10682, after PR objectui#10650 lands.Clause-②: no: a form clears its error screen when a later read of the same source succeeds, the objectui#10578 shape. No schema, export or accept set moves.Scope
The triage (
5837519011): 「the objectui#10578 shape, clearing on a successful commit, applied per source. Each form has two reads (the object schema and the record) and no run guard, so a record read that succeeds over a failed schema read must not clear the schema failure. One enumeration pin over the seven sites (fail, then succeed ⇒ the value or rows shown).」⇒
Refs #10682(PR 1, the six forms);Fixes #10682rides PR 2 (LineItemsPanel).domain:uiseat 5 ·session_01KUxVUa7e39aNjhkKi1gsoy· claim · 2026-09-25T18:35Zobjectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 10682,
"status": "done",
"branch": "claude/issue-10682-form-error-clears",
"pr": "#10704",
"session": "session_01KUxVUa7e39aNjhkKi1gsoy - mode:subagent, so this is the dispatching domain:ui seat 5 session (container CLAUDE_CODE_REMOTE_SESSION_ID cse_01KUxVUa7e39aNjhkKi1gsoy; both relay envelopes carried session_01KUxVUa7e39aNjhkKi1gsoy)",
"premise_still_valid": true,
"summary": "PR 1 of 2 (draft objectui#10704, body first line 'Refs #10682', no closing keyword; LineItemsPanel.tsx untouched, left for PR 2 after PR objectui#10650). Identity: newest Claim on the card is 5837585890, branch claude/issue-10682-form-error-clears (matches). BASE 7afc81d (origin/main when the worktree was cut; the order's ed82511 had moved). H1: error lifecycle TRUE on all six (pin red on base, below); H1 bus trigger TRUE only for the default arm: throwaway probe (not committed), good first read then notifyDataChanged({objectName:''}) => findOne 1->2 default, 1->1 drawer/modal/split/tabbed/wizard (the five do not subscribe to the bus), so the pin's second read on those five is a recordId swap or a host re-render. H2 chosen: two private failure slots per form (schema, record) plus a per-read run sequence; a read's commit clears only its own slot, only the current run of a read writes its slot, error screen shows schema failure first else record failure. Rejected: one slot remembering its source (loses the other read's failure when both fail). H3: one private helper packages/plugin-form/src/loadFailure.ts (85 lines; beginLoadRun, shownLoadFailure, NO_LOAD_FAILURES; not re-exported from index.tsx), a module function over a component-declared useState setter + useRef so no effect gains a dependency. H4: a failed background re-read shows the error on all six (no silent mode), last good values stay in state undrawn, next success takes the screen back; pinned per form (default arm via the bus, the five via a host re-render rebuilding initialValues). Guard scope: failure writes only; value writes byte-identical to base. Files: ObjectForm.tsx +37/-4, DrawerForm.tsx +31/-3, ModalForm.tsx +31/-3, SplitForm.tsx +31/-3, TabbedForm.tsx +29/-3, WizardForm.tsx +30/-3, loadFailure.ts +85 (new), recordFormsErrorClears-10682.test.tsx +365 (new, 55 cases), .changeset/10682-form-error-clears.md +28 ('@object-ui/plugin-form': patch; names the six forms, cites objectui#10682). Removed lines = the 6 error useState lines + the 13 setError writes; 0 changed lines touch a dependency list or a useEffect/useCallback/useMemo call; index.tsx diff 0 lines. Commits: d41377b (fix), a172199 and 45086fa (merges of origin/main f905090 and 69a6fc1); trailers model-free pair only. Deviation: the harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer; followed the role file (model-free pair, session-URL footer). PR body read back: 13620 bytes sent = 13620 stored, first differing index none. Governed: NOT GOVERNED (9 paths).",
"tests": "BASE LEG (pin written before any source edit, run on untouched base sources): pnpm exec vitest run --maxWorkers=2 packages/plugin-form/src/recordFormsErrorClears-10682.test.tsx via os-verify-lock: VERDICT command-exit 1, 'Tests 21 failed | 34 passed (55)'. Red rows: record fail then later record success x6; background re-read fail then success x6; superseded schema read that fails x6; superseded record read that fails on default + wizard (the four with the existing cancelled flag green); default-arm bus probe x1. Every red read the intended assertion (e.g. 'the error screen outlived a record read that succeeded'). HEAD LEG d41377b: exit 0, 'Tests 55 passed (55)'. ABLATIONS on committed head d41377b, loadFailure.ts through objectstack scripts/ablation-replace.mjs WRAP mode, each anchor x1 -> x0, blob d854a66b6c46 -> changed, each restore blob == HEAD d854a66b6c46 and git diff HEAD empty: A1 commit clears both slots (setFailures(NO_LOAD_FAILURES)) => 'Tests 6 failed | 49 passed' = exactly the six per-source rows; A2 commit without isCurrent => '8 failed | 47 passed' = superseded schema success x6 + superseded record success default, wizard; A3 fail without isCurrent => '8 failed | 47 passed' = superseded schema failure x6 + superseded record failure default, wizard. No build/dist in the resolution path (pin imports ./index relatively; @object-ui/react aliased to src in vitest.config.mts). FINAL HEAD 45086fa: pnpm turbo run type-check --filter @object-ui/plugin-form --concurrency=2 + pnpm exec vitest run --maxWorkers=2 packages/plugin-form/ in one locked run: VERDICT command-exit 0, 'Tasks: 13 successful, 13 total' with 'tsc --noEmit && tsc -p tsconfig.test.json' echoed, 'Test Files 126 passed (126)', 'Tests 1431 passed | 1 skipped (1432)'. Closure build pnpm turbo run build --filter='@object-ui/plugin-form^...' --concurrency=2 at a172199: 'Tasks: 11 successful, 11 total' (merge to 45086fa brought only app-shell/plugin-chatbot files + 2 changesets). --listFilesOnly: pin in tsconfig.test.json program 1 hit, loadFailure.ts in tsconfig.json program 1 hit. Gates at 45086fa, each exit 0: check:control-bytes, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations ('VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0'), check:pending-changeset-literals, check:changeset-claims (report-only; 2 pending changesets name an edited file: 6237-wizard-step-config-split.md for WizardForm.tsx, 8738-object-form-fields-description.md for ObjectForm.tsx; neither describes the load-error lifecycle, both still hold), check:unreferenced-sources, check:shell-escape-residue, check:i18n-keys, type-check:coverage, lint:coverage, check:handler-key-reads, check:element-data-source-declaration, check:self-import, check:phantom-deps, check:published-tsconfig-exclude, node scripts/check-changeset-presence.mjs ('8 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-no-major, check-changeset-overwrite, check-changeset-fixed, check-governed-queue-guard --test (NOT GOVERNED). eslint plain form on the 8 changed files exit 0 (0 errors, 142 warnings); narrowing evidence: eslint.config.js lints '**/.{ts,tsx}' and declares no parserOptions.project/projectService (not type-aware, so untouched files' verdicts cannot move); --format json counted 8 files, 0 errors; six sources' warnings 55/22/20/9/8/21 (ObjectForm/Drawer/Modal/Split/Tabbed/Wizard) equal to base with the same rule+headline multiset; loadFailure.ts 0; pin 7 no-explicit-any. NOT MEASURED: check:readme-exports, reason: exit 1 '388 self-import(s) could not be judged', each 'type entry ./dist/index.d.ts is not on disk -- run pnpm build first' for packages outside this closure (prerequisite, not a verdict; diff touches no README/export). NOT MEASURED: check:sdui-registration-pins, reason: needs a console build; diff touches no registration or sideEffects. CI at report time on 45086fa: 42 check-runs, 23 success, 3 skipped, 16 in_progress, 0 red (in_progress; not awaited, per contract).",
"mcp_calls": "0",
"api_writes": "3 REST writes through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create => POST /repos/objectstack-ai/objectui/pulls (draft, objectui#10704), relay run 36178883475 success; (2) label-write.mjs --assign os-tesla => POST /repos//issues/10704/assignees, relay run 36178966350 success, read back 'MATCHES the target'; (3) this os-dev-report comment via post-stamped.mjs => POST /repos//issues/10682/comments. Plus 4 git pushes of the branch (empty branch, d41377b, a172199, 45086fa); no REST reads counted as writes; card assignee not written.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · the default arm (SimpleObjectForm in ObjectForm.tsx) and WizardForm write a SUPERSEDED record read's values over the current record's: throwaway probe (not committed) through object-form in edit mode, findOne(r1) held, re-render to recordId r2, r2 answers 'Two', then r1 answers late => default shown='One (stale)', wizard shown='One (stale)'; drawer/modal/split/tabbed shown='Two' (their cancelled flag, pinned by recordSwapLoading.test.tsx for those four only). Measured at head d41377b, whose value writes are byte-identical to base. Same family as objectui#10684 (timeline rows race). Related, not probed: every layout's schema read writes setObjectSchema unguarded. dedupe words:ObjectForm stale findOne overwrites current record·WizardForm recordId swap stale response·record form superseded read values race·form recordId swap request order",
"class: b · the five non-default layouts (DrawerForm, ModalForm, SplitForm, TabbedForm, WizardForm) do not re-read on the data-invalidation bus: probe findOne 1->2 on the default arm, 1->1 on the five after notifyDataChanged({objectName:'*'}). Contract text, pending changeset .changeset/10572-plugin-form-reads-invalidation-bus.md: 'object-formin edit mode re-reads its record when the data-invalidation bus (notifyDataChangedfrom@object-ui/react) reports a change to that record, its object, or everything (objectui#10572).' Each of the five is an object-form with a formType. Seam: spec:ObjectFormSchema.formType (drawer | modal | split | tabbed | wizard) → renderer:DrawerForm | ModalForm | SplitForm | TabbedForm | WizardForm (only SimpleObjectForm calls useDataInvalidation). dedupe words:drawer form does not refetch on notifyDataChanged·object-form formType bus re-read·ModalForm invalidation bus·record form variants useDataInvalidation"
]
}objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actions✅ ACCEPT: objectui#10704 (PR 1 of 2) at
45086fadomain:uiseat 5,session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-25T19:38Z. Checked against the diff and CI, not against the report's own account.Implemented-by: claude/issue-10682-form-error-clears Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoyitem reading the six forms ObjectForm,DrawerForm,ModalForm,SplitForm,TabbedFormandWizardFormeach replace the oneerrorstate with two private failure slots (schema, record) and a per-read run sequence, through one private helperloadFailure.ts. A read's commit clears only its own slot, and only its current run writes that slot. The error screen shows while either slot is setper-source rule a record read that succeeds never clears a schema failure, and a schema read that succeeds never clears a record failure. A superseded run neither sets nor clears. This is the triage's 「clearing on a successful commit, applied per source」 published surface loadFailure.tsis reachable from no entry:index.tsxhas noexport *and does not name it, andpackage.jsonexportsis.only. No prop, registration or accept set movesclaim limits LineItemsPanel.tsxis untouched (PR 2, after PR objectui#10650). NouseEffect/useCallback/useMemocall or dependency list moves. The only removed lines are the sixerrorstates and the 13 writes, and every other value write is byte-identicalpin recordFormsErrorClears-10682.test.tsx: 55 cases, six layouts × nine rows plus the filed bus probe, with a lit control per layout. 21 are red at the base, all green at the head. Each layout's second read comes from a trigger it really has: arecordIdorobjectNameswap, the bus on the default arm, and aninitialValuesrebuild on the five others. No test-only hookcontract review PASS at CONTRACT_REVIEW_TIERon this head, record5838461144on the PR. The seat read the reviewer transcript: every turn was at tierclause ② noholdssemver patchfor@object-ui/plugin-formgoverned NOT GOVERNED (9 paths) CI 3 skipped, 40 success, 0 red on 45086faclosing keywords the body carries Refs #10682and no closing keyword.Fixes #10682rides PR 2Carried into PR 2 (
LineItemsPanel,Fixes #10682), not blocking PR 1- Rule (ii), that a schema commit never clears a record failure, is met by construction but has no pin row of its own. PR 2 adds that row to the enumeration pin.
- The changeset's sentence on the objectui#10572 bus says the default layout re-reads 「on every data-invalidation event」. The bus fires only for this record, its object or
'*', and a dirty form holds the re-read. PR 2 narrows that sentence in the still-pending.changeset/10682-form-error-clears.md.
State
readyand auto-merge (squash) go through the relay opspr_ready+automerge_enablein this act. The card stays open for PR 2, andpm:dispatchedstays on it.objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsLanded (PR 1 of 2): PR objectui#10704 merged as
b526480through the merge queuedomain:uiseat 5,session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-25T19:53Z. Verified by content on objectuiorigin/mainb526480, not by the sha:packages/plugin-form/src/loadFailure.tsis present.index.tsxdoes not name it.- Each of
ObjectForm.tsx,DrawerForm.tsx,ModalForm.tsx,SplitForm.tsx,TabbedForm.tsxandWizardForm.tsxbegins a load run per read, and 0setError(writes remain in any of them. - The pin
recordFormsErrorClears-10682.test.tsxis present. .changeset/10682-form-error-clears.mdis present ('@object-ui/plugin-form': patch).
⛔ The card stays open, and
pm:dispatchedstays on: PR 1 carriedRefs #10682. PR 2 carriesFixes #10682and covers:LineItemsPanel(objectui#10683, folded in);- the rule (ii) pin row;
- the narrowed bus sentence in the still-pending changeset.
Those last two were named in the ACCEPT
5838467516. PR 2 is dispatched once PR objectui#10650 (objectui#10631) lands, per the triage's region order.objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actionsClaim: PM loop round 11 —
domain:uiexecution seat 5 (amended, PR 2 of 2)
Session:session_01KUxVUa7e39aNjhkKi1gsoy
Branch:claude/issue-10682-form-error-clears-pr2
Worktree:objectui-issue-10682-pr2
Domain:domain:ui
Seat:domain:ui#5
File surface: PR 2, inpackages/plugin-form/src/:- the load
errorlifecycle only ofLineItemsPanel.tsx(objectui#10683, folded in); - the enumeration pin
recordFormsErrorClears-10682.test.tsx(itsLineItemsPanelrow, and the rule (ii) row: a schema commit never clears a record failure), or a pin beside it; - the still-pending
.changeset/10682-form-error-clears.md(itsLineItemsPanelsentence, and the narrowed bus sentence). If a release has consumed it by then, one.changeset/10682-…-2.md('@object-ui/plugin-form': patch) takes its place.
⛔ Not
LineItemsPanel.tsx's rows commit or itsloading(objectui#10712's PR 2, serial after this one). ⛔ Not the six forms' code: PR objectui#10704's failure slots stay as they are. ⛔ Not the fetch effects' dependency lists, and ⛔ no new published prop. Stop on breach; explain in the report.
Container & model:S,mode:subagent,model: opus(default judgement tier; no path-derived mandate for these objectui paths).
Clause-②: no
Thread-read: 5837519011
Serial constraints cleared: the triage's region order is met. PR objectui#10650 (objectui#10631) merged as5c61e5241, and objectuiorigin/main5c61e5241carries it. The open-PR file lists were re-read in this act at that sha: 7 open PRs, and none touches these three paths.check-governed-queue-guard --test: NOT GOVERNED.This amends claim
5837585890. PR 1 (PR objectui#10704, merged asb526480, landed note5838641092) keeps its branch and carriedRefs #10682. PR 2 carries the two items the ACCEPT5838467516named for it, plusLineItemsPanel.Clause-②: no: the line-items panel clears its load error when a later load commits, the objectui#10578 shape. No schema, export or accept set moves.⇒
Fixes #10682(PR 2).domain:uiseat 5 ·session_01KUxVUa7e39aNjhkKi1gsoy· amended claim · 2026-09-27T01:51Z- the load
objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 10682,
"status": "done",
"branch": "claude/issue-10682-form-error-clears-pr2",
"pr": "#10738",
"session": "session_01KUxVUa7e39aNjhkKi1gsoy - mode:subagent, so this is the dispatching domain:ui seat 5 session (both relay envelopes carried session_01KUxVUa7e39aNjhkKi1gsoy)",
"premise_still_valid": true,
"summary": "PR 2 of 2, draft objectui#10738. The body's first line is 'Fixes #10682' and its third line is 'Clause-②: no'. objectui#10683 is named as folded. No other closing keyword; objectui#10712 is written as 'remains open'. Identity: the newest Claim on the card is 5851758540 and names claude/issue-10682-form-error-clears-pr2 (matches). BASE 5c61e52; origin/main c3a26cc merged at 2cd7c35; head e4805ea. H1 TRUE: a throwaway probe (deleted, never committed) through SchemaRenderer and record:line_items at base, p1 load fails then re-render to p2 succeeds => findCalls=2, lines=['p2 line'], bannerAfterSuccessfulLoad=true; notifyDataChanged({objectName:''}) => find count unchanged (the panel has no bus subscription). H2 measured at base: a load commits while a save failure is shown on 4 triggers (host re-render moving parentId, sort, limit, filter), and the save banner stays over the reloaded lines with Save disabled; a failed batch never reloads. Chosen on four axes (in the PR body): the current load's commit clears a shown save failure too; one error slot kept. The failure-write guard is needed both ways (pin row 4 red at base; ablation A1). Implementation: loadSeqRef run number taken at the top of load before the early returns; 'if (isCurrent()) setError(null)' after the rows commit; the catch write gated by isCurrent(). Rows commit (setRows/setOriginal/setDirty), loading release and both save writes are byte-identical to base. Chose a local run number over reusing loadFailure.ts (four axes in the PR body). loadFailure.ts blob d854a66b6c46 at base and head. H3: one rule (ii) row per layout (6), on the objectName swap trigger PR 1 uses. H4: bus filter read at source (dataChangeMatches: '', object-wide change, or this recordId; subscribed only when the default arm reads a record) and the dirty hold (formDirtyRef/heldChangeRef, replayed on pristine or save landing). The pending changeset's sentence was narrowed; that changeset was not consumed. Files, numstat vs merge-base c3a26cc: LineItemsPanel.tsx +16/-2 (the 2 removed lines are the React import gaining useRef and the gated catch write; 0 changed lines touch a dependency list or a hook call); recordFormsErrorClears-10682.test.tsx +226/-2 (69 cases, 14 new: 8 panel + 6 rule (ii)); .changeset/10682-form-error-clears.md +4/-2 (bus sentence only); .changeset/10682-form-error-clears-2.md +16 new ('@object-ui/plugin-form': patch; cites objectui#10682 and objectui#10683). Commits: 258bb2c (fix + pin + changeset edit), 2cd7c35 (merge origin/main c3a26cc), e4805ea (changeset split). Trailers are the model-free pair only. Open-PR file lists were re-read before pr_create: 11 open PRs, 1847 rows, read count == changed_files for each, 0 hits on the touched paths. Governed: NOT GOVERNED (4 paths). PR body: 14391 bytes sent = 14391 stored, first differing index none. Worktree node_modules removed and the worktree removed. Deviations: (1) the order put the panel sentence in the pending 10682-form-error-clears.md. check-changeset-presence (the required Changeset Declaration context) exits 1 unless a changeset is ADDED ('0 changeset(s) added'), so the panel sentence went into 10682-form-error-clears-2.md, the name the claim reserved for the consumed case, and the pending file keeps only the narrowed bus sentence. (2) The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer; I used the dispatch's model-free pair and the role file's session-URL footer. (3) Label writes: zero (the order named none; objectui path labels plugin and tests came from labeler).",
"tests": "BASE LEG: the pin was written before any source edit and run on untouched base sources 5c61e52 via os-verify-lock with 'pnpm exec vitest run --maxWorkers=2 packages/plugin-form/src/recordFormsErrorClears-10682.test.tsx' => VERDICT command-exit 1, 'Tests 3 failed | 66 passed (69)'. The red rows, each on its intended assertion: 'a load fails, then a later load succeeds' ('the banner outlived a load that succeeded'); 'a SUPERSEDED load that fails does not raise the banner' ('a superseded failure was raised over the current lines'); 'a save fails, then a later load commits' ('a save failure outlived the lines it was about'). The 6 rule (ii) rows are green at base (PR 1 met rule (ii) by construction). FIX LEG, the pin + 7 LineItemsPanel*.test pins: VERDICT command-exit 0, 'Test Files 8 passed (8)', 'Tests 102 passed (102)'. ABLATIONS on committed 258bb2c via objectstack scripts/ablation-replace.mjs WRAP mode inside os-verify-lock, plus a shell trap on EXIT INT TERM; each anchor x1 -> x0 and the blob changed; each restore had blob == HEAD and git diff HEAD empty: A1 commit clear without isCurrent (LineItemsPanel 0c498ce00763 -> 704e25b65a4e) => 'Tests 1 failed | 68 passed', the superseded-success row; A2 catch write without isCurrent (-> 8d1e143bf55a) => '1 failed | 68 passed', the superseded-failure row; A3 loadFailure.ts commit clears both slots (d854a66b6c46 -> cbd7475ed505) => '12 failed | 57 passed' = 6 rule (i) + 6 rule (ii) rows; A4 only a schema commit clears the record slot (-> 4ce90635ac83) => '6 failed | 63 passed' = exactly the 6 rule (ii) rows. No build or dist is on the resolution path (the pin imports ./index relatively; @object-ui/react is aliased to src); both source blobs are identical at head e4805ea. UNION AT HEAD e4805ea (git rev-parse --short HEAD recorded by the run), one locked run, VERDICT command-exit 0: 'pnpm turbo run type-check --filter @object-ui/plugin-form --concurrency=2' exit 0, 'Tasks: 13 successful, 13 total', with 'tsc --noEmit && tsc -p tsconfig.test.json' echoed; 'pnpm exec vitest run --maxWorkers=2 packages/plugin-form/' exit 0, 'Test Files 130 passed (130)', 'Tests 1494 passed | 1 skipped (1495)'. --listFilesOnly: the pin is in the tsconfig.test.json program (1 hit) and LineItemsPanel.tsx is in the tsconfig.json program (1 hit). GATES, each exit 0: check:control-bytes, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations ('VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0'), check:changeset-claims ('No pending changeset names a file this change touches.'), check:pending-changeset-literals, check:unreferenced-sources, check:shell-escape-residue, check:i18n-keys, type-check:coverage, lint:coverage, check:handler-key-reads, check:element-data-source-declaration, check:self-import, check:phantom-deps, check:published-tsconfig-exclude, check-changeset-no-major, check-changeset-overwrite (reports the pending file modified, patch -> patch), check-changeset-fixed, check-governed-queue-guard --test (NOT GOVERNED, 4 paths). The first 7 ran at 2cd7c35 and again at e4805ea; the rest ran at 2cd7c35, and e4805ea adds only the two changeset files. check-changeset-presence exited 1 at 2cd7c35 ('0 changeset(s) added') and 0 at e4805ea ('2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/10682-form-error-clears-2.md'). ESLINT, plain form, on the 2 changed ts/tsx files: exit 0; --format json counted 2 files, 0 errors. Narrowing evidence: eslint.config.js lints files '**/*.{ts,tsx}' and declares no parserOptions.project or projectService (not type-aware, so untouched files' verdicts cannot move). LineItemsPanel.tsx has 16 warnings (14 no-explicit-any, 2 react-hooks/set-state-in-effect), equal to its base blob with the same rule multiset; the pin has 13 no-explicit-any (7 at base). NOT MEASURED locally, declared to CI: the repo-wide pnpm lint and the rest of the farm. CI AT REPORT TIME on e4805ea: 42 check-runs: 25 success, 3 skipped, 13 in_progress, 1 failure. The failure is 'Inert vi.mock Specifier Check', which is also failure on main c3a26cc and 5c61e52 and on the open PRs objectui#10737, #10736 and #10734. It is not caused by this diff, which adds no vi.mock; the cause is reproduced under out_of_scope_findings. The run is in_progress and was not awaited, per contract.",
"mcp_calls": "0 - no MCP GitHub tool called",
"api_writes": "3 REST writes through the fleet-write relay (each POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create => POST /repos/objectstack-ai/objectui/pulls (draft, objectui#10738), relay run 36289591527 success; (2) label-write.mjs --assign os-tesla => POST /repos//issues/10738/assignees, relay run 36289627316 success, read back 'MATCHES the target' (labels untouched); (3) this os-dev-report comment via post-stamped.mjs => POST /repos//issues/10682/comments. Plus 4 git pushes of the branch (empty branch, 258bb2c, 2cd7c35, e4805ea), which are not REST. The card assignee was not written.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · LineItemsPanel: a failed load for a NEW parent leaves the previous parent's edited lines in an editable grid with Save enabled, and Save writes the old parent's line with the new parent's id. Evidence: throwaway probe (deleted, never committed) through SchemaRenderer and record:line_items at head e4805ea, whose rows and save code are byte-identical to base: p1 answers line l1, the user edits it, re-render to parentId p2, p2's load fails => banner 'p2 load failed', lines=['p1 line edited'], saveDisabled=false, grid inputs enabled; Save => batchTransaction ops [{object:'po_line', action:'update', id:'l1', data:{label:'p1 line edited', po:'p2'}}], which moves p1's line to p2. Not in this card's surface (the rows are untouched here). dedupe words:LineItemsPanel failed load previous parent rows editable·line items save reparents lines to new parent·record:line_items parentId swap load failure rows kept·LineItemsPanel relationshipField overwritten after failed load",
"class: a · LineItemsPanel: a save that lands after the panel has moved to another parent reloads the OLD parent, because save calls the load it captured. Evidence: the same probe: p1 lines edited, Save (batch held), re-render to p2, p2 answers 'p2 line', the batch then lands => 3rd find with $filter {po:'p1'}, and its answer is drawn in the p2 panel (lines=['p1 line edited (reloaded)']). This is objectui#10712's family (a superseded read overwrites the current one) and the same file its PR 2 guards: named as a candidate row or sub-issue of objectui#10712, not a new card. dedupe words:LineItemsPanel save reload stale parent·line items save lands after parent swap·LineItemsPanel save captured load closure",
"class: a · the 'Inert vi.mock Specifier Check' workflow (vi-mock-specifiers.yml, run with no install by design) is red on main c3a26cc and 5c61e52 and on every open PR head checked (objectui#10737, #10736, #10734, #10738). Evidence: a fresh no-install detached worktree at c3a26cc ran 'node scripts/check-vi-mock-specifiers.mjs' => exit 1, '1 mock resolves to no file on disk': apps/console/src/tests/filterContextTokensSweep-10666.test.tsx mocks the relative path '../../../../packages/plugin-map/node_modules/react-map-gl/dist/maplibre.js', which exists only after pnpm install. The same script exits 0 in an installed tree. The file arrived with objectui#10666 (PR objectui#10729, f9c06ef). The REST job-log download was refused by the egress proxy, so the CI log itself was not read; the reproduction stands in for it. dedupe words:vi.mock specifier node_modules path·Inert vi.mock Specifier Check red on main·filterContextTokensSweep maplibre mock·check-vi-mock-specifiers no install",
"carrier: objectui#10712 PR 2 (next to touch LineItemsPanel.tsx) · a save still in flight when the panel moves to another parent writes its failure unguarded (the save lifecycle, outside this card's load-error surface); not probed · noted in PR Acceptance notes, not filed",
"carrier: 承接者:无 · a load run that declines (no dataSource or parentId, a refused filter, no childObject) is numbered, so it supersedes a load in flight, but does not clear an earlier failure (rule: cleared on a commit; the forms' cannot-proceed branches behave the same) · noted in PR Acceptance notes, not filed"
]
}objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actions✅ ACCEPT: objectui#10738 (PR 2 of 2) at
e4805eadomain:uiseat 5,session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-27T03:30Z. Checked against the diff and CI, not against the report's own account.Implemented-by: claude/issue-10682-form-error-clears-pr2 Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoyitem reading the seventh site (objectui#10683) LineItemsPanel's load takes a private run number at its top, before its early returns. When the current load commits its rows it clears the banner (if (isCurrent()) setError(null)), and its failure write is gated the same way. So a superseded load can neither clear nor raise the banner, and the current load always can. This is the objectui#10578 shape (ObjectGantt'sreloadSeqRef)the save failure the panel keeps its one errorslot. The current load's commit also clears a save failure still shown: after it the rows are that load's answer, nothing is dirty and Save is off, so the message describes edits no longer on screen. The review reads this as correct, not a breach of 「applied per source」, which the triage states for the forms' two readsclaim limits the rows commit ( setRows/setOriginal/setDirty) andloadingare byte-identical to base (objectui#10712's PR 2). The six forms are untouched, andloadFailure.tsis the base blob. The dependency list, the fetch effect, the save andLineItemsPanelSchemaare unchangedthe two ACCEPT carries ( 5838467516)rule (ii): one row per layout, in which a schema commit leaves a record failure standing. The rows are green at base by construction, and an ablation that lets a schema commit clear the record slot turns exactly those six red. The bus sentence in the pending .changeset/10682-form-error-clears.mdnow reads true: the re-read runs for this record, its object or'*', and is held while the form has unsaved inputpin 69 cases, 14 new (8 panel, 6 rule (ii)). At base, 3 are red on their intended assertions: the stale banner, a superseded failure raised, and a save failure outliving its lines. Ablating either isCurrent()gate turns its superseded row red. The triggers are the panel's own (aparentIdre-render, the Save button)the changeset split check-changeset-presencecounts only ADDED declarations, so an edit to the pending file alone fails that required check. The panel's sentence therefore sits in a new.changeset/10682-form-error-clears-2.md(patch), and the pending file carries the narrowed bus sentence. Twopatchentries for one package make one bump. The seat adopts the splitcontract review PASS at CONTRACT_REVIEW_TIERon this head, record5852304366on the PR. The seat read the reviewer transcript: every turn was at tierclause ② noholdssemver patchfor@object-ui/plugin-form. Every changeset sentence reads true. 「Now only the current load writes that banner」 is true as a contrast between loads, and the save's own writes staygoverned NOT GOVERNED (4 paths) CI 3 skipped, 39 success, 0 red of this PR's own. The one other failure, Inert vi.mock Specifier Check, is not a required check and is red onmainc3a26ccdatoo:filterContextTokensSweep-10666.test.tsxmocks a path underpackages/plugin-map/node_modules, which the pre-install check cannot resolve (objectui#10731 / objectui#10732, ungraded; no fix exists yet). This diff adds novi.mockclosing keywords Fixes #10682is the body's first line, and it closes this card with PR 1 (PR objectui#10704,Refs)Carried to objectui#10712's PR 2, which owns
LineItemsPanel's rows commit- A sort, limit or filter change re-runs the load while rows are dirty, and the rows commit discards the unsaved edits with no signal. The candidate is a dirty hold on the reload, with the default form's bus hold as the precedent.
- A save that lands after the panel has moved to another parent re-reads the old parent through the load it captured.
- A save still in flight across a parent move writes its failure unguarded (not probed).
Filed separately from this PR's report: objectui#10740. A failed load for a new parent keeps the old parent's lines saveable, and Save moves them.
State
readyand auto-merge (squash) go through the relay opspr_ready+automerge_enablein this act. The card closes throughFixes #10682when this lands.objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actionsLanded (PR 2 of 2): PR objectui#10738 merged as
ee6f6c6through the merge queuedomain:uiseat 5,session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-27T03:45Z. Verified by content on objectuiorigin/mainee6f6c6, not by the sha:packages/plugin-form/src/LineItemsPanel.tsxcarriesloadSeqRef. The current load's commit clears the banner, and its failure write is gated on the current run.recordFormsErrorClears-10682.test.tsxhas theLineItemsPanelrows and a rule (ii) row per layout..changeset/10682-form-error-clears-2.mdis present ('@object-ui/plugin-form': patch), and.changeset/10682-form-error-clears.mdcarries the narrowed bus sentence.
With PR 1 (PR objectui#10704,
b526480), the card's seven sites are done. Three rows aboutLineItemsPanel's rows commit ride objectui#10712's PR 2, per the ACCEPT5852306780. objectui#10740 was filed separately.The card closed
completedthroughFixes #10682.pm:dispatchedis removed in the same stroke.
Filing-gate category: ① a product defect with named sites and a reproduction. Reader: triage first (route and grade), then the seat that claims it.
Filed by the
domain:uiseat 5 (session_01KUxVUa7e39aNjhkKi1gsoy) from the objectui#10663 dev report (PR objectui#10680, the family closure card for never-clearederror). That PR's census excluded the record forms as outside the data-view population, and named this as class a. The seat re-read the sites on objectuiorigin/main7baede3. ⛔ Filed bare, not graded here.Sites
Each form keeps an
errorstate that its reads set and nothing clears.setError(writes /setError(null)writes onorigin/main:setError(setError(null)packages/plugin-form/src/ObjectForm.tsx(:697,:791,:807)DrawerForm.tsxModalForm.tsxSplitForm.tsxTabbedForm.tsxWizardForm.tsxReproduction (the dev's throwaway probe, deleted, never committed): the real
SchemaRendererand the plugin-form registration, withobject-formin edit mode.findOnefails once, then anotifyDataChangedre-read of the record succeeds ⇒errorBefore=true,findOneCalls=2,errorAfterSuccessfulReread=true,valueShown=false. The other five were read at source only.Since objectui#10572 the forms re-read on the data-invalidation bus, so a transient failure followed by any save elsewhere is an ordinary way in.
Direction (for triage, not a ruling)
The objectui#10578 shape (clear on a successful commit), with one caveat the dev raised: each form has TWO reads (the object schema and the record) and no run guard. So the rule is per source: a record read that succeeds over a failed schema read must not clear the schema failure. PR objectui#10680 (in review) is the precedent for the data views.
Dedupe
Searched this repo with the MCP semantic issue search, including closed issues:
ObjectForm record form error screen never cleared after successful re-read findOne⇒ objectui#10190, objectui#10156, objectui#7354, objectui#7345, objectui#4292 and objectui#2968, all closed, all other plugin-form defects. None is about the form'serrorlifecycle, and the hits show the instrument is lit. objectui#10663 covers the data views, not the forms.Dedupe words:
ObjectForm never clears error·record form error screen stays after re-read·DrawerForm ModalForm error never cleared·form setError null