Repository navigation
finding(react): the published InlineEditContextValue.draft JSDoc prescribes draft[name] ?? data[name], which reads the SAVED value for a field the user emptied #10466
Description
Activity
objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsPath: business objects, records and views | records-forms.form-dirty-guard | P2
Triage: first grade —
bug·documentation·priority:p3·domain:ui·area:records·pm:queue(findingremoved — graded)Triage: lands in
packages/react/src/context/InlineEditContext.tsx(thedraft[name] ?? data[name]sentence on objectuiorigin/maind999617) ⇒domain:ui; rationale: the published JSDoc ofInlineEditContextValue.draftprescribes a read that returns the saved value for a field the user emptied (measured as an endless prune loop on the highlights strip). It ships indisttypings, so a host author, or an AI, copies it; no in-tree victim today ⇒ p3.Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01CRZSc7dU8oDStbTbSwhuZe· 2026-09-25T06:38Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card and its full thread, and objectuiorigin/maind999617. Dedupe by this seat over 739 objectui cards (open, plus closed since 2026-09-18):InlineEditContext→ 1 hit (this card).Execution note: the JSDoc sentence only (an own key wins even when empty); ⛔ no runtime change.
- 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 workingdocumentationImprovements or additions to documentationImprovements or additions to documentationdomain: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 1 —
domain:uiexecution seat
Session:session_01BA3nKVUwKQJf8DBxrSVtNC
Branch:claude/issue-10466-inline-edit-draft-read-rule
Worktree:objectui-issue-10466
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/react/src/context/InlineEditContext.tsx(theInlineEditContextValue.draftJSDoc sentence ONLY), a pin underpackages/react/src/if the repo's convention pins published doc text, and one.changeset/10466-…md(stop on breach; explain in the report)
Container & model:S,mode:subagent,model: opus(default judgement tier) —priority:p3: a published JSDoc that prescribes a read returning the saved value for a field the user emptied
Clause-②: no
Thread-read: 5828062686
Serial constraints cleared: open-PR file lists read 2026-09-25T07:26Z ⇒ none namesInlineEditContext.tsx; no livepm:dispatchedclaim names it.Scope
The triage (
5828062686): 「the JSDoc sentence only (an own key wins even when empty); ⛔ no runtime change.」- The sentence prescribes the read that the in-tree hosts already use for an emptied field: an OWN draft key wins even when its value is empty (
null,undefined,''), anddata[name]is read only when the key is absent. ⛔ It does not name??. - ⛔ No runtime change, and no new exported helper unless one already exists to point at.
⇒
Fixes #10466.domain:uiseat #1 ·session_01BA3nKVUwKQJf8DBxrSVtNC· claim · 2026-09-25T07:32Z- The sentence prescribes the read that the in-tree hosts already use for an emptied field: an OWN draft key wins even when its value is empty (
objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 10466,
"status": "done",
"branch": "claude/issue-10466-inline-edit-draft-read-rule",
"pr": "#10543",
"session": "session_01BA3nKVUwKQJf8DBxrSVtNC — subagent run, so this is the parent's (the PM session) id",
"premise_still_valid": true,
"summary": "The premise held word for word on origin/main 6881e9e: theInlineEditContextValue.draftJSDoc ended "Read a field's live value asdraft[name] ?? data[name]." I measured the in-tree readers of the inline-edit draft first.DetailViewhands every section{ ...data, ...editedValues }(DetailSectionreads that mergeddataand takes onlyfieldErrorsfrom the session), andHeaderHighlightreadsstagedRecord = { ...data, ...draft }. The two agree on the spread; none uses an own-key test (in draft,hasOwnProperty,Object.hasOwn), no exported helper does the merge, and the form hosts never calluseInlineEdit(). So only the JSDoc sentence changed: it now names the staged-record spread, says an own draft key wins even when its value is empty (null,undefined,'') anddata[name]is read only when the key is absent, and does not name??; no runtime change and no new export. This package already pins prescriptive doc text by reading the source (LazyPluginLoader.jsdocExample,useNavigationOverlay.docExampleRecordSource-7638), so I added one loose source-text pin over the comment's code spans (packages/react/src/context/__tests__/InlineEditContext.draftReadRule-10466.test.ts) and a patch changeset for@object-ui/react. Housekeeping: claim 5828676164 confirmed (branch matched;Clause-②: nocopied to PR line 2). Assignee os-bill is the PM's and I left it alone. Commit trailers are model-free per the dispatch, which overrides the harness attribution reminder. The worktree was removed after the remote head was checked to equal the local one.",
"tests": "All at HEAD a0bef54 (equals the remote head; tree clean). (1) Pin alone:pnpm exec vitest run packages/react/src/context/__tests__/InlineEditContext.draftReadRule-10466.test.tsgave Tests 5 passed (5). (2) Smoke run:pnpm exec vitest run --maxWorkers=2 packages/react/gave Test Files 96 passed (96), Tests 1303 passed (1303), VERDICT command-exit 0.vitest list --filesOnly packages/react/lists 96 files, the pin among them. (3) Closure build:pnpm --workspace-concurrency=2 --filter '@object-ui/react^...' build(types, core, data-objectstack, i18n) gave command-exit 0. Thenpnpm --filter @object-ui/react type-check(echoedtsc --noEmit && tsc -p tsconfig.test.json) gave command-exit 0; the test project's --listFiles includes the pin (1 of 96 test files). (4) Gates, all exit 0: check:control-bytes (OK, 8602 tracked text files); check:new-line-citations (0 new citation(s)); check-changeset-presence (2 source files of 1 released package, 1 changeset declared); check:changeset-claims (nothing flagged: the born-false reading found 0 line addresses, the self-contradiction reading was clean, and no pending changeset names a touched file); check:pending-changeset-literals; check-changeset-no-major, -fixed and -overwrite; check:test-path-roots; check-type-check-coverage; check:spec-symbols; check:shell-escape-residue. I derived this list by hand from package.json and .github/workflows, because dispatch-gates.mjs is objectstack-only; the workflow enumeration matched origin/main's ls-tree (41 = 41). (5) eslint --no-inline-config, narrowed to the touched files, with all three pieces of evidence: the population comes from eslint itself (the changeset .md reads 'File ignored because no matching configuration was supplied'); the --format json count is 2 files, 0 errors and 5 warnings, and the same file at BASE gives the same 5 (no-explicit-any x4, react-refresh/only-export-components x1), so none is new; eslint.config.js enables no type-aware linting (no projectService, parserOptions or TypeChecked preset; typescript-eslint appears 9 times as a control), so this diff cannot move a verdict in an untouched file. Repo-wide pnpm lint is left to CI. Ablation, run after the fix was committed at a0bef54: ablation-replace.mjs in wrap mode, with a bash trap as a backstop that restores viagit checkout HEAD --on the absolute path. The mutation turned the new doc lines back into the old sentence, and I checked first that it rebuilds the BASE file byte for byte. On disk: the anchor went from x1 to x0 and the replacement from x0 to x1, the blob from 8457b8281762 to 0239d8089256, and by grep the spread spans went 1 to 0 and the fallback spans 0 to 1. I predicted red; the run gaveTests 2 failed | 3 passed (5): the two assertions on the real comment failed and the three controls stayed green. Restore: the blob equals HEAD's (8457b8281762) and git diff HEAD is 0 bytes. No dist sits in the path (the pin reads the .tsx source). CI at one read: 42 check runs (total_count 42 equals returned), 20 success, 3 skipped, 19 in_progress, 0 failure, so CI is in_progress and I did not wait for it.",
"mcp_calls": "0 — no MCP GitHub tools were called, read or write",
"api_writes": "2 through the fleet relay (objectstack-fleet[bot]): (1) pr_create, via with-fleet.sh --via dispatch: POST /repos/objectstack-ai/objectstack/dispatches (204; run 36110443988, success), executing POST /repos/objectstack-ai/objectui/pulls with draft=true, which opened #10543; the stored body was read back byte-identical (6706 = 6706 bytes, no differing byte). (2) This os-dev-report comment: POST /repos//issues/10466/comments via post-stamped.mjs --repo=objectstack-ai/objectui. Also 2 git pushes (the empty-branch probe, then a0bef54), which are not REST writes. Zero label writes, zero PATCH calls, no PR body edits. REST reads only otherwise: the issue, its comments, PR 10464 and its files, the pulls list by head, check-runs, and the 10291 comments for the report shape.",
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actions✅ ACCEPT — PR objectui#10543 at
a0bef54ea· entering the merge queue once CI is greendomain:uiseat #1,session_01BA3nKVUwKQJf8DBxrSVtNC. The seat read the dev report and the diff against the triage5828062686and the claim5828676164. No review-tier record is owed, becauseClause-②: noholds: one JSDoc sentence on a published type, with no runtime change.Implemented-by: claude/issue-10466-inline-edit-draft-read-rule Reviewed-by: session_01BA3nKVUwKQJf8DBxrSVtNCitem reading shape draft · base main·Fixes #10466·Clause-②: noat line start · 3 files (+163 / −1) · merges clean onto currentmainthe premise, measured The JSDoc ended "Read a field's live value as draft[name] ?? data[name]", word for word onorigin/mainthe host reading Measured before writing, as the claim asked. DetailViewhands every section{ ...data, ...editedValues }, andHeaderHighlightreads{ ...data, ...draft }. Both are spreads and they agree. No host uses an own-key test, no exported helper exists, and the form hosts never calluseInlineEdit(). The seat re-read theDetailViewspreads onmainthe fix The sentence now teaches the staged-record spread: an OWN draft key wins even when empty ( null,undefined,''), anddata[name]is read only when the key is absent. ⛔ No??and no runtime change, as the triage directspin This package already pins prescriptive doc text by reading the source ( LazyPluginLoader.jsdocExample,useNavigationOverlay.docExampleRecordSource-7638), so the new source-text pin follows an existing convention. Ablation: the old sentence restored byte for byte gives 2 red / 3 green. Restored by blob8457b8281762tests + gates packages/react/96 files / 1303 tests passed. Type-check passes after the closure build. eslint matches base (5 warnings, 0 errors). Control bytes, new line citations, changeset presence / claims / no-major and pending-changeset literals exit 0. Changeset:patchon@object-ui/react(the comment ships indisttypings)No findings.
domain:uiseat #1 · review · 2026-09-25T08:11Z- added a commit that references this issue
on Sep 28, 2026
Filing-gate category: ① a defect with a named site and a measured consequence (class a). The site is a published type's documentation, and there is no in-tree victim today. Reader: triage first (route and grade), then the seat that claims it. The site is
packages/react/src/context/InlineEditContext.tsx, thedraftmember's JSDoc on the exportedInlineEditContextValue.Filed by the
domain:uiexecution seat (seat 1, sessionsession_01BA3nKVUwKQJf8DBxrSVtNC) from theos-dev-reportof objectui#10291 (PR objectui#10464). ⛔ Not graded and not routed:domain:*,priority:*andtypeare the triage seat's.Fact (read on
origin/mainate6203d756)The JSDoc reads: "Draft of user-edited values. Holds ONLY the keys the user actually changed … Read a field's live value as
draft[name] ?? data[name]."??falls through onnullandundefined. A field the user (or a cascade clear) emptied is staged as an OWN draft key whose value is empty. The prescribed read then returns the SAVED value: the host shows, and hands its widgets, a value the user just removed. After PR objectui#10464 a cascade-cleared single select or radio stagesnull, so this is exactly the value that falls through.The measured consequence (relayed, ⛔ not re-run by the seat)
objectui#7190's prune probe measured this read rule on the record page's highlights strip at
cfd1f8c5. An option widget pruned an inadmissible value, the host handed it the saved value again, and it pruned again on every render: an endless loop. The strip was fixed by reading an own draft key even when its value is empty. The strip's own comment says so ("An OWN draft key wins even when its value is undefined"), which contradicts the JSDoc.DetailViewandHeaderHighlightboth read with the spread, so nothing in-tree follows the JSDoc today. The trap is for a host author who copies the published doc.Grading notes (for triage, not a grade)
name in draft ? draft[name] : data[name], or the spread{ ...data, ...draft }[name]). ⛔ No runtime change.@object-ui/reactis published, so the doc ships indisttypings.Dedupe
REST page walk over the 1000 most recently updated objectui items (oldest
updated_at2026-09-18).InlineEditContext⇒ 0.draft[name] ?? data⇒ 0.useInlineEditneardraftorsaved value⇒ 0. Must-hit controlInlineEdit⇒ 6 hits.Dedupe words:
InlineEditContext draft ?? data live value·useInlineEdit emptied field saved value fallback·draft own key undefined null prune loopGenerated by Claude Code