Repository navigation
docs(fields,app-shell,components,plugin-grid): the ctx.formValues / ctx.data tail is unsettable, not a live channel - #7208
Conversation
….data tail is unsettable, not a live channel
Thirteen comments across ten files described the tail of
`dependentValues ?? ctx.formValues ?? ctx.data ?? {}` as a live channel that a
host could supply. `SchemaRendererContextType` declares exactly `dataSource`,
`debug`, `debugFlags` and `apiFetch`, and `SchemaRendererProvider` accepts no
other prop, so that tail is unconditionally empty in production -- unsettable
rather than merely unset, because no host can populate a member the type does
not declare. `dependentValues` is today the only channel that can carry a
record. The comments now state that.
Comments only: the diff contains zero non-comment lines, and the reads
themselves are left exactly as they are. Whether the channel should be made
real or retired stays open on the tracking issue and is not answered here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
|
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 |
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
PM review —
|
Part of #7206 — the comment-correction slice only. The A/B/C ruling is escalated and untouched here.
What this changes
Comments, and nothing else. The diff contains zero non-comment lines, measured rather than asserted:
The
?? ctx.formValues ?? ctx.datareads are left exactly as they are — retiring them is option C's half, which would foreclose option B, and B is the recommended direction.packages/reactis not touched at all.The measurement these comments now state
SchemaRendererContextType(packages/react/src/context/SchemaRendererContext.tsx:12) declares exactly four members —dataSource,debug,debugFlags,apiFetch— andSchemaRendererProvideraccepts no other prop. There is nodatamember and noformValuesmember.⇒ The tail of
dependentValues ?? ctx.formValues ?? ctx.data ?? {}is unconditionally empty in production. Unsettable, not merely unset: no host can populate a member the type does not declare.dependentValuesis today the only channel that can carry a record.Every corrected comment states that and cites #7206. None of them asserts a direction: the prose deliberately does not say the tail "will be removed" or that a
datamember "is planned", because neither is decided.Re-derived against the card's assumptions
The card predicted three such comments. I found thirteen, in ten files.
fields/src/widgets/LookupField.tsx(x2)ctx.datathe "record scope" channel, andctx.formValuesa "form-data context provided by @object-ui/react"fields/src/widgets/useCascadingOptions.tsfields/src/widgets/toHostProps.tsctx.formValues, thenctx.data"fields/src/widgets/types.tsSchemaRendererContext"app-shell/src/views/ActionParamDialog.tsxSchemaRendererContext'sformValues/data— the OUTER page's record"plugin-grid/src/components/BulkActionDialog.tsxcomponents/src/renderers/form/form.tsxdependentValueswith a context fallback that onlydataSourcehasplugin-grid/src/ObjectGrid.tsxapp-shell/.../resolveActionParams.optionVisibleWhen.test.tsx(x3)components/.../form-data-source-wiring.test.tsxSchemaRendererContextfallback"Two of these are worth calling out beyond the count:
ActionParamDialog.tsxrecorded a "ruled cost" that was never paid. It said supplying the dialog's values means a predicate naming a row field "no longer resolves against the host page here". Measured, it never resolved against a host page — the tail could not reach one before either. The cost is real as a description of the code shape, but it was not a loss. That paragraph now says so.form.tsxand its sibling test both said two of the three props have a context fallback. OnlydataSourcedoes. That one is not stale framing, it is a wrong member count.A2.2 — verified, and nothing else reads the chain. Exactly two executable readers, both in
@object-ui/fields:LookupField.tsx:372anduseCascadingOptions.ts:47. A repo-widegit grep -n '\.formValues'returns 17 lines, and every one outside those two is prose, a changelog, or a test.A2.3 — the load-bearing claim holds, but its provider number does not. The card and the claim comment both say exactly ONE non-test provider. I count four:
The card's census command was scoped to
packages/ examples/, which excludesapps/. That is exactly the defect class this card is about — a scope copied into prose rather than measured at the seam it describes — so it is recorded here rather than quietly corrected.The premise survives it. All three
apps/siteproviders pass{ dataSource }and nothing else (defaultCtxatInteractiveDemo.tsx:33,LiveSplitDemo.tsx:46,SchemaThumbnail.tsx:38). No provider suppliesdataorformValues, and none could. The comments here therefore make the type-level claim, not the provider-count claim — the member list is what makes the tail unsettable, and it does not rot when someone adds a fifth provider.A2.4 — no stale/wrong split; at least two were wrong on the day. Blame is uninformative for eleven of the thirteen: this checkout is shallow and the context file has exactly one commit, the grafted root
ad0f5f11f. Two are datable and both were written today, hours before this measurement —toHostProps.ts:68(f08bcd9af) andObjectGrid.tsx:3753(84ffdbcbb, the #7165 interim). A pickaxe over the visible history finds zero commits that ever putformValuesin the context file, against a live control:So nothing here is stale-but-once-true within reach of this history.
Verification, on
242a54553All of the following ran on the final commit; the heavy runs ran on byte-identical content (
git diff HEADempty).type-checkfor each —tsc --noEmit && tsc -p tsconfig.test.json, all fourDone, noerror TS. The two edited test files are genuinely in that program, not merely adjacent to it:tsc -p tsconfig.test.json --listFilesreturns 1 hit for each.pnpm --filter PKG exec vitestwas rejected by this repo's own guard (objectui#3378) rather than run — a NOT MEASURED, not a red; from a package directory vitest silently runs the console package's 22 files and reports them as passing.projectServiceand noparserOptions.projectineslint.config.js, against a control that finds 10 rules-bearing lines in the same file), so this diff cannot move the verdict of any file it does not touch. The 10 changed files lint with 0 errors / 358 warnings, and the baseline is identical: linting each file'sorigin/maincontent througheslint --stdin --stdin-filenamegives 358 findings too, and the per-file per-rule histogramsdiffclean. Zero lint findings added or removed. The repo-wide scan stays CI's.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.The changeset has empty frontmatter (releases nothing), which
check-changeset-presencenames as the explicit exemption. Theskip-changesetlabel is a pinned phantom in this repo and was deliberately not applied.Deliberate scope boundaries
resolveActionParams.optionVisibleWhen.test.tsxis still titled "falls back to the context record when the host supplies none". A title is an argument toit(), so renaming it would put executable text in this diff. The corrected comment sits directly above the assertion instead; the rename belongs with the follow-up work on finding(react,fields):SchemaRendererContexthas nodataand noformValuesmember, so the tail ofdependentValues ?? ctx.formValues ?? ctx.datais unconditionally empty in production — and three comments describe it as live #7206.CHANGELOG.mdentries carrying the old framing were left alone — they are the historical record of what was said at the time, and two of them already say "a memberSchemaRendererContextnever had".skills/were swept and make no such claim, so nothing there needed changing andskills/is untouched.Generated by Claude Code