You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
DrawerForm, TabbedForm and WizardForm: editing a record that does not exist opens a blank editable form #29247
Filing gate: ② out-of-scope finding, class a, from #29157 (PR #29246; report /mnt/project-files/issue-29157/os-dev-report.md) · filed by domain:ui#1 · ⛔ Not a claim. No grade: triage sets it.
Business reason. A user who opens an old link to a deleted record in a drawer, tabbed or wizard form sees a blank form they can edit. They fill it in and press save, and only then learn the record does not exist. #29157 fixes this for the modal form (the ?form= overlay) only. The other three form shells still do it.
Measured (the #29157 dev, by reading at dedc3505; the modal case was probed live in tests).ObjectStackAdapter.readOne answers null on a 404, and each shell turns that into an empty record:
ui/packages/plugin-form/src/WizardForm.tsx:678-679: setFormData(data || {}), which also sets setPersistedRecord
Wanted. In edit mode, a null read in each shell draws the same not-found state PR #29246 gives ModalForm: Empty with empty.recordNotFound and empty.recordNotFoundDescription, no form, no footer and no save. Reuse #29246's private RecordNotFoundError in loadFailure.ts. Create mode and a found record are unchanged. No new string.
Tests. For each shell, an edit whose findOne answers null renders the not-found state and no save control. Controls: create, and a found record. Each must be red on main.
Reader.domain:ui (area:records). Size S. Serialize after PR #29246 (#29157), which adds the shared failure marker.
Claim: PM loop round 14 · 2026-11-02T11:32Z
Session: session_01QsR2jTeZsth6vzGnL4eoaN (card thread, os-dev); dispatched by seat session session_01QsR2jV4dWohojjbTJTQq5m
Account: zhuangjianguo
Branch: claude/project-thread-w237hz (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29247
Domain: domain:ui
Seat: domain:ui#3
File surface: ui/packages/plugin-form/src/DrawerForm.tsx, TabbedForm.tsx, WizardForm.tsx, loadFailure.ts (a module-private shared helper only), ModalForm.tsx only if its not-found drawing moves into that helper, their tests, and a changeset (stop on breach; explain in the report)
Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: open — recorded at review
Thread-read: 6575606718
Premise: holds (at origin/mainc2a5be03: DrawerForm.tsx:448, TabbedForm.tsx:415 and WizardForm.tsx:678 each do setFormData(data || {}) on an edit read, so a null read draws a blank editable form; loadFailure.ts:94RecordNotFoundError is module-private and ModalForm.tsx:625 / :921 raise and draw it). No new string; no new export from the package entry.
Serial constraints cleared: PR #29246 (#29157) landed as db6b4084, as the card requires. Open PR #29287 (spec, FormView drawer keys) touches no plugin-form file at its current head; re-checked before pr_create. No in-flight claim of seats 1-5 names these files.
{
"issue": 29247,
"status": "done",
"branch": "claude/project-thread-w237hz",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29351",
"session": "session_01QsR2jTeZsth6vzGnL4eoaN",
"premise_still_valid": true,
"summary": "Premise held at c347d22d: DrawerForm, TabbedForm and WizardForm each seeded setFormData(data || {}) (the wizard also setPersistedRecord) after a findOne that answers null for a missing record, drawing a blank editable form with Update. Each now fails a null edit read with the package-private RecordNotFoundError (the ModalForm branch from PR #29246) and draws a new module-private RecordNotFoundState in loadFailure.ts (Empty + EmptyTitle + EmptyDescription, existing pack keys empty.recordNotFound / empty.recordNotFoundDescription through its own createSafeTranslation table with the en values); ModalForm's inline drawing and its two table rows moved into that helper (test id kept). No form, no footer, no save; a bus re-read that finds the record deleted shows the same state; create and a found record unchanged. No new string, no new package export. Draft PR #29351 at head 56a7a05d.",
"tests": "New recordNotFound-29247.test.tsx (describe.each over DrawerForm, TabbedForm, WizardForm; 4 cases each: null read draws the state + test id with no title input, no Update, no Error loading form; bus re-read finds the record deleted; CONTROL found record draws form + Update; CONTROL create draws empty form + Create, no findOne). At c4c659f5 with the 29157 file: Tests 16 passed (16). Ablation at 35a69884: the three layouts restored from base c347d22d via git restore --source (RecordNotFoundError count 0 each; data || {} count 1/1/2), loadFailure.ts at HEAD: Tests 6 failed, 6 passed (12); all 6 failures are the two not-found cases per layout (Unable to find an element with the text: Record not found), all 6 CONTROLs green. Restored via git restore --source=HEAD --staged --worktree under an EXIT trap; git diff HEAD empty, status clean, each git hash-object equal to its HEAD blob. plugin-form package locked at maxWorkers=2: 220/220 files, 2435 passed 1 skipped at 903d4662 (same at 35a69884). turbo type-check @object-ui/plugin-form (tsc --noEmit && tsc -p tsconfig.test.json): exit 0 at 903d4662 and 56a7a05d (first run at 35a69884 red TS2769 on createElement data-testid, fixed in 815181c6 by typing the prop, no cast). At 56a7a05d after the second main merge: 4 files (both not-found pins, ModalForm.untypedRepeater new on main, recordSwapLoading) 33/33. eslint on changed files: 0 errors, 1 warning in a changed hunk (documented any in the new test, same shape as recordSwapLoading.test.tsx). check-i18n-call-site-keys exit 0; check:new-line-citations 0 new.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 34 derived (identical vs merge bases c347d22d and d9885f5d). At 903d4662: 33 non-vitest gates in one loop, all exit 0 (check-sdui-manifest.mjs and --self-test, check:nul-bytes, check:query-options-erasure, check:type-check-debt, check:test-source-alias, check:cross-package-test-inputs, check:published-files among them); pnpm --dir ui exec vitest run scripts/: 169 passed 2 skipped files, 4903 passed 2 skipped tests, exit 0. dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED. At 56a7a05d (main merged again, 21 commits, none touching the changed files): check-sdui-manifest, check-changeset-no-major, check-adr-0087-registration, check-empty-changeset, check:nul-bytes, check:type-check-debt, check:type-check-coverage, check:test-source-alias, check:cross-package-test-inputs, check:undeclared-dep-imports, check:published-files all exit 0; the other 23 last measured at 903d4662. PR body gates before pr_create: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword all exit 0. PR check-runs: not read at report time; re-runs are the seat's.",
"line_budget": "7 files, +237/-18 vs merge base (git diff --numstat origin/main...HEAD at 56a7a05d): DrawerForm +12/-1, TabbedForm +12/-1, WizardForm +13/-2, ModalForm +2/-13, loadFailure.ts +43/-1, new test +145, changeset +10",
"files_changed": ["ui/packages/plugin-form/src/DrawerForm.tsx", "ui/packages/plugin-form/src/TabbedForm.tsx", "ui/packages/plugin-form/src/WizardForm.tsx", "ui/packages/plugin-form/src/ModalForm.tsx", "ui/packages/plugin-form/src/loadFailure.ts", "ui/packages/plugin-form/src/recordNotFound-29247.test.tsx", ".changeset/29247-record-forms-not-found.md"],
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "4 — read only: issue_read x3 (get, get_comments x2), pull_request_read x1 (get_files on PR 29287); ToolSearch loads not counted",
"api_writes": "2 — fleet-write relay pr_create (objectstack#29351, read back 9781 bytes identical, no assignees), this post-stamped comment on #29247; plus git push (not REST)",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: a · reach: component probe only, public door not measured — a throwaway vitest probe at 56a7a05d (removed after) rendered ObjectForm (the default object-form arm) with mode edit, recordId NOPE_x and findOne answering null: the title input is drawn empty and enabled, the Update button is present, no Record not found (Tests 1 passed). ui/packages/plugin-form/src/ObjectForm.tsx record read (await dataSource.findOne, then setInitialData(data) and run.commit) has no null branch; the fix shape is the RecordNotFoundError branch plus RecordNotFoundState this PR gives the other four layouts. Which console door mounts this arm for an edit by id was not measured. · dedupe words: ObjectForm edit missing record blank form, default object-form findOne null, record not found ObjectForm",
"boundary, not filed: ModalForm's master-detail arm (MasterDetailForm header read) still not measured for a null header read, as the 29157 report noted · carrier: none"
],
"deviations": "The session context asks to set the PR assignee and reviewer through GitHub MCP writes; os-dev.md and the seat rules forbid PR assignees and MCP writes, so neither was done. The gate loop first started at 35a69884 was stopped (own PIDs) after 23 gates so the union could run once on the merged head 903d4662; after a second main merge right before pr_create, only the 11 fast ratchet/changeset gates and the touched tests were re-run at 56a7a05d (declared above). No live-console measurement; the chain is pinned in vitest from the findOne answer down."
}
Landed: PR #29351 merged at 2026-11-02T13:35Z as c92ae761. Fixes #29247 closed this card; pm:dispatched removed. Delivered: the drawer, tabbed and wizard forms draw the record-not-found state for an edit whose record no longer exists (no form, no save), sharing one module-private RecordNotFoundState with the modal form. Follow-up filed: #29354 (the default ObjectForm layout). Nothing left on this card.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
pm:queue· 2026-11-02T11:05Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m
Claim: PM loop round 14 · 2026-11-02T11:32Z
Session:
session_01QsR2jTeZsth6vzGnL4eoaN(card thread, os-dev); dispatched by seat sessionsession_01QsR2jV4dWohojjbTJTQq5mAccount:
zhuangjianguoBranch:
claude/project-thread-w237hz(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29247Domain:
domain:uiSeat:
domain:ui#3File surface:
ui/packages/plugin-form/src/DrawerForm.tsx,TabbedForm.tsx,WizardForm.tsx,loadFailure.ts(a module-private shared helper only),ModalForm.tsxonly if its not-found drawing moves into that helper, their tests, and a changeset (stop on breach; explain in the report)Container & model: S,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
open — recorded at reviewThread-read: 6575606718
Premise: holds (at
origin/mainc2a5be03:DrawerForm.tsx:448,TabbedForm.tsx:415andWizardForm.tsx:678each dosetFormData(data || {})on an edit read, so a null read draws a blank editable form;loadFailure.ts:94RecordNotFoundErroris module-private andModalForm.tsx:625/:921raise and draw it). No new string; no new export from the package entry.Serial constraints cleared: PR #29246 (#29157) landed as
db6b4084, as the card requires. Open PR #29287 (spec, FormView drawer keys) touches no plugin-form file at its current head; re-checked before pr_create. No in-flight claim of seats 1-5 names these files.objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jTeZsth6vzGnL4eoaN
os-dev-report
{ "issue": 29247, "status": "done", "branch": "claude/project-thread-w237hz", "pr": "https://github.com/objectstack-ai/objectstack/pull/29351", "session": "session_01QsR2jTeZsth6vzGnL4eoaN", "premise_still_valid": true, "summary": "Premise held at c347d22d: DrawerForm, TabbedForm and WizardForm each seeded setFormData(data || {}) (the wizard also setPersistedRecord) after a findOne that answers null for a missing record, drawing a blank editable form with Update. Each now fails a null edit read with the package-private RecordNotFoundError (the ModalForm branch from PR #29246) and draws a new module-private RecordNotFoundState in loadFailure.ts (Empty + EmptyTitle + EmptyDescription, existing pack keys empty.recordNotFound / empty.recordNotFoundDescription through its own createSafeTranslation table with the en values); ModalForm's inline drawing and its two table rows moved into that helper (test id kept). No form, no footer, no save; a bus re-read that finds the record deleted shows the same state; create and a found record unchanged. No new string, no new package export. Draft PR #29351 at head 56a7a05d.", "tests": "New recordNotFound-29247.test.tsx (describe.each over DrawerForm, TabbedForm, WizardForm; 4 cases each: null read draws the state + test id with no title input, no Update, no Error loading form; bus re-read finds the record deleted; CONTROL found record draws form + Update; CONTROL create draws empty form + Create, no findOne). At c4c659f5 with the 29157 file: Tests 16 passed (16). Ablation at 35a69884: the three layouts restored from base c347d22d via git restore --source (RecordNotFoundError count 0 each; data || {} count 1/1/2), loadFailure.ts at HEAD: Tests 6 failed, 6 passed (12); all 6 failures are the two not-found cases per layout (Unable to find an element with the text: Record not found), all 6 CONTROLs green. Restored via git restore --source=HEAD --staged --worktree under an EXIT trap; git diff HEAD empty, status clean, each git hash-object equal to its HEAD blob. plugin-form package locked at maxWorkers=2: 220/220 files, 2435 passed 1 skipped at 903d4662 (same at 35a69884). turbo type-check @object-ui/plugin-form (tsc --noEmit && tsc -p tsconfig.test.json): exit 0 at 903d4662 and 56a7a05d (first run at 35a69884 red TS2769 on createElement data-testid, fixed in 815181c6 by typing the prop, no cast). At 56a7a05d after the second main merge: 4 files (both not-found pins, ModalForm.untypedRepeater new on main, recordSwapLoading) 33/33. eslint on changed files: 0 errors, 1 warning in a changed hunk (documented any in the new test, same shape as recordSwapLoading.test.tsx). check-i18n-call-site-keys exit 0; check:new-line-citations 0 new.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 34 derived (identical vs merge bases c347d22d and d9885f5d). At 903d4662: 33 non-vitest gates in one loop, all exit 0 (check-sdui-manifest.mjs and --self-test, check:nul-bytes, check:query-options-erasure, check:type-check-debt, check:test-source-alias, check:cross-package-test-inputs, check:published-files among them); pnpm --dir ui exec vitest run scripts/: 169 passed 2 skipped files, 4903 passed 2 skipped tests, exit 0. dispatch-gates --ran: 34 derived, 34 run, 0 NOT-MEASURED. At 56a7a05d (main merged again, 21 commits, none touching the changed files): check-sdui-manifest, check-changeset-no-major, check-adr-0087-registration, check-empty-changeset, check:nul-bytes, check:type-check-debt, check:type-check-coverage, check:test-source-alias, check:cross-package-test-inputs, check:undeclared-dep-imports, check:published-files all exit 0; the other 23 last measured at 903d4662. PR body gates before pr_create: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword all exit 0. PR check-runs: not read at report time; re-runs are the seat's.", "line_budget": "7 files, +237/-18 vs merge base (git diff --numstat origin/main...HEAD at 56a7a05d): DrawerForm +12/-1, TabbedForm +12/-1, WizardForm +13/-2, ModalForm +2/-13, loadFailure.ts +43/-1, new test +145, changeset +10", "files_changed": ["ui/packages/plugin-form/src/DrawerForm.tsx", "ui/packages/plugin-form/src/TabbedForm.tsx", "ui/packages/plugin-form/src/WizardForm.tsx", "ui/packages/plugin-form/src/ModalForm.tsx", "ui/packages/plugin-form/src/loadFailure.ts", "ui/packages/plugin-form/src/recordNotFound-29247.test.tsx", ".changeset/29247-record-forms-not-found.md"], "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "4 — read only: issue_read x3 (get, get_comments x2), pull_request_read x1 (get_files on PR 29287); ToolSearch loads not counted", "api_writes": "2 — fleet-write relay pr_create (objectstack#29351, read back 9781 bytes identical, no assignees), this post-stamped comment on #29247; plus git push (not REST)", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: a · reach: component probe only, public door not measured — a throwaway vitest probe at 56a7a05d (removed after) rendered ObjectForm (the default object-form arm) with mode edit, recordId NOPE_x and findOne answering null: the title input is drawn empty and enabled, the Update button is present, no Record not found (Tests 1 passed). ui/packages/plugin-form/src/ObjectForm.tsx record read (await dataSource.findOne, then setInitialData(data) and run.commit) has no null branch; the fix shape is the RecordNotFoundError branch plus RecordNotFoundState this PR gives the other four layouts. Which console door mounts this arm for an edit by id was not measured. · dedupe words: ObjectForm edit missing record blank form, default object-form findOne null, record not found ObjectForm", "boundary, not filed: ModalForm's master-detail arm (MasterDetailForm header read) still not measured for a null header read, as the 29157 report noted · carrier: none" ], "deviations": "The session context asks to set the PR assignee and reviewer through GitHub MCP writes; os-dev.md and the seat rules forbid PR assignees and MCP writes, so neither was done. The gate loop first started at 35a69884 was stopped (own PIDs) after 23 gates so the union could run once on the merged head 903d4662; after a second main merge right before pr_create, only the 11 fast ratchet/changeset gates and the touched tests were re-run at 56a7a05d (declared above). No live-console measurement; the chain is pinned in vitest from the findOne answer down." }objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m
Landed: PR #29351 merged at 2026-11-02T13:35Z as
c92ae761.Fixes #29247closed this card;pm:dispatchedremoved. Delivered: the drawer, tabbed and wizard forms draw the record-not-found state for an edit whose record no longer exists (no form, no save), sharing one module-privateRecordNotFoundStatewith the modal form. Follow-up filed: #29354 (the defaultObjectFormlayout). Nothing left on this card.