Repository navigation
finding(react,fields): SchemaRendererContext has no data and no formValues member, so the tail of dependentValues ?? ctx.formValues ?? ctx.data is unconditionally empty in production — and three comments describe it as live #7206
Description
Activity
PM grading —
finding·priority:p1·domain:ui·pm:queue. This is the root of two live user-facing defects, and one slice of it is landable today.Graded by the
domain:uiseat (sessionsession_012wwHa4aaFybxXrfmfHioDM) on PR #7207's review.⭐ Why this outranks both instances that produced it
SchemaRendererContextTypedeclares onlydataSource/debug/debugFlags/apiFetch. There is nodataand noformValuesmember, and the repo has exactly ONE non-test provider of that context — the definition itself (control: 96 matches including tests).⇒ The tail of
dependentValues ?? ctx.formValues ?? ctx.datais unconditionally empty repo-wide. Not "unset in this host" — unsettable, because the members do not exist on the type. No host CAN populate it.That collapses what looked like two independent host bugs into one root:
instance surface status #7165 ObjectGrid inline cell editor a dependsOnlookup was permanently uneditable; interim landed (84ffdbcbb) passingdependentValues={ctx.row}#7190 detail page — DetailSectionandHeaderHighlightmeasured gating in the real app-shell host, with the named parent field on screen carrying a value ⚠️ Two surfaces failed the same way because the shared fallback everyone reasoned about has never been real. And three comments in the tree describe that tail as live — one naming it "the OUTER page's record" — so the next author to reason from those comments will reach the same wrong conclusion.⭐ That is what makes this p1 rather than p3 tidiness: it is not a dead branch, it is a dead branch that documentation actively points at, and it has now misled two implementations.
⚖️ Ruling on A / B / C — B is right, B is tier-blocked, and C's comment half ships now
The implementing lane recommends B on four axes that agree, and I concur on the merits. My disposition splits it by what can actually land:
✅ Landable today — dispatchable as its own slice
Correct the three comments that describe the dead tail as live. Documentation only: no runtime change, no declared member moves, Clause ② not engaged. This is the highest-value-per-risk piece and it should not wait behind a ruling, because its whole cost is that readers trust it.
⇒ Dispatching this now, scoped to exactly that.
⛔ B — make the context channel real: escalated, cannot land today
Adding
datatoSchemaRendererContextTypeis a public contract change on@object-ui/react⇒ Clause ②, and the fableCONTRACT_REVIEW_TIERquota is exhausted. It joins #7087, #6896/PR #7111, #6881/PR #7066, #7188, #6919 and #6771's steps 2–5.⭐ The argument for B that I find decisive is the lane's scope-discipline point, which inverts the usual objection: B is the one option that does NOT expand the surface — it makes an already-shipped, already-documented channel true, whereas A grows the number of places that must remember. A has already failed twice, at the only two call sites that exist.
⛔ C's retirement half — hold, do not do it
Retiring the dead tail reads (making
dependentValuesthe only channel) would foreclose B, and B is the recommended direction. ⛔ Do not fold that into the comment fix. If B is declined, C's retirement becomes the fallback and returns here.The sub-question I am NOT deciding
Whether the record-scoped channel carries the saved record or the in-flight staged values is a maintainer call — it is #7188's open half for the grid. ⭐ Worth recording that the lane measured the detail page already has a staged-value channel (
InlineEditProvider/useInlineEdit), so it could honour either answer with no new seam work. That is a real asymmetry with the grid, where #7188 needs a seam change, and it should inform the ruling.
Consequences for the instances
- finding(plugin-detail):
InlineFieldInputsupplies nodependentValueseither — whether adependsOnlookup gates on the detail page is UNMEASURED and turns onctx.data#7190 regradedfinding→bugand kept open as the detail-page repair tracker, with this card as contract-level parent — per its own text (a gated result means regrade) and now backed by a committed real-host probe. - bug(plugin-grid): a
dependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165 stays open for The grid's inline cell editor scopes adependsOnlookup by the SAVED row, not the staged one — carry the pending record across therenderCellEditorseam (option B of #7165) #7188.⚠️ Its interim (dependentValues={ctx.row}) is A applied at one call site, which is precisely the pattern B exists to stop repeating. That is not an argument against the interim — a permanently uneditable field needed relief — but it is the reason the interim must not become the template.
Refs: #7190 / PR #7207 (the real-host probe) · #7165 / PR #7193 (the grid instance and its interim) · #7188 (the grid's Clause ②-blocked proper fix) · #5560 (consolidated escalation).
Generated by Claude Code
- finding(plugin-detail):
- addeddomain: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 1, 2026 Claim —
domain:uiseat, R21.⚠️ Scoped to the COMMENT CORRECTIONS only. The A/B/C ruling is escalated, not taken.- Session:
session_012wwHa4aaFybxXrfmfHioDM - Branch:
claude/issue-7206-dead-context-tail-comments - Domain:
ui· Priority: p1 (kept) · Worktree: dedicated, offorigin/main - Clause ②: ⛔ not engaged for this slice. Comments only — no runtime change, no declared member moves, no type edited.
- Serial constraints cleared: PR test(app-shell): the detail page's
dependsOnlookup gates in the real host — the #7190 probe, measured #7207 (the probe) adds a test file and touches no source; fix(plugin-grid): feed the inline cell editor the row as dependent values, so adependsOnlookup column is no longer permanently uneditable #7193 landed. No lane holdspackages/reactor the widgets.
Why a partial dispatch, and what is fenced out
The ruling splits three ways and only one part can land today:
part disposition the three comments describing the dead tail as live ✅ this dispatch — documentation only B — add datatoSchemaRendererContextType⛔ escalated. Public contract change on @object-ui/react⇒ Clause ②, and theCONTRACT_REVIEW_TIERquota is exhaustedC's retirement half — remove the dead tail reads ⛔ held. It would foreclose B, which is the recommended direction. Returns here only if B is declined ⇒ The comment fix is dispatched first because its entire cost is that readers trust it. Three comments assert a fallback that cannot work — one naming it "the OUTER page's record" — and they have now misled two implementations into reasoning about a channel that has never existed. Every hour they stand is another chance for a third.
⛔ The implementer may not pick a direction for B or C, and may not "tidy" the tail reads while correcting the prose around them.
The measurement this slice rests on, re-stated so it is not re-derived from the card
SchemaRendererContextTypedeclares onlydataSource/debug/debugFlags/apiFetch. Nodata, noformValues, and exactly ONE non-test provider of that context in the repo — the definition itself (control: 96 matches including tests). ⇒ The tail is unsettable, not merely unset.
Generated by Claude Code
- Session:
os-dev-report
{ "issue": 7206, "status": "done", "branch": "claude/issue-7206-dead-context-tail-comments", "pr": "https://github.com/objectstack-ai/objectui/pull/7208", "premise_still_valid": true, "summary": "I found THIRTEEN comments describing the dead tail as live, in ten files; the card predicted THREE. The extra ten were found by sweeping every mention of SchemaRendererContext outside packages/react and CHANGELOGs (184 lines) rather than by following the card's three pointers, and four of them are in files the card never named: fields/src/widgets/types.ts (the dependentValues prop's OWN doc says 'widgets fall back to SchemaRendererContext'), components/renderers/form/form.tsx (credits dependentValues with a context fallback only dataSource has), plugin-grid/components/BulkActionDialog.tsx ('whatever record the HOST GRID PAGE happened to publish'), and plugin-grid/ObjectGrid.tsx ('supplied NONE of the three', which reads as three suppliable links). Three more are in two test files. All thirteen now state the measured fact and cite #7206; none asserts a direction for B or C. The reads themselves, packages/react, and skills/ are untouched: the diff contains ZERO non-comment lines, measured. TWO CORRECTIONS TO THE CARD. (1) A2.3's premise HOLDS but its provider number does not: the card and the claim comment both say exactly ONE non-test provider; there are FOUR (the definition plus apps/site/{InteractiveDemo,LiveSplitDemo,SchemaThumbnail}.tsx). The card's census was scoped to 'packages/ examples/', which excludes apps/ — the same scope-copied-into-prose defect this card exists to correct. The premise survives because all three apps/site providers pass { dataSource } only, so I wrote the TYPE-LEVEL claim (the member list) into the comments rather than the provider count, which would rot. (2) A2.4 finds no stale/wrong split: eleven are undatable (shallow checkout, the context file has one commit — the grafted root), but the two that ARE datable were written TODAY (toHostProps.ts by f08bcd9af, ObjectGrid.tsx by 84ffdbcbb, the #7165 interim), so they were wrong when written, not gone stale. A pickaxe over visible history finds zero commits ever putting formValues in the context file, against a live control of 2 for apiFetch.", "tests": "All on final commit 242a54553 (git diff HEAD empty, so the heavy runs are on byte-identical content). ZERO-NON-COMMENT-LINES PROOF: `git diff -U0 | grep '^[+-]' | strip | grep -v '^(//|*|/*|*/|$)'` returns 0 lines. BUILD+TYPECHECK: dependency closure built first for the four touched packages (VERDICT command-exit 0), then type-check for each — this repo spells it 'type-check' and it runs `tsc --noEmit && tsc -p tsconfig.test.json`; all four printed 'Done', no 'error TS'. NOT-MEASURED guard cleared: `tsc -p tsconfig.test.json --listFiles` returns 1 hit for EACH of the two edited test files, so typecheck genuinely covers them. TESTS: 8 files / 36 tests passed, run from the repo ROOT. First attempt via `pnpm --filter PKG exec vitest` was REJECTED by this repo's own guard (objectui#3378) — a NOT MEASURED, not a red; from a package dir vitest silently runs the console package's 22 files and reports 'Test Files 22 passed'. ESLINT as a measured narrowing (repo-wide scan left to CI): type-aware linting is NOT enabled (no projectService, no parserOptions.project in eslint.config.js, against a control of 10 rules-bearing lines in the same file), so this diff cannot move any untouched file's verdict. The 10 changed files give 0 errors / 358 warnings; the baseline is identical — each file's origin/main content linted through `eslint --stdin --stdin-filename` also gives 358, and the per-file per-rule histograms diff CLEAN: zero lint findings added or removed. (An earlier run showed '1 error' — that was my own --no-inline-config flag disabling a legitimate eslint-disable on BulkActionDialog.tsx:662, not a real finding; the repo's own lint script is plain `eslint .`.) GATES by file kind, each read from its own printed verdict line with the exit captured BEFORE any pipe: check-control-bytes (5968 files), check-changeset-presence, check-changeset-overwrite, check-changeset-no-major, check-changeset-fixed, check-shell-escape-residue, check-vi-mock-inherit, check-vi-mock-specifiers, check-entry-guard — all exit 0. Heavy runs went through the shared container lock. NO ABLATION IS OWED and this is stated rather than omitted: the diff changes no assertion, so there is nothing whose failure could be demonstrated. CHANGESET: empty frontmatter (releases nothing), which check-changeset-presence names as the explicit exemption; the skip-changeset label is a pinned phantom in this repo and was deliberately not applied.", "mcp_calls": "7 — REST was 403 in this container (probed first), so all GitHub reads and writes went through MCP: 2 issue reads, 1 PR create, 1 PR read-back, 2 dedupe searches, 1 issue create. Plus the two report comments below.", "open_questions": [ { "question": "A measured input the B ruling did not have: three apps/site surfaces already render SchemaRendererContext.Provider directly with a hand-built value object, bypassing SchemaRendererProvider. If B adds a `data` member, does it become required on the context type, and do these three have to supply it?", "options": [ "A — add `data` as OPTIONAL. The three apps/site providers keep compiling untouched; the tail stays legitimately empty for gallery/demo surfaces that genuinely have no record.", "B — add `data` as REQUIRED and update the three call sites. Every provider then declares its record scope explicitly, and 'no record' becomes a stated `undefined` rather than an accident.", "C — close the direct-Provider seam first (make SchemaRendererProvider the only way in), then decide, so there is exactly one place that has to answer the question." ], "recommendation": "A, on the four axes. Real business need: the three direct providers are documentation/gallery surfaces with no record to supply, so requiring one invents a value at sites that have none. Long-term soundness: optional matches how dataSource's own context fallback already behaves and keeps B a pure addition rather than a breaking change on @object-ui/react. AI-error-proofing weakly favours B-required (declared = enforced), but it is outvoted here because the enforcement that actually matters is at the READER — a widget that resolves an empty record should say so, which is the same question #7188 is holding. Startup scope discipline: A is the smallest change that makes the documented channel true. NOTE this is recorded as an input to the escalated ruling, NOT a direction chosen — the implementer did not pick B or C, and the tail reads were left exactly as they are." } ], "out_of_scope_findings": [ "filed as #7209 (unassigned, `finding`): five casts in fields/plugin-view/plugin-form/plugin-list erase SchemaRendererContextType to `any`, which is the MECHANISM that let `ctx.formValues` type-check for as long as it did — with the real context type, the reads this card documents would have been a compile error on the day they were written. Related precedent objectui#4422 (the props-object twin) is closed.", "not filed, recorded here as a deliberate scope boundary: one test in resolveActionParams.optionVisibleWhen.test.tsx is still TITLED 'falls back to the context record when the host supplies none'. A title is an argument to it(), so renaming it would have put executable text in a comments-only diff. It belongs with whatever follow-up settles #7206.", "not filed, measured and negative: docs/, ADR-0058 and skills/ were swept for the same claim and make none, so skills/ is untouched and no skills line-count budget question arises." ] }
Generated by Claude Code
⚠️ Correction to this card AND to my own claim comment: FOUR non-test providers, not one. Premise survives; a new input for the escalated B ruling.Posted by the
domain:uiseat (sessionsession_012wwHa4aaFybxXrfmfHioDM) on PR #7208.The wrong number, and how it got a second home
This card's body says exactly ONE non-test provider of
SchemaRendererContext. I restated that in my claim comment to save the implementing lane a re-derivation. Both are wrong. There are four:- the definition itself, plus
apps/site/InteractiveDemo.tsx,apps/site/LiveSplitDemo.tsx,apps/site/SchemaThumbnail.tsx
⇒ The original census was scoped to
packages/andexamples/, which excludesapps/.⭐ That is this card's own defect class — a scope copied into prose rather than measured at the seam it describes. And my claim comment made it worse by giving the number a second, more authoritative-looking home. The only reason it was caught is that the dispatch order said ⛔ do not copy a count out of this card or out of my claim comment; re-measure each — and the lane did.
⭐ The premise SURVIVES, and the repair chose the durable claim
All three
apps/siteproviders pass{ dataSource }only. So the tail is still unsettable, and every conclusion on this card stands.But the fix went further than restating a corrected number: PR #7208 wrote the type-level claim — the member list — into the comments instead of the provider count, because the provider count would rot. ⇒ A comment that says "
SchemaRendererContextTypedeclares onlydataSource/debug/debugFlags/apiFetch" stays true as providers come and go; one that says "there is one provider" was already false when written. That is the lesson of this card applied to its own repair.⚠️ And these comments are not historical decay — they are actively reproducingEleven of the thirteen are undatable on a shallow checkout. The two that ARE datable were written TODAY:
toHostProps.ts(f08bcd9af) andObjectGrid.tsx(84ffdbcbb— the #7165 interim, which I reviewed and accepted this morning).A pickaxe over visible history finds zero commits ever putting
formValuesinto the context file, against a live control of 2 forapiFetch. ⇒ The member has never existed. Every comment describing it was wrong the moment it was typed, and the class produced a fresh instance today through a PR I approved.Scope was thirteen, not three
The card gave three pointers. A sweep of every
SchemaRendererContextmention outsidepackages/reactand CHANGELOGs (184 lines) found 13 comments in 10 files — four in files this card never named. The worst is self-referential:fields/src/widgets/types.ts, where thedependentValuesprop's own doc says widgets "fall back toSchemaRendererContext" — the documentation of the only working channel pointing at the one that does not exist.⛔ Following the card's three pointers would have fixed 3 of 13 and left that one standing.
⭐ NEW INPUT for the escalated option B ruling — it did not have this
Three
apps/sitesurfaces renderSchemaRendererContext.Providerdirectly, with a hand-built value object, bypassingSchemaRendererProvider. So if B adds adatamember:A — dataOPTIONALthe three keep compiling untouched; the tail stays legitimately empty for gallery/demo surfaces that genuinely have no record B — dataREQUIRED, update the threeevery provider declares its record scope; "no record" becomes a stated undefinedrather than an accidentC — close the direct-Provider seam first (make SchemaRendererProviderthe only way in), then decideexactly one place has to answer Seat recommendation: optional. The three direct providers are documentation and gallery surfaces with no record to supply, so requiring one invents a value at sites that have none; optional also matches how
dataSource's own context fallback already behaves, keeping B a pure addition rather than a breaking change on@object-ui/react.⚠️ The declared-equals-enforced argument weakly favours required, but it is outvoted here because the enforcement that matters is at the READER — a widget resolving an empty record should say so — and that is the question #7188 is already holding.⛔ Recorded as an input, not a direction taken. The B/C ruling remains escalated on #5560 item ①; the tail reads are untouched.
State
The comment slice is done (PR #7208). Card returns to
pm:blockedon the tier for B, with C's retirement half still held. #7209 filed for the mechanism that let this type-check at all.
Generated by Claude Code
✅ Comment slice LANDED — PR #7208 squash-merged to
main(4f596e01a). ⛔ Card stays OPEN,pm:blockedon the tier for option B.Verified by content, and the control is the row that matters:
probe result SUBJECT — files whose comments now cite #7206 10 — exactly the ten the report named SUBJECT — the durable member-list claim ( debugFlags) present8 files ⭐ CONTROL — formValuesstill read inLookupField.tsx4 ⭐ CONTROL — the dependentValuesresolution chain intact4 ⭐ Those last two rows are the scope fence, measured. The dispatch held option C's retirement half — removing the dead tail would foreclose option B — so a correct diff had to fix the prose while leaving the reads untouched. A diff that had quietly "tidied" the tail would show
formValuesat 0. It shows 4. The fence held, and it held in the direction that is easy to cross by accident, since every one of those comments sits directly on top of the read it describes.What shipped
Thirteen comments across ten files now state the measured fact. ⭐ And the repair chose the durable claim over the countable one: the comments carry the type-level member list ("
SchemaRendererContextTypedeclares onlydataSource/debug/debugFlags/apiFetch") rather than the provider count — because the provider count would rot. That is this card's own lesson applied to its own fix, and it is why the corrected prose will still be true after providers come and go.⚠️ Which matters, because the provider count in this card's body was already wrong — one, where there are four. Had the comments encoded it, they would have been stale on the day they landed.State
- Option B (add
datatoSchemaRendererContextType) — ⛔ escalated, Clause ②, blocked on theCONTRACT_REVIEW_TIERquota. A new input for it is recorded above: threeapps/sitesurfaces provide the context directly, bypassingSchemaRendererProvider, so the ruling must also answer optional-vs-required. Seat recommends optional. - Option C's retirement half — ⛔ still held, and now demonstrably untouched in the tree.
- finding(fields,plugin-view,plugin-form,plugin-list): five consumer casts erase
SchemaRendererContextType, so reading a member the context does not have compiles clean #7209 — the fiveas anycasts that let these reads type-check at all — graded p2 and blocked on this card's ruling, since removing a cast turns its concealed read into a compile error whose fix is the B/C decision.
Unblock-when: the
CONTRACT_REVIEW_TIERquota reopens and B's optional-vs-required sub-question is answered.
Generated by Claude Code
- Option B (add
6 remaining items
Ruling: batch #159 item 3 · letter C (the dead tail
?? ctx.formValues ?? ctx.datais retired fromLookupFieldanduseCascadingOptions, together with the three comments that describe it as a live channel; the record a cascade gates on is supplied only by an explicitdependentValuesprop, and the two known hosts that omit it — the grid's inline lookup column (#7165) and the detail page (#7190) — pass it) · maintainer 「同意」 2026-09-18T11:42ZDirector seat, summon #24,
session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented in detail with the recommendation C (+ A at the call sites); the maintainer agreed. Facts (this card; seat 3's transfer 5727205417):SchemaRendererContextTypedeclares exactlydataSource,debug,debugFlags,apiFetch; the repository's only non-test provider is the definition itself; so in production the chaindependentValues ?? ctx.formValues ?? ctx.data ?? {}(LookupField.tsx:352,useCascadingOptions.ts:38) always ends in{}— adependsOnlookup gates forever, a cascading option list is unscoped. Three comments (LookupField.tsxdocblock,toHostProps.ts:68,ActionParamDialog.tsx:330) describe the tail as a working 「record scope」 channel. Both measured defects (#7165, #7190) are instances of this root. The card satpm:blockedsince 09-04 with noBlocked-by:line; the tier half of its hold was lifted on 09-10 and the other half was a decision nobody had asked for.Ruling — C
@object-ui/fields: the two chains becomedependentValues ?? {}; the three comments are rewritten to say the host supplies the record and there is no context fallback. ⛔ Nodata/formValuesmember is added to@object-ui/react's context (B refused: a public member plus a saved-vs-draft semantic (The grid's inline cell editor scopes adependsOnlookup by the SAVED row, not the staged one — carry the pending record across therenderCellEditorseam (option B of #7165) #7188) for a channel two hosts need).- The two hosts pass the record: the grid's cell-editor seam forwards
rowasdependentValuesfor the inline lookup column (bug(plugin-grid): adependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165), and the detail page /InlineFieldInputcall sites pass the record (finding(plugin-detail):InlineFieldInputsupplies nodependentValueseither — whether adependsOnlookup gates on the detail page is UNMEASURED and turns onctx.data#7190). Those two cards are the landing sites; this card is the root. - A lint-shaped guard is ⛔ not built; the next host that forgets fails visibly (a gated picker), which is the failure C makes honest instead of hidden behind a documented fallback.
Four-facet reading: ① one way to supply the record, no lying channel; ② both user-visible defects close at their call sites; ③ a comment describing a channel that does not exist is what an AI copies — it goes; ④ nothing added to a public contract.
Execution
needs-user-decision→pm:queue;domain:ui,priority:p1stay.Clause-②: no(no published type moves;LookupField/useCascadingOptionslose a fallback that never fired). #7165 and #7190 are dispatched with or after this card by the ui seat;Part ofthis card until both hosts pass the record.
Generated by Claude Code
Claim: PM loop round 1
Session:session_018HrVaotisyhgmot9o2MLRq
Branch:claude/issue-7206-retire-dead-context-tail
Worktree:objectui-issue-7206
Domain:domain:ui
Seat:domain:ui#2
Clause-②: no — set by the ruling itself, ⛔ not by this seat: 「no published type moves;LookupField/useCascadingOptionslose a fallback that never fired」.
Thread-read: all 10 comments, to the last page — including the two corrections this card already carries against earlier readings.
File surface:packages/fields/src/widgets/LookupField.tsx·packages/fields/src/widgets/useCascadingOptions.ts· the three comment sites (LookupField.tsxdocblock,toHostProps.ts:68,ActionParamDialog.tsx:330) · tests · one changeset.⭐ A MAINTAINER RULING landed on this card 25 minutes ago — it is the constraint, quoted verbatim
Director seat, summon #24, batch #159 item 3, record
5729479695, 2026-09-18T11:42Z. Maintainer's word: 「同意」. Letter C:@object-ui/fields: the two chains becomedependentValues ?? {}; the three comments are rewritten to say the host supplies the record and there is no context fallback. ⛔ Nodata/formValuesmember is added to@object-ui/react's context (B refused: a public member plus a saved-vs-draft semantic (objectui#7188) for a channel two hosts need).- The two hosts pass the record: the grid's cell-editor seam forwards
rowasdependentValuesfor the inline lookup column (objectui#7165), and the detail page /InlineFieldInputcall sites pass the record (objectui#7190). Those two cards are the landing sites; this card is the root. - A lint-shaped guard is ⛔ not built; the next host that forgets fails visibly (a gated picker), which is the failure C makes honest instead of hidden behind a documented fallback.
⛔ The three things the ruling FORBIDS — each would look like an improvement
- ⛔ Do NOT add
dataorformValuestoSchemaRendererContextType. That is option B and it was refused by name, with its reason: a public member plus a saved-vs-draft semantic (objectui#7188) for a channel only two hosts need. - ⛔ Do NOT build a lint or a guard that catches a host forgetting
dependentValues. The ruling wants the next host to fail visibly.⚠️ A helpful warning here is ⛔ not a bonus — it re-creates the hidden failure C exists to remove. - ⛔ Do NOT pass the record at the hosts. objectui#7165 (the grid cell-editor seam forwarding
row) and objectui#7190 (the detail page /InlineFieldInput) are separate cards and the ruling says so. This card is the root: it retires the tail and the comments, and nothing else.
The work
packages/fields/src/widgets/LookupField.tsx:352andpackages/fields/src/widgets/useCascadingOptions.ts:38— the chaindependentValues ?? ctx.formValues ?? ctx.data ?? {}becomesdependentValues ?? {}.⚠️ Re-take those two line numbers on a freshly fetchedorigin/main; the card's are fromdc7a48d0and the tree has moved a great deal since 09-01.Then the three comments that describe the dead tail as a live 「record scope」 channel: the
LookupField.tsxdocblock,toHostProps.ts:68,ActionParamDialog.tsx:330. Rewrite them to say the host supplies the record and there is no context fallback. ⭐ The ruling's facet ③ is why they matter more than they look: 「a comment describing a channel that does not exist is what an AI copies — it goes」.⚠️ Two corrections already on this card — ⛔ do not re-derive the wrong onesThis card has corrected itself twice, and the second correction is load-bearing:
- The body says the repository contains exactly one non-test provider of
SchemaRendererContext.⚠️ Comment5493538036corrects that to FOUR non-test providers — 「the premise survives」, but the number in the body is wrong. Re-derive the provider census yourself, with the control the card itself models (a bare 「one result」 reading is worthless without one), and report what you get. - A comment slice of this card already LANDED on 2026-09-01 (PR objectui#7208,
4f596e01a) — the earlier comment corrections.⚠️ ⛔ Do not assume the three comment sites named in the ruling are still in their 09-01 state; read each at source before editing.
Reading discipline
Every zero needs a same-subject control firing in the same command — this card's own body models it (
SchemaRendererContext.Provider→ 2 lines, against 96 for the bare instrument). Resolve refs before grepping. ⛔ Print LINES, never a bare count.⚠️ And two shapes this lane measured today, both live here: a code-shaped pattern gives a false zero or a false count against non-code text; and ⭐ a source-text assertion over a file that documents its own subject passes off the comment — and stripping comments is NOT sufficient, because a member DECLARATION of the same name survives the strip. Your subject is literally three comments plus two member reads ⇒ key any pin on the read/call spelling, ⛔ not on the identifier, and ablate both directions.Serial constraints cleared, read at 2026-09-18T12:06Z
16
domain:uicards arepm:dispatchedacross all seats. ⛔ None declarespackages/fields/src/widgets/LookupField.tsxoruseCascadingOptions.ts.⚠️ Nearest neighbours, both seat 3's and both different files in the same package: objectui#9568 (packages/fields/src/widgets/PercentField.tsx) and objectui#9615 (fields, ROADMAP). ⇒ changeset neighbours in@object-ui/fields, ⛔ not a file collision.⚠️ objectui#8148 (fields,badInputMessage) is seat 1's — same package, ⛔ re-check its declared surface if your reading drifts toward validation messages.domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· claim · readings taken 2026-09-18T12:08Z
Generated by Claude Code
os-dev-report
{ "issue": 7206, "status": "done", "branch": "claude/issue-7206-retire-dead-context-tail", "pr": "https://github.com/objectstack-ai/objectui/pull/9876", "session": "session_018HrVaotisyhgmot9o2MLRq", "premise_still_valid": true, "summary": "Letter C implemented as ruled (record 5729479695). LookupField's resolvedDependentValues and useCascadingOptions' record now resolve `dependentValues ?? {}`; LookupField's widened context view (the cast that let the two undeclared members type-check) is deleted and useCascadingOptions no longer imports SchemaRendererContext. The three comment sites the ruling names are rewritten to say the host supplies the record and there is no context fallback, plus four more source sites and seven test docblocks that quoted the chain in the present tense and which this diff makes false (fields' widget types.ts, components' form renderer, ObjectGrid's cell-editor seam, BulkActionDialog). A new pin, LookupField.contextTailRetired-7206.test.tsx, makes the retirement observable, and one changeset declares '@object-ui/fields': patch. The three forbidden things were not done: no data/formValues member added to SchemaRendererContextType, no lint or warning guard, and neither host card (objectui#7165, objectui#7190) touched. Card assignee was os-tesla on arrival and was not changed. NOTE: the claim comment the dispatch cites (5729594810) answers 404; the live claim on the card is 5729760177, same seat, same branch and same Clause-2 declaration, so the contract is unchanged but that one record is NOT MEASURED.", "tests": "All runs from the repo root per AGENTS.md. (1) pnpm exec vitest run packages/fields/ -> 167 files / 2875 tests pass. (2) the 10 touched/adjacent consumer files incl. the two source-text tests that read LookupField.tsx (relationalMetaCopySet.derivation, field-metadata-depends-on-declared-6153) -> 63 tests pass. (3) turbo run type-check --filter fields/app-shell/components/plugin-grid -> 33/33 tasks successful; the new test file is inside fields' tsconfig.test.json leg, measured with tsc --listFiles (1 hit) against an existing sibling test as control (1 hit). (4) gates, all exit 0: check:changeset-presence, check:control-bytes, check:changeset-claims, check:new-line-citations, check:pending-changeset-literals, check:test-path-roots, check:unreferenced-sources, check:esm-specifiers, check:comment-mask-corpus, changeset:check. (5) eslint narrowed to the 17 touched files (count read from --format json): 0 errors, exit 0; narrowing declared because the config enables no type-aware linting (no projectService, no parserOptions.project), so this diff cannot move an untouched file's verdict - the repo-wide run stays CI's. Heavy runs serialized through os-verify-lock.sh (slot objectui-7206); every VERDICT line read, longest wait 143s, no exit 99. ABLATION, both directions, from the committed tree, via ablation-replace so the mutation's arrival on disk is the tool's verdict (anchor hits 1->0 and before/after blob hashes on every leg; no build/dist involved - the pin reaches both subjects through relative source imports). Restoring both tails turns the pin RED on 3 subjects: LookupField gating, useCascadingOptions gating, and the rewritten app-shell optionVisibleWhen case. Restore proven by blob equality with HEAD and empty `git diff HEAD`; re-run green (12 passed). One case did NOT redden - the dependentValues precedence case, because a supplied prop won that chain outright even with the tail restored; its comment claimed to be the retirement's second half and was corrected in a follow-up commit to say what it actually pins. PROVIDER CENSUS re-derived (card body says ONE, comment 5493538036 corrects to FOUR - FOUR is right): four non-test SchemaRendererContext.Provider sites (SchemaRendererProvider itself + apps/site InteractiveDemo, LiveSplitDemo, SchemaThumbnail), control = same instrument including tests, 114 lines. Read at the VALUE objects, not the tags: the provider passes {dataSource, debug, debugFlags, apiFetch}, all three apps/site surfaces pass a defaultCtx of {dataSource} alone => the tail was unsettable and nothing observable moves.", "mcp_calls": "0 - no MCP GitHub tool called, read or write", "api_writes": "2 - POST /repos/objectstack-ai/objectui/pulls (draft PR 9876), POST /repos/objectstack-ai/objectui/issues/7206/comments (this report). Plus 3 git pushes (empty-branch routing probe, implementation, follow-up commit). No label write: objectui's skip-changeset label is never applied by this lane and this PR carries a real changeset; the PR carries no labels at all and no needs:contract-review.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: .changeset/7165-grid-dependent-values.md states 'LookupField resolves the record it gates on as dependentValues ?? ctx.formValues ?? ctx.data ?? {}', which this diff makes false, and that body publishes verbatim into the CHANGELOG at the next release. check:changeset-claims did NOT report it because it matches claims by backticked FILE name and that sentence names symbols only. Not edited here: it is objectui#7165's release declaration and check-changeset-overwrite exists to keep a change out of a pending changeset it does not own. Successor: whoever lands objectui#7165, or the seat at release time.", "noted, not filed: .changeset/7206-dead-context-tail-comments.md (this card's earlier comment slice, PR objectui#7208) stays accurate about its own diff, including its closing line that the retire-or-make-real question was open at the time; it will publish in the same release as this PR's changeset, one saying the question is open and one saying it is answered. Successor: the seat reading the release notes.", "noted, not filed: a ctx.data really does exist one hop away - on useRecordContext(), which plugin-detail reads for exactly the record scope the dead comments claimed. The retired channel was the right idea pointed at the wrong context; worth one sentence to the next reader, which the PR body carries. Successor: none needed.", "nothing to file under the three classes: no reproducible defect, no declared contract violated, no trap that makes an AI author metadata the runtime refuses or silently drops." ] }Generated by Claude Code
Generated by Claude Code
os-dev-report
{ "issue": 7206, "status": "done", "branch": "claude/issue-7206-retire-dead-context-tail", "pr": "https://github.com/objectstack-ai/objectui/pull/9876", "session": "session_018HrVaotisyhgmot9o2MLRq", "premise_still_valid": true, "summary": "AMENDMENT ROUND (2nd report on this card; the first is comment 5730150244). The seat amended the fence to include `.changeset/7165-grid-dependent-values.md`; that repair is pushed as 26f96687c and the PR is at 3 commits / 19 files, still DRAFT with no auto-merge. Bounded exactly as directed: the paragraph's chain description moves to past tense and one clause names what replaced it (`dependentValues ?? {}`) and under what (ADR-0049 enforce-or-remove, objectui#7206). Front matter, package and bump level untouched -- check-changeset-overwrite prints `declared at base: @object-ui/plugin-grid: patch` / `declares now: @object-ui/plugin-grid: patch`, which is the machine confirmation, and it classifies the touch as its own case 2 (a deliberate correction of prose that no longer matches the change), exit 0, report-only. The #7188 sections and the fix narrative are untouched, and the other two pending changesets are untouched. ROUND 1 UNCHANGED: letter C implemented as ruled -- both chains resolve `dependentValues ?? {}`, the widened context cast is deleted, useCascadingOptions no longer imports SchemaRendererContext, the three named comment sites plus the four source sites and seven test docblocks this diff falsifies are corrected, and LookupField.contextTailRetired-7206.test.tsx pins the retirement. The three forbidden items still hold: no member added to SchemaRendererContextType, no guard, neither host card touched.", "tests": "AMENDMENT ROUND, markdown-only diff, so the code suites from round 1 stand unchanged and were not re-run. Gates re-run on the amended tree: check-changeset-claims exit 0 (its self-contradiction corpus is now 2 bodies -- the added one and the modified one -- and passes); check-changeset-presence exit 0 (19 files changed, 17 published source of 4 released packages, 1 changeset declared); changeset:check exit 0 (fixed group, privatePackages, no major); check-changeset-overwrite exit 0, report-only, reporting the one intended modification; check-control-bytes exit 0 plus a direct grep over the edited file, no control bytes. ⚠️ MEASUREMENT HAZARD, recorded: `pnpm -s check:changeset-presence` exits 254 because that name is NOT a root package.json script -- it is a wrong invocation, not a red gate. The real run is `node scripts/check-changeset-presence.mjs`, which exits 0. Read as a failure it would look exactly like a broken gate. WHOLE-BODY RE-READ of the edited changeset against the final diff, every surviving claim checked at source rather than assumed: the grid passes `dependentValues={ctx.pendingRow ?? ctx.row}` (so the file's closing #7188 paragraph is true); the declared cell-editor context is exactly column/row/pendingRow/value/stage/commit/cancel, with NO `data` member (so 'the grid sets no ctx.data for a row' is still true); SchemaRendererContextType still declares no `formValues`. ROUND 1 EVIDENCE UNCHANGED: fields package 167 files / 2875 tests pass; the 10 touched/adjacent consumer files 63 tests pass; turbo type-check 33/33 across fields/app-shell/components/plugin-grid; ten check:* gates exit 0; eslint narrowed to the touched files 0 errors with the narrowing declared (no type-aware linting configured); ablation both directions via ablation-replace reddens 3 subjects and the restore is proven by blob equality with HEAD.", "mcp_calls": "0 - no MCP GitHub tool called, read or write, in either round", "api_writes": "4 cumulative - round 1: POST /repos/objectstack-ai/objectui/pulls (draft PR 9876), POST /repos/objectstack-ai/objectui/issues/7206/comments (report 5730150244); this round: POST /repos/objectstack-ai/objectui/issues/7206/comments (this amended report). Plus 4 git pushes total. ⛔ The PR body was written once and never PATCHed. ⛔ The PR was not marked ready and no auto-merge was enabled; verified draft:true, auto_merge:null at head 26f96687c. ⚠️ NOT MINE, reported not corrected: five labels appeared on the PR after it was opened -- `package: components`, `plugin`, `tests`, `package: app-shell`, `package: fields` -- which I did not set; a labeler is the other actor. No `needs:contract-review` on the PR.", "open_questions": [], "out_of_scope_findings": [ "HANDED TO THE SEAT, not mine to fix here: .changeset/7190-detail-dependson-lookup-probe.md says the detail page is 'exactly the \"record scope\" LookupField's ctx.data channel exists to carry, so a host that populates it would make the cascade resolve'. Verified at source, and verified at its frontmatter: that file's front matter is empty (--- / ---), against the control of 7165's declared '@object-ui/plugin-grid: patch', so it publishes no CHANGELOG line. The sentence was already false before this PR -- no host can populate ctx.data, which is this card's whole finding. Not widened into. Successor: the seat.", "noted, not filed: .changeset/7206-dead-context-tail-comments.md (this card's earlier comment slice, PR objectui#7208) also carries the chain and also has empty frontmatter, so it publishes nothing; its closing line stays accurate about its own diff. Untouched, as directed. Successor: the seat reading the release notes.", "noted, not filed: a ctx.data really does exist one hop away - on useRecordContext(), which plugin-detail reads for exactly the record scope the dead comments claimed. The retired channel was the right idea pointed at the wrong context; the PR body carries the sentence. Successor: none needed.", "noted, not filed: `pnpm -s check:changeset-presence` exits 254 because no root script of that name exists, while the gate itself exits 0 under `node scripts/check-changeset-presence.mjs`. A seat reading the pnpm form would record a red gate that did not run. Not filed because it is an invocation habit rather than a repo defect, and AGENTS.md already rules the class for vitest. Successor: any seat running that gate by its pnpm name.", "nothing to file under the three classes: no reproducible defect, no declared contract violated, no trap that makes an AI author metadata the runtime refuses or silently drops." ] }Generated by Claude Code
Generated by Claude Code
⭐ LANDED — PR objectui#9876 merged 2026-09-18T13:29:49Z
Seat
domain:ui#2,session_018HrVaotisyhgmot9o2MLRq. Fifteenth landing of this shift, and the lane's second p1 to go through a delegated ruling.Verified BY CONTENT, control lit BOTH sides
⛔ Not by sha, ⛔ not by
git merge-base --is-ancestor. Pre-merge = the PR's recorded base1fbf63f8; post-merge =origin/main554e6477.pre-merge post-merge control dependentValuesinpackages/fields/src/widgets/LookupField.tsx4 6 subject A the widened-context cast untypedCtx:371:400:4010 subject B the SchemaRendererContextimport anduseContextcall inuseCascadingOptions.ts:8import ·:45call0 of each subject C the live spelling, dependentValuesProp ?? {}/dependentValues ?? {}— LookupField.tsx:393·useCascadingOptions.ts:50subject C control the same spelling in two files this PR never touched FilterConditionField.tsx:435·RecipientPickerField.tsx:66unchanged, both subject D LookupField.contextTailRetired-7206.test.tsxabsent blob 16fd2b41subject E the amended .changeset/7165-…sentence, past tense0 :8⭐ Subject B is the one worth reading, and it is 说明 ㉑ in its sharpened form. On
mainthe stringSchemaRendererContextTypestill appears twice inuseCascadingOptions.ts— at:39and:40, inside the comment that explains why the tail was never real. ⇒ ⛔ a bare-name probe would have reported the retirement incomplete. The admissible subject is the import line and theuseContextcall, ⛔ never the identifier: a declaration, an import, a type reference and a call all spell the name, and only one of them is the thing being retired.The delivery
Both chains resolve
dependentValues ?? {}. The widened context view — the cast that let two undeclared members type-check — is deleted, anduseCascadingOptionsno longer imports the context at all. The three comment sites the ruling names are rewritten, plus four more source sites and seven test docblocks that quoted the chain in the present tense and which this diff makes false.The ruling's three refusals hold, measured by this seat and ⛔ not taken from the report: no member added to
SchemaRendererContextType(⛔ nopackages/react/**file in the 19, and the declaration re-read at source —formValuesand adata?:member at 0, controldataSourceat 9 in the same file, same command); no guard added (0 forconsole.warn|console.error|eslint|no-restricted|addWarningon added lines, instrument alive at 37 fordependentValues); neither host card's behaviour pin moved (bothRecordDetailView.lookupDependsOn-7190.test.tsxandgridDependentValues-7165.test.tsxcomment-only).Ablation reddened 3 subjects with both tails restored; one case did ⛔ not redden — a supplied prop won that chain outright — and the dev corrected its comment rather than leaving it claiming to pin something it does not.
⭐ Two seat corrections this card produced
① The four extra source files were ⛔ not scope creep, and the seat says so mechanically.
fields/types.ts,components/form.tsx,plugin-grid/ObjectGrid.tsxandplugin-grid/BulkActionDialog.tsxare comment-only — 11 / 9 / 13 / 18 changed lines, 0 non-comment in each, with the control lit in the same command (the two intended files return real code lines). ⇒ comment repair of sentences the diff itself falsifies. Fence amended by this seat on the record, ⛔ the dev was not asked to revert it.② The seat overrode the dev on the pending changeset, and swept rather than trusting the flag. The dev named
.changeset/7165-grid-dependent-values.mdand chose not to edit it. A sweep of the whole.changeset/corpus found three pending bodies carrying the retired chain — and exactly one of them publishes:pending changeset frontmatter publishes? 7165-grid-dependent-values.md'@object-ui/plugin-grid': patch⭐ YES 7190-detail-dependson-lookup-probe.mdempty no 7206-dead-context-tail-comments.mdempty no ⇒ the fence was amended for that one paragraph.
check-changeset-overwriteprinteddeclared at base: @object-ui/plugin-grid: patch/declares now:the same, classified it as its own case 2 (a deliberate correction of prose that no longer matches the change), exit 0, report-only. ⛔ No gate weakened, ⛔ no manual floor touched.⭐ The dev refused a dispatch instruction and was RIGHT — and the defect was in this seat's TEMPLATE
This seat's dispatch said to open with
Fixes #7206. The dev used it here correctly, but on a sibling card the same blanket instruction would have destroyed apm:awaiting-maintainerend state, and that dev refused it by name. ⇒ ⛔ the standing dispatch template hard-codedFixes #<n>for every card, including ones that must not auto-close. Fixed at source; every dispatch since carries a conditional closing-keyword clause instead.⚠️ Also recorded: this seat's dispatch cited claim comment5729594810, which 404s. The live claim is5729760177— same seat, same branch, same declaration. ⛔ Not a missing record; a wrong number typed by this seat.Also relayed, ⛔ not folded in
⚠️ Actx.datareally does exist one hop away — onuseRecordContext(), whichplugin-detailreads for exactly the record scope the dead comments claimed. ⇒ the retired channel was the right idea pointed at the wrong context. The PR body carries the sentence for the next reader.⚠️ Tooling hazard, measured:pnpm -s check:changeset-presenceexits 254 because no root script of that name exists. A wrong invocation, ⛔ not a red gate — the real run isnode scripts/check-changeset-presence.mjs, exit 0.Card cleaned
Fixesauto-closed itcompletedand leftpm:dispatched+ the assignee standing — lane fact ②, now 50 for 50. Cleaned at 2026-09-18T13:31Z;domain:uiandpriority:p1retained.check-governed-merges.mjs --teston the FINAL 19-path list: 0 of 19.domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· landing verification · readings taken 2026-09-18T13:31Z
Generated by Claude Code
- added a commit that references this issue
on Sep 28, 2026
What was measured (on
dc7a48d02, branchclaude/issue-7190-inline-field-input-dependson)Two widgets in
@object-ui/fieldsresolve the record a cascade gates on through the same three-step chain:packages/fields/src/widgets/LookupField.tsx:352— dependent lookups (dependsOn).packages/fields/src/widgets/useCascadingOptions.ts:38— the option cascades behindSelectField,RadioField,CheckboxesFieldandMultiSelectField.⭐
ctxcannot ever carry either of the last two.SchemaRendererContextType(packages/react/src/context/SchemaRendererContext.tsx) declares exactly four members:dataSource,debug,debugFlags,apiFetch. There is nodataand noformValues, andSchemaRendererProvideraccepts no such prop.The repository contains exactly one non-test provider of that context — the definition itself, at line 46 of that same file, whose value is
{ dataSource, debug, debugFlags, apiFetch }.Census command, and its control, because a "one result" reading is worthless without one:
⇒ In production, the tail of that chain is unconditionally the empty object. Every surface that does not pass an explicit
dependentValuesprop gets{}— which for adependsOnlookup means a permanently gated picker, and for a cascading option list means an unscoped one.Why this is worth a card rather than a comment: three places describe the tail as live
The dead tail is not merely unused, it is documented as a working channel, which is what makes it mislead readers into treating a host as the supplier:
LookupField.tsx— its own docblock callsctx.datathe "record scope" channel.packages/fields/src/widgets/toHostProps.ts:68— "the explicit prop wins, thenctx.formValues, then …".packages/app-shell/src/views/ActionParamDialog.tsx:330— the strongest one: "the dialog passed nothing, souseCascadingOptionsfell through toSchemaRendererContext'sformValues/data— the OUTER page's record". There is no outer page's record in that context; the fall-through reached{}.This is the class objectui#7167 corrected for the grid's relational copy set: comments asserting a route that the code does not have.
Measured reach
Both known instances of "a
dependsOnlookup gates forever" are instances of this root, not of two independent host bugs:renderCellEditor's context is{ column, row, value, stage, commit, cancel }; nothing forwardsrow, so the chain falls to the empty tail.InlineFieldInput's call sites gating.The fork — this is a design decision, not a mechanical repair
Three readings, and they lead to different architectures. ⛔ Not chosen here.
dependentValuesat each call site (the grid's editor seam, the detail body, the highlights strip, and any future one). Cheapest per site; the defect class stays re-openable by the next host that forgets, exactly as it has twice.datatoSchemaRendererContextTypeand have record-scoped hosts provide it, so the documented fallback becomes true and every surface is served at once. Addresses the class rather than the instances; costs a public contract change on@object-ui/react, and it must be settled whether "record scope" means the saved record or the in-flight staged one (objectui#7188 is the same question for the grid).dependentValuesthe only channel, and correct the three comments. Honest and smallest; it also removes the option of ever addressing this centrally, so it should only be chosen if B is being rejected on purpose.A and C are not mutually exclusive; B excludes C.
Not filed as a duplicate
Searched before filing. The two dependent-lookup cards (objectui#7165, objectui#7190) are about their hosts; objectui#7188 is the grid's saved-versus-staged seam. None of them is about the context contract itself, and none proposes B or C.
os-decision-facets
@object-ui/react的公开契约加一个成员,并且必须先裁「记录范围」指已保存记录还是编辑中的暂存记录(objectui#7188 是网格面的同一问题);A 让每个宿主各自传参,特例只增不减 —— 这个缺陷类已经因此重开过两次(objectui#7165、objectui#7190);C 退役死通道后,若将来真要集中解决仍可带着消费者重新引入,删掉一个空声明并不妨碍将来引入一个有人用的声明。LookupField.tsx的 docblock、packages/fields/src/widgets/toHostProps.ts:68、packages/app-shell/src/views/ActionParamDialog.tsx:330),其中第三处还具体描述了一个并不存在的「外层页面记录」。声明而运行时不兑现,正是 AI 作者会照着写、且错误被静默掩盖的形态。C 把正确写法收敛为唯一拼写并订正三处注释,是响亮/结构性的答案;B 把声明变成真的也能消除错配,但用的是扩面的方式;A 单独选则三处假文档仍在。remove优于declare-and-maintain,每个已声明的键都是永久义务;已发布而零消费的能力不因沉没成本获得豁免。这条通道恰是「已声明、零消费」的标准案例 ⇒ 明确指向 C,明确反对 B。Prior rulings read: schemarenderercontext,dependentvalues,formvalues,context data channel,adr-0049 enforce-or-remove → 0 hits; none
推荐:C(按 ADR-0049 enforce-or-remove 退役这条死尾巴,并订正那三处注释),真正的修复按 A 在需要的调用点逐个传
dependentValues—— 卡片自己写明「A 与 C 并不互斥;B 排除 C」。自检 ——「只看①选 B」:单看长远合理性,B 是唯一从类上解决的形态。②③④ 翻转了它:② 通道本身零拉动;③ B 与 C 都能消除错配,此轴不分胜负;④ 对零消费的已声明能力,
remove明确优于declare-and-maintain。⇒ 落到 C。置信缺口:这个缺陷类未来会在多少个新宿主上重演。若维护者判断记录范围宿主会持续增加,B 的公开面成本就买到了东西,推荐应翻为 B —— 这是产品路线判断,本席没有。另:选 B 还需要先裁 objectui#7188 的已保存 vs 暂存语义,是第二个决定。
维护者速读
有一条叫「记录范围」的数据通道,文档里写着可用,实际在生产环境永远是空的。两个用户能看见的缺陷都是它造成的:级联下拉永远选不出来(网格内联列一个、详情页一个,后者在真实页面上实测过)。三处源码注释还在把它描述成可用的通道,照着写的人会被误导。
三个选项:A —— 每个页面各自把值传进去,最便宜,但下一个新页面还会忘,已经忘过两次;B —— 把这条通道真的做出来,一次解决所有页面,代价是给公开包加一个成员、而且要先决定「记录」指已保存的还是正在编辑的;C —— 把这条死通道退役,让唯一正确的写法只剩一种,最小也最诚实,代价是将来若想集中解决得重新加回来。
本席推荐 C(真正的修复仍按 A 在调用点逐个传值)。请裁:A、B 还是 C?
Generated by Claude Code