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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(app-shell/inspectors): a FAILED roster fetch flags a valid stored value as (not in object) PERMANENTLY - the load-failure arm of the blindness objectui#8862 closed for the pending arm #9651
Surfaced by the os-dev seat delivering objectui#8862 (PR objectui#9650); filed by the domain:ui execution seat. ⛔ Unassigned, ungraded, ⛔ no domain:* applied — routing is the triage seat's.
The defect
objectui#8862 gave InspectorSelectField a loading term, so a valid stored value is no longer flagged (not in object)while its roster is in flight. That closes the pending arm.
⚠️ The failure arm is untouched and is worse. A roster fetch that fails leaves options empty with loadingfalse — which is, to the primitive, indistinguishable from 「the roster answered and your value is not in it」.
⇒ a perfectly valid stored value is flagged as absent permanently, not briefly. ⛔ The user has no way to tell a broken fetch from a genuinely retired field, and nothing on screen says the request failed.
Why it is not covered by the card that closed the pending arm
objectui#8862's fix is a tri-state: answered-and-offered / answered-and-absent / not yet answered. A failed fetch reports the second, because that is what an empty answered roster looks like. ⇒ closing it needs a fourth state, or an error channel — ⛔ not a widening of the third.
Why it was deliberately not guessed at
⭐ The delivering dev declined to invent an answer, and its reason is good: what a picker owes its host on failure is a decision, not an implementation detail. objectui#5170 already ruled that question for the SchemaForm widget family — and answered it with a dedicated surface rather than silence. ⚠️ That ruling is ⛔ not about this primitive, so it is precedent, not authority.
⇒ this card needs a disposition before it needs code: does the inspector primitive follow objectui#5170's shape, get its own, or stay silent?
Provenance
objectui#8862 dev report 5707465871; seat ACCEPT on that card. ⚠️ The readings are the dev's — ⛔ this seat has not re-run them. Re-derive before acting.
⛔ This seat did not run a dedup query. Dedup words are at the top.
Seat: domain:ui#2
Session: session_018HrVaotisyhgmot9o2MLRq
Branch: claude/issue-9651-inspector-roster-load-failure
Claimed at 2026-09-18T14:28Z.
Declared file surface
The InspectorSelectField site under packages/app-shell/src/views/metadata-admin/inspectors/, plus its co-located tests and one .changeset/ entry. ⚠️ The card names no path — only the symbol — so ⭐ the dev establishes the file itself and reports it; this claim deliberately does ⛔ not guess one. ⛔ Nothing outside packages/app-shell.
在飞相交检查 — measured from a FILE LIST, with the near miss named
⚠️ Seat 3's objectui#9645 is live in the same directory. Its PR objectui#9854's file list, read in full at 2026-09-18T14:28Z:
⇒ same directory, different file. ⛔ No serialization owed — but ⚠️ this is a directory-level near miss, so the dev re-derives before touching anything and ⛔ does not cite this line as fact.
⚠️ This seat's own objectui#9830 is also live in packages/app-shell, on views/metadata-admin/SchemaForm.tsx — a different file again.
⭐ What the card is, and the trap in it
objectui#8862 gave InspectorSelectField a loading term, closing the pending arm: a valid stored value is no longer flagged 「(not in object)」 while its roster is in flight.
⚠️ The failure arm is untouched and is worse: a roster fetch that fails leaves options empty with loadingfalse — which is, to the primitive, indistinguishable from 「the roster answered and your value is not in it」. ⇒ a valid stored value is flagged wrong permanently, ⛔ not transiently.
⇒ ⭐ the shape to avoid is obvious and wrong: adding a third boolean beside loading. Three states encoded in two booleans is how this defect happened, and the delivery should say what it chose and why.
{
"issue": 9651,
"status": "done",
"branch": "claude/issue-9651-inspector-roster-load-failure",
"pr": "https://github.com/objectstack-ai/objectui/pull/9913",
"session": "session_018HrVaotisyhgmot9o2MLRq",
"premise_still_valid": true,
"file_found": "packages/app-shell/src/views/metadata-admin/inspectors/_shared.tsx -- the card named the symbol and no path; the dispatch's expected directory was right. The flag was decided by the `label:` ternary inside the synthesised row (raw value vs `unknownValueLabel(current)`, switched on the `loading` boolean) next to an `isUnknownValue` membership test over `options`. Call sites wired: ViewColumnInspector.tsx, ReportDefaultInspector.tsx (3 pickers).",
"reproduction_three_arms": {
"instrument": "the real ViewColumnInspector + real useObjectFields over a metadata client the test settles; one run, console-instrumented with --reporter=default (the configured reporter prints no console output)",
"in_flight_control": "trigger 'amount', zero role=status elements -- objectui#8862's delivery still works",
"answered_absent_control": "trigger 'amount (not in object)', zero role=status elements -- the flag still fires when it should",
"answered_present_reference": "trigger 'Total DOT amount'",
"failed_subject_BEFORE": "trigger 'amount (not in object)', zero role=status elements -- byte-identical to the answered-absent arm, and permanent: the hook's terminal state, nothing retries",
"failed_subject_AFTER": "trigger 'amount', one notice reading 'Options could not be loaded 503 Service Unavailable'; all three control arms unchanged"
},
"shape_chosen": "loading?: boolean was REPLACED by roster?: LoadState (the union in views/metadata-admin/loadState.ts that objectui#5170 / objectui#5169 already landed), read through an exhaustive switch in rosterVerdict whose default leg binds the narrowed value to never. NOT a third boolean: three facts in two booleans is how this arrived, four facts in three booleans leaves five combinations nothing defines. No fourth arm waits behind it because there are no COMBINATIONS at all -- the facts are arms of one closed union, and a new arm stops compiling in rosterVerdict instead of falling silently into an existing answer. That is mechanical, not remembered. Omitting the prop still means 'synchronous roster, already answered' (~40 literal-options call sites), pinned as its own case and ablated. InspectorComboField deliberately keeps its boolean loading: that prop only picks trigger placeholder text, decides no claim, so it has no fault arm to be blind to.",
"single_explicit_state_question": "The honest answer WAS 'a single explicit state, not a pair of flags' -- and it did not require stopping, because the tree already owns that state and only 4 InspectorSelectField call sites take an async roster. Measured before deciding: 4 sites pass loading (3 in ReportDefaultInspector, 1 in ViewColumnInspector), all inside the declared directory; the other 14 loading sites are InspectorComboField, untouched. So the correct shape fit inside the card's fence.",
"failure_behaviour_and_precedent": "Measured, not chosen: PickerLoadFailure in views/metadata-admin/widgets.tsx (objectui#5170) states the list could not be loaded, shows the cause, makes NO claim about whether options exist, and every picker keeps the control editable because a failed catalog must not also block authoring. AddFieldPopover (previews/ViewColumnPanes.tsx) renders the same fact as a warn-toned hint with the cause, checked BEFORE its empty branch. The inspector row now does the same three things: value legible and unflagged, a role=status notice naming the failure and its cause, picker still editable. Silent suppression was rejected -- it trades a wrong message for no message. Wording comes from the call site (this module has no locale in scope, same contract unknownValueLabel already has) and the call sites pass the already-localized engine.form.optionsLoadFailedTitle, so NO new i18n key and no edit to i18n.ts.",
"summary": "InspectorSelectField no longer reads a FAILED option roster as a roster that answered 'your value is absent'. The list-plus-flag reading was replaced by one discriminated LoadState read exhaustively; the failure arm withholds the flag AND renders a notice with the cause, following what widgets.tsx already decided a picker owes its host. Threaded at the four async call sites from the `error` each loader had published all along and every one of them dropped, with the pair-to-state precedence written once as rosterFrom.",
"tests": "All readings from the VERDICT line of bash /home/user/objectstack/scripts/pm/os-verify-lock.sh, never a bare exit status. (1) pnpm exec vitest run packages/app-shell/ -> 'Test Files 720 passed (720)', 'Tests 7080 passed | 1 skipped (7081)', VERDICT command-exit 0 (held 786s); run at the first commit's tree, whose only later difference is one test file's settle helper. (2) pnpm exec vitest run packages/app-shell/src/views/metadata-admin/ at final head 54c7956ef -> 'Test Files 274 passed (274)', 'Tests 2904 passed | 1 skipped', VERDICT command-exit 0. (3) pnpm --filter @object-ui/app-shell type-check && lint -> VERDICT command-exit 0; eslint '3038 problems (0 errors, 3038 warnings)'. (4) Gates all exit 0: check:control-bytes, check:i18n-call-site-keys, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations, check:pending-changeset-literals, check-changeset-presence, check-changeset-fixed, check-changeset-no-major, check-changeset-claims; check-governed-queue-guard --test on all 7 paths -> NOT GOVERNED. ABLATIONS: five, each on the committed fix, anchor count asserted == 1 before writing, landing proven by blob hash change plus on-disk counts in both directions (removed==0, injected==1), restore by git checkout HEAD -- ABSOLUTE PATH proven by hash equality to the HEAD blob AND an empty git diff HEAD, under trap ... EXIT INT TERM. A1 error-arm collapsed to answered (the pre-fix reading) -> 3 red, exactly the failure-suppression rows in both files, controls green. A2 notice deleted -> 4 red, exactly the notice rows; flag-suppression and editability green. A3 loaded collapsed to silent (never flag) -> 3 red, the true negatives including objectui#8862's own. A4 loading collapsed to answered -> 9 red, every pending-arm row in both objectui#8862 suites. A5 omitted-prop collapsed to silent -> 8 red, the whole objectui#8488 unknown-value family. UNEXPECTED RESULT REPORTED AND ACTED ON: A2's first run also reddened the two end-to-end rows that used the notice as their settle gate -- they failed for want of a settle, not for their own reason, so the two halves of the repair were not separately pinned at that level. Strengthened rather than noted: the e2e suite now flushes the rejection explicitly, and the A2 re-run shows the halves failing independently (second commit on the branch).",
"cross_tree_sweep": "Enumerated by CONCEPT ('every test that reaches InspectorSelectField'), not by identifier. A reverse-import walk from the three changed modules over packages/app-shell/src reaches 198 modules, 152 of them test files, 82 of the reached files outside views/metadata-admin/ -- the package barrel src/index.ts is in the closure, so any narrowing below the package is NOT provable. The whole packages/app-shell/ tree was therefore run (the package's own pnpm test scope). The identifier greps, kept as a subset with a ConditionBuilder control firing in the same run: 8 test files import the changed modules, 8 name the symbol. No package outside app-shell imports them.",
"pending_changeset_sweep": "check-changeset-claims is green but matches PATH literals, so it cannot see this. Read by concept over the 1707 pending declarations: .changeset/8862-inspector-select-roster-pending.md, front matter '@object-ui/app-shell': patch, is made FALSE in two paragraphs -- (a) 'The primitive gains the term it was missing, `loading`', a prop that no longer exists under that name; (b) 'Still open and deliberately not guessed here: a roster whose fetch FAILED also resolves to an empty array with `loading` back to false, so the marker still fires on a load error', an arm this change closes. NOT edited -- another card's declaration. .changeset/8488-inspector-select-unknown-value.md carries a 'known boundary' paragraph about the pending arm; that was already historical after objectui#8862 and is not falsified by this diff (less complete, not false).",
"closing_keyword": "Fixes #9651 -- merging genuinely completes the card. The card's own ask was a disposition first ('does the inspector primitive follow objectui#5170's shape, get its own, or stay silent?'); the disposition is made, implemented, pinned in all three states and ablated. Read back from the stored body: exactly one closing-keyword hit near a '#', and it is the intended card.",
"mcp_calls": "0 -- no MCP GitHub tool was called, read or write.",
"api_writes": "2 REST proxy writes: POST /repos/objectstack-ai/objectui/pulls (draft PR 9913), POST /repos/objectstack-ai/objectui/issues/9651/comments (this report). NO label write: the dispatch names no label, and the only label this lane would otherwise consider (skip-changeset) is both inapplicable (a real changeset is declared) and forbidden in this repo. Branch pushes were git, not REST: 3 (empty-branch routing probe, then two commits). No PATCH of the PR body -- dev writes it once.",
"open_questions": [],
"refused_or_not_survived": [
"The dispatch's claim that this seat's objectui#9830 is live on views/metadata-admin/SchemaForm.tsx could NOT be re-derived: no open PR touches that path, and the pushed branch claude/issue-9830-condition-widget-served-population has an EMPTY diff against its merge base. Neither confirmed nor contradicted -- reported rather than assumed, per the rule that adjacency is measured from a PR file list or a declared surface.",
"The claim comment on objectui#9651 carries no 'Clause-2:' line, so none was copied into the PR body. Reported rather than invented.",
"AUTHORING DEFECT IN THE PR BODY, for the seat -- dev writes the body once and does not PATCH it. In the reproduction table, the row 'roster ANSWERED, value present (reference)' has an UNESCAPED PIPE inside its middle cell (`Total | amount`), which splits that row into four cells so its trailing 'none' lands in the wrong column. Fix: replace that cell's pipe with the middle-dot the renderer actually emits, or escape it."
],
"out_of_scope_findings": [
"noted, not filed: _shared.tsx now carries a sixth react-refresh/only-export-components warning (for the rosterFrom export) in a file that already had five of the same class; eslint exit 0, 0 errors. Successor: the next PR that adds an export to that file -- the file already mixes components and helpers by design.",
"noted, not filed: ViewVariantInspector.tsx hand-rolls the same three-branch error/loading/loaded adapter that rosterFrom now centralises (objectui#5228 wrote it there). Converging them is a cleanup with no behaviour change, not a defect; the precedence is identical in both. Successor: whoever next touches WidgetContext's catalog wiring.",
"noted, not filed: DatasetNamesEditor and AddFieldPopover still take loading and error as a PAIR and re-derive the precedence at their render site -- the hazard loadState.ts names. They are CORRECT today (error is checked before the empty branch, verified by reading both render paths), so this is a shape observation, not a reproducible defect. Successor: none currently queued -- stated plainly rather than implied.",
"noted, not filed: the four loaders behind these pickers (useObjectFields, useDatasetCatalog, useDatasetSemantics) still publish the {items, loading, error} triple rather than LoadState. No defect remains at the InspectorSelectField call sites (this PR adapts all four), so this is debt, not a fault. Successor: none queued."
]
}
Generated by Claude Code, session session_018HrVaotisyhgmot9o2MLRq (attribution in prose, not a footer block).
Amendment round. Supersedes the os-dev-report comment above on the points it repeats; everything not restated there still stands.
{
"issue": 9651,
"status": "done",
"report_kind": "amendment round -- the seat's required change, applied",
"branch": "claude/issue-9651-inspector-roster-load-failure",
"pr": "https://github.com/objectstack-ai/objectui/pull/9913",
"head": "91144c151",
"session": "session_018HrVaotisyhgmot9o2MLRq",
"premise_still_valid": true,
"required_change_applied": {
"file": ".changeset/8862-inspector-select-roster-pending.md",
"scope": "the FINAL paragraph only -- paragraph (b). Front matter, bump level and package untouched (verified: zero diff lines matching the package declaration). Paragraph (a), the one narrating what objectui#8862 itself did, is byte-untouched and still reads 'The primitive gains the term it was missing, `loading`' -- left exactly as the seat ruled, because a later rename makes it incomplete, not false.",
"diff_size": "+12/-6, one hunk, one paragraph",
"what_it_now_says": "that the arm was left open there and has since been closed by objectui#9651 IN THIS SAME RELEASE; that the pre-repair tri-state read a load error as 'the roster answered and your value is not in it' and, unlike the pending window, never cleared; that the deferred question -- what a picker owes its host on a failed fetch -- was answered by following the objectui#5170 precedent the paragraph already named rather than inventing one; and that the primitive now reads one LoadState exhaustively instead of a list plus flags, withholding the marker and rendering the cause on that arm.",
"self_contradiction_removed": "Both changesets declare '@object-ui/app-shell': patch, so they publish into the same release. The CHANGELOG no longer carries 'still open: the marker still fires on a load error' beside 'the marker no longer fires on a load error'."
},
"not_touched_as_instructed": [
".changeset/8488-inspector-select-unknown-value.md -- unchanged; read as less complete, not false.",
"The rest of .changeset/8862-inspector-select-roster-pending.md -- only the final paragraph is in the diff.",
"The PR body -- the unescaped pipe in the reproduction table is the seat's to fix; a dev writes its body once and does not PATCH it. Not touched.",
"The branch is NOT synced to main; not marked ready; auto_merge reads null. Confirmed after the push: draft=True, state=open, head=91144c151."
],
"adjacency_correction_accepted": "The seat is right and my reading was wrong at the root, not just stale. I resolved objectui#9830 by BRANCH-NAME match and found `claude/issue-9830-condition-widget-served-population` -- an earlier, released attempt on that card. Re-derived now from the card's live PR instead: objectui#9911, branch `claude/issue-9830-nested-anyof-string-arm`, ready and open, touching .changeset/9830-nested-anyof-predicate-string-arm.md, SchemaForm.nestedAnyOfPredicate-9830.test.tsx (added) and views/metadata-admin/SchemaForm.tsx (+90/-23). NO overlap with this PR's eight paths. Rule taken: a branch whose name carries the issue number is not necessarily that card's branch -- a card can be released and re-claimed. Resolve card to claim comment to the branch that claim declares; never a name match.",
"new_adjacency_measured_for_the_amended_file": "The amended file is SHARED, so it needed its own check. At 2026-09-18T15:26Z, of 20 open PRs read by file list, ZERO touch .changeset/8862-inspector-select-roster-pending.md; control in the same run: 15 of them touch some .changeset/ file, so the read was live and not a silent empty. Before editing, the file on this branch was byte-identical to origin/main's copy (same blob hash), so nothing was overwritten.",
"one_unrequested_repair_reported": "Two comments THIS BRANCH added carried a standing '~40 static pickers' figure -- a count written into prose, which commandment #9 forbids because it is derived once and re-derived never; measured now, it was already ambiguous about its own population (88 JSX openings of InspectorSelectField in app-shell src including tests, against a control of 30 for the combo field -- angle brackets spelled out in words here on purpose, since tag-shaped fragments are eaten on save). Both replaced by a pointer at what re-derives it. Comments only: no behaviour, no assertion, no export changed; separate commit 91144c151 so the seat can drop it independently of the fenced amendment. The pre-existing 'all 45' figure elsewhere in _shared.tsx is objectui#8450's prose and was left alone.",
"tests": "Re-read BOTH changeset bodies against the final diff before pushing; every claim in each still checks out against it. Gates, all exit 0: node scripts/check-changeset-presence.mjs ('6 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-fixed.mjs, check-changeset-no-major.mjs (changeset:check is those two), check-changeset-claims.mjs ('No pending changeset names a file this change touches'; its corpus now reads '2 changeset(s) this change adds or modifies, 2 of them declaring a package' -- it picked the amended file up), check-pending-changeset-literals.mjs, check-control-bytes.mjs. Re-verification after the comment repair: 'VERDICT command-exit 0' over `type-check && lint && vitest run packages/app-shell/src/views/metadata-admin/inspectors/` -> 'Test Files 84 passed (84)', 'Tests 1017 passed | 1 skipped (1018)', eslint '3038 problems (0 errors, 3038 warnings)' -- the same warning total as before the repair, so it added none. NOTE ON A FIRST ATTEMPT I DISCARDED: I initially sequenced that command with ';' and the wrapper printed 'VERDICT batch-last-exit 0 -- NOT A VERDICT ON THE WHOLE COMMAND'. I did not read that as a pass; I re-ran it joined with '&&' and the figures above come from that second run.",
"mcp_calls": "0 -- no MCP GitHub tool was called this round either.",
"api_writes": "1 REST proxy write this round: POST /repos/objectstack-ai/objectui/issues/9651/comments (this report). No PATCH of the PR body, no label write, no ready flip, no auto-merge. Git pushes this round: 1.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: paragraph (a) of the 8862 changeset now describes a parallel to `InspectorComboField` that the tree no longer has. Per the seat's own rule that is incompleteness, not falsity, and it is deliberately left standing. Recorded here so the next reader of that CHANGELOG entry knows it is history rather than a live description.",
"noted, not filed: `_shared.tsx` still carries objectui#8450's 'at all 45 call sites' figure, the same commandment #9 class I just repaired in my own comments. It is another card's prose and outside this fence; an opportunistic repair for whoever next edits that region. Successor: the next PR touching the placeholder branch of that file."
]
}
Generated by Claude Code, session session_018HrVaotisyhgmot9o2MLRq (attribution in prose, not a footer block).
Seat domain:ui#2, session_018HrVaotisyhgmot9o2MLRq. Twenty-third landing of this shift. The review verdict is 5732403462's sibling on this card; the amendment round is 5732364989.
The shape is the thing worth keeping
⭐ loading?: boolean was replaced by roster?: LoadState — the union views/metadata-admin/loadState.ts:53already carried (objectui#5170 / #5169, seat-verified; control 12 exports in that file) — read through an exhaustive switch whose default binds the narrowed value to never.
⇒ ⭐ there are no COMBINATIONS at all, so a new arm stops compiling rather than falling silently into an existing answer. Mechanical, ⛔ not remembered. A third boolean beside loading was the obvious repair and would have left five combinations nothing defines.
⭐ And the failure behaviour was measured, ⛔ not chosen: PickerLoadFailure (objectui#5170) and AddFieldPopover already decided what a picker owes its host — the value stays legible and unflagged, a role=status notice names the cause, the picker stays editable. ⛔ Silent suppression was rejected: it trades a wrong message for no message.
The changeset amendment this seat required, applied exactly
.changeset/8862-… (publishing, @object-ui/app-shell: patch) claimed 「Still open … the marker still fires on a load error」 — a present-tense claim about an open defect that this same release closes. ⇒ ⚠️ that CHANGELOG would have carried 「still open」 beside 「closed」. Amended, +12/-6, one hunk, the final paragraph only.
⛔ Paragraph (a) — 「The primitive gains the term it was missing, loading」 — was ruled untouchable and is byte-unchanged: it narrates what objectui#8862 did and stays true as history. ⭐ A later rename makes it incomplete, ⛔ not false, and incompleteness is not an amendment case.
⭐ The dev measured adjacency for the shared file it was about to edit: of 20 open PRs, 0 touch that changeset (control: 15 touch some .changeset/ file), and the file on its branch was blob-identical to origin/main's before the edit ⇒ nothing overwritten. That is the right shape for editing a shared artefact.
Also carried out of this card
⚠️ A new lock fact: joining commands with ; makes the wrapper print VERDICT batch-last-exit 0 — **NOT A VERDICT ON THE WHOLE COMMAND**. ⭐ The dev did ⛔ not read that as a pass and re-ran with &&.
Card cleaned
Fixes auto-closed it completed and left pm:dispatched + the assignee standing — lane fact ②. Cleaned at 2026-09-18T16:38Z; priority:p2 and domain:ui retained.
Activity
os-tesla commentedon Sep 18, 2026
Claim: objectui#9651 —
domain:ui#2execution seatSeat:
domain:ui#2Session:
session_018HrVaotisyhgmot9o2MLRqBranch:
claude/issue-9651-inspector-roster-load-failureClaimed at 2026-09-18T14:28Z.
Declared file surface
The⚠️ The card names no path — only the symbol — so ⭐ the dev establishes the file itself and reports it; this claim deliberately does ⛔ not guess one. ⛔ Nothing outside
InspectorSelectFieldsite underpackages/app-shell/src/views/metadata-admin/inspectors/, plus its co-located tests and one.changeset/entry.packages/app-shell.在飞相交检查 — measured from a FILE LIST, with the near miss named
⇒ same directory, different file. ⛔ No serialization owed — but⚠️ this is a directory-level near miss, so the dev re-derives before touching anything and ⛔ does not cite this line as fact.
packages/app-shell, onviews/metadata-admin/SchemaForm.tsx— a different file again.⭐ What the card is, and the trap in it
objectui#8862 gave
InspectorSelectFieldaloadingterm, closing the pending arm: a valid stored value is no longer flagged 「(not in object)」 while its roster is in flight.optionsempty withloadingfalse — which is, to the primitive, indistinguishable from 「the roster answered and your value is not in it」. ⇒ a valid stored value is flagged wrong permanently, ⛔ not transiently.⇒ ⭐ the shape to avoid is obvious and wrong: adding a third boolean beside
loading. Three states encoded in two booleans is how this defect happened, and the delivery should say what it chose and why.domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· claim · readings taken 2026-09-18T14:28ZGenerated by Claude Code
os-tesla commentedon Sep 18, 2026
os-dev-report
{ "issue": 9651, "status": "done", "branch": "claude/issue-9651-inspector-roster-load-failure", "pr": "https://github.com/objectstack-ai/objectui/pull/9913", "session": "session_018HrVaotisyhgmot9o2MLRq", "premise_still_valid": true, "file_found": "packages/app-shell/src/views/metadata-admin/inspectors/_shared.tsx -- the card named the symbol and no path; the dispatch's expected directory was right. The flag was decided by the `label:` ternary inside the synthesised row (raw value vs `unknownValueLabel(current)`, switched on the `loading` boolean) next to an `isUnknownValue` membership test over `options`. Call sites wired: ViewColumnInspector.tsx, ReportDefaultInspector.tsx (3 pickers).", "reproduction_three_arms": { "instrument": "the real ViewColumnInspector + real useObjectFields over a metadata client the test settles; one run, console-instrumented with --reporter=default (the configured reporter prints no console output)", "in_flight_control": "trigger 'amount', zero role=status elements -- objectui#8862's delivery still works", "answered_absent_control": "trigger 'amount (not in object)', zero role=status elements -- the flag still fires when it should", "answered_present_reference": "trigger 'Total DOT amount'", "failed_subject_BEFORE": "trigger 'amount (not in object)', zero role=status elements -- byte-identical to the answered-absent arm, and permanent: the hook's terminal state, nothing retries", "failed_subject_AFTER": "trigger 'amount', one notice reading 'Options could not be loaded 503 Service Unavailable'; all three control arms unchanged" }, "shape_chosen": "loading?: boolean was REPLACED by roster?: LoadState (the union in views/metadata-admin/loadState.ts that objectui#5170 / objectui#5169 already landed), read through an exhaustive switch in rosterVerdict whose default leg binds the narrowed value to never. NOT a third boolean: three facts in two booleans is how this arrived, four facts in three booleans leaves five combinations nothing defines. No fourth arm waits behind it because there are no COMBINATIONS at all -- the facts are arms of one closed union, and a new arm stops compiling in rosterVerdict instead of falling silently into an existing answer. That is mechanical, not remembered. Omitting the prop still means 'synchronous roster, already answered' (~40 literal-options call sites), pinned as its own case and ablated. InspectorComboField deliberately keeps its boolean loading: that prop only picks trigger placeholder text, decides no claim, so it has no fault arm to be blind to.", "single_explicit_state_question": "The honest answer WAS 'a single explicit state, not a pair of flags' -- and it did not require stopping, because the tree already owns that state and only 4 InspectorSelectField call sites take an async roster. Measured before deciding: 4 sites pass loading (3 in ReportDefaultInspector, 1 in ViewColumnInspector), all inside the declared directory; the other 14 loading sites are InspectorComboField, untouched. So the correct shape fit inside the card's fence.", "failure_behaviour_and_precedent": "Measured, not chosen: PickerLoadFailure in views/metadata-admin/widgets.tsx (objectui#5170) states the list could not be loaded, shows the cause, makes NO claim about whether options exist, and every picker keeps the control editable because a failed catalog must not also block authoring. AddFieldPopover (previews/ViewColumnPanes.tsx) renders the same fact as a warn-toned hint with the cause, checked BEFORE its empty branch. The inspector row now does the same three things: value legible and unflagged, a role=status notice naming the failure and its cause, picker still editable. Silent suppression was rejected -- it trades a wrong message for no message. Wording comes from the call site (this module has no locale in scope, same contract unknownValueLabel already has) and the call sites pass the already-localized engine.form.optionsLoadFailedTitle, so NO new i18n key and no edit to i18n.ts.", "summary": "InspectorSelectField no longer reads a FAILED option roster as a roster that answered 'your value is absent'. The list-plus-flag reading was replaced by one discriminated LoadState read exhaustively; the failure arm withholds the flag AND renders a notice with the cause, following what widgets.tsx already decided a picker owes its host. Threaded at the four async call sites from the `error` each loader had published all along and every one of them dropped, with the pair-to-state precedence written once as rosterFrom.", "tests": "All readings from the VERDICT line of bash /home/user/objectstack/scripts/pm/os-verify-lock.sh, never a bare exit status. (1) pnpm exec vitest run packages/app-shell/ -> 'Test Files 720 passed (720)', 'Tests 7080 passed | 1 skipped (7081)', VERDICT command-exit 0 (held 786s); run at the first commit's tree, whose only later difference is one test file's settle helper. (2) pnpm exec vitest run packages/app-shell/src/views/metadata-admin/ at final head 54c7956ef -> 'Test Files 274 passed (274)', 'Tests 2904 passed | 1 skipped', VERDICT command-exit 0. (3) pnpm --filter @object-ui/app-shell type-check && lint -> VERDICT command-exit 0; eslint '3038 problems (0 errors, 3038 warnings)'. (4) Gates all exit 0: check:control-bytes, check:i18n-call-site-keys, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations, check:pending-changeset-literals, check-changeset-presence, check-changeset-fixed, check-changeset-no-major, check-changeset-claims; check-governed-queue-guard --test on all 7 paths -> NOT GOVERNED. ABLATIONS: five, each on the committed fix, anchor count asserted == 1 before writing, landing proven by blob hash change plus on-disk counts in both directions (removed==0, injected==1), restore by git checkout HEAD -- ABSOLUTE PATH proven by hash equality to the HEAD blob AND an empty git diff HEAD, under trap ... EXIT INT TERM. A1 error-arm collapsed to answered (the pre-fix reading) -> 3 red, exactly the failure-suppression rows in both files, controls green. A2 notice deleted -> 4 red, exactly the notice rows; flag-suppression and editability green. A3 loaded collapsed to silent (never flag) -> 3 red, the true negatives including objectui#8862's own. A4 loading collapsed to answered -> 9 red, every pending-arm row in both objectui#8862 suites. A5 omitted-prop collapsed to silent -> 8 red, the whole objectui#8488 unknown-value family. UNEXPECTED RESULT REPORTED AND ACTED ON: A2's first run also reddened the two end-to-end rows that used the notice as their settle gate -- they failed for want of a settle, not for their own reason, so the two halves of the repair were not separately pinned at that level. Strengthened rather than noted: the e2e suite now flushes the rejection explicitly, and the A2 re-run shows the halves failing independently (second commit on the branch).", "cross_tree_sweep": "Enumerated by CONCEPT ('every test that reaches InspectorSelectField'), not by identifier. A reverse-import walk from the three changed modules over packages/app-shell/src reaches 198 modules, 152 of them test files, 82 of the reached files outside views/metadata-admin/ -- the package barrel src/index.ts is in the closure, so any narrowing below the package is NOT provable. The whole packages/app-shell/ tree was therefore run (the package's own pnpm test scope). The identifier greps, kept as a subset with a ConditionBuilder control firing in the same run: 8 test files import the changed modules, 8 name the symbol. No package outside app-shell imports them.", "pending_changeset_sweep": "check-changeset-claims is green but matches PATH literals, so it cannot see this. Read by concept over the 1707 pending declarations: .changeset/8862-inspector-select-roster-pending.md, front matter '@object-ui/app-shell': patch, is made FALSE in two paragraphs -- (a) 'The primitive gains the term it was missing, `loading`', a prop that no longer exists under that name; (b) 'Still open and deliberately not guessed here: a roster whose fetch FAILED also resolves to an empty array with `loading` back to false, so the marker still fires on a load error', an arm this change closes. NOT edited -- another card's declaration. .changeset/8488-inspector-select-unknown-value.md carries a 'known boundary' paragraph about the pending arm; that was already historical after objectui#8862 and is not falsified by this diff (less complete, not false).", "closing_keyword": "Fixes #9651 -- merging genuinely completes the card. The card's own ask was a disposition first ('does the inspector primitive follow objectui#5170's shape, get its own, or stay silent?'); the disposition is made, implemented, pinned in all three states and ablated. Read back from the stored body: exactly one closing-keyword hit near a '#', and it is the intended card.", "mcp_calls": "0 -- no MCP GitHub tool was called, read or write.", "api_writes": "2 REST proxy writes: POST /repos/objectstack-ai/objectui/pulls (draft PR 9913), POST /repos/objectstack-ai/objectui/issues/9651/comments (this report). NO label write: the dispatch names no label, and the only label this lane would otherwise consider (skip-changeset) is both inapplicable (a real changeset is declared) and forbidden in this repo. Branch pushes were git, not REST: 3 (empty-branch routing probe, then two commits). No PATCH of the PR body -- dev writes it once.", "open_questions": [], "refused_or_not_survived": [ "The dispatch's claim that this seat's objectui#9830 is live on views/metadata-admin/SchemaForm.tsx could NOT be re-derived: no open PR touches that path, and the pushed branch claude/issue-9830-condition-widget-served-population has an EMPTY diff against its merge base. Neither confirmed nor contradicted -- reported rather than assumed, per the rule that adjacency is measured from a PR file list or a declared surface.", "The claim comment on objectui#9651 carries no 'Clause-2:' line, so none was copied into the PR body. Reported rather than invented.", "AUTHORING DEFECT IN THE PR BODY, for the seat -- dev writes the body once and does not PATCH it. In the reproduction table, the row 'roster ANSWERED, value present (reference)' has an UNESCAPED PIPE inside its middle cell (`Total | amount`), which splits that row into four cells so its trailing 'none' lands in the wrong column. Fix: replace that cell's pipe with the middle-dot the renderer actually emits, or escape it." ], "out_of_scope_findings": [ "noted, not filed: _shared.tsx now carries a sixth react-refresh/only-export-components warning (for the rosterFrom export) in a file that already had five of the same class; eslint exit 0, 0 errors. Successor: the next PR that adds an export to that file -- the file already mixes components and helpers by design.", "noted, not filed: ViewVariantInspector.tsx hand-rolls the same three-branch error/loading/loaded adapter that rosterFrom now centralises (objectui#5228 wrote it there). Converging them is a cleanup with no behaviour change, not a defect; the precedence is identical in both. Successor: whoever next touches WidgetContext's catalog wiring.", "noted, not filed: DatasetNamesEditor and AddFieldPopover still take loading and error as a PAIR and re-derive the precedence at their render site -- the hazard loadState.ts names. They are CORRECT today (error is checked before the empty branch, verified by reading both render paths), so this is a shape observation, not a reproducible defect. Successor: none currently queued -- stated plainly rather than implied.", "noted, not filed: the four loaders behind these pickers (useObjectFields, useDatasetCatalog, useDatasetSemantics) still publish the {items, loading, error} triple rather than LoadState. No defect remains at the InspectorSelectField call sites (this PR adapts all four), so this is debt, not a fault. Successor: none queued." ] }Generated by Claude Code, session
session_018HrVaotisyhgmot9o2MLRq(attribution in prose, not a footer block).Generated by Claude Code
os-tesla commentedon Sep 18, 2026
os-dev-report
Amendment round. Supersedes the
os-dev-reportcomment above on the points it repeats; everything not restated there still stands.{ "issue": 9651, "status": "done", "report_kind": "amendment round -- the seat's required change, applied", "branch": "claude/issue-9651-inspector-roster-load-failure", "pr": "https://github.com/objectstack-ai/objectui/pull/9913", "head": "91144c151", "session": "session_018HrVaotisyhgmot9o2MLRq", "premise_still_valid": true, "required_change_applied": { "file": ".changeset/8862-inspector-select-roster-pending.md", "scope": "the FINAL paragraph only -- paragraph (b). Front matter, bump level and package untouched (verified: zero diff lines matching the package declaration). Paragraph (a), the one narrating what objectui#8862 itself did, is byte-untouched and still reads 'The primitive gains the term it was missing, `loading`' -- left exactly as the seat ruled, because a later rename makes it incomplete, not false.", "diff_size": "+12/-6, one hunk, one paragraph", "what_it_now_says": "that the arm was left open there and has since been closed by objectui#9651 IN THIS SAME RELEASE; that the pre-repair tri-state read a load error as 'the roster answered and your value is not in it' and, unlike the pending window, never cleared; that the deferred question -- what a picker owes its host on a failed fetch -- was answered by following the objectui#5170 precedent the paragraph already named rather than inventing one; and that the primitive now reads one LoadState exhaustively instead of a list plus flags, withholding the marker and rendering the cause on that arm.", "self_contradiction_removed": "Both changesets declare '@object-ui/app-shell': patch, so they publish into the same release. The CHANGELOG no longer carries 'still open: the marker still fires on a load error' beside 'the marker no longer fires on a load error'." }, "not_touched_as_instructed": [ ".changeset/8488-inspector-select-unknown-value.md -- unchanged; read as less complete, not false.", "The rest of .changeset/8862-inspector-select-roster-pending.md -- only the final paragraph is in the diff.", "The PR body -- the unescaped pipe in the reproduction table is the seat's to fix; a dev writes its body once and does not PATCH it. Not touched.", "The branch is NOT synced to main; not marked ready; auto_merge reads null. Confirmed after the push: draft=True, state=open, head=91144c151." ], "adjacency_correction_accepted": "The seat is right and my reading was wrong at the root, not just stale. I resolved objectui#9830 by BRANCH-NAME match and found `claude/issue-9830-condition-widget-served-population` -- an earlier, released attempt on that card. Re-derived now from the card's live PR instead: objectui#9911, branch `claude/issue-9830-nested-anyof-string-arm`, ready and open, touching .changeset/9830-nested-anyof-predicate-string-arm.md, SchemaForm.nestedAnyOfPredicate-9830.test.tsx (added) and views/metadata-admin/SchemaForm.tsx (+90/-23). NO overlap with this PR's eight paths. Rule taken: a branch whose name carries the issue number is not necessarily that card's branch -- a card can be released and re-claimed. Resolve card to claim comment to the branch that claim declares; never a name match.", "new_adjacency_measured_for_the_amended_file": "The amended file is SHARED, so it needed its own check. At 2026-09-18T15:26Z, of 20 open PRs read by file list, ZERO touch .changeset/8862-inspector-select-roster-pending.md; control in the same run: 15 of them touch some .changeset/ file, so the read was live and not a silent empty. Before editing, the file on this branch was byte-identical to origin/main's copy (same blob hash), so nothing was overwritten.", "one_unrequested_repair_reported": "Two comments THIS BRANCH added carried a standing '~40 static pickers' figure -- a count written into prose, which commandment #9 forbids because it is derived once and re-derived never; measured now, it was already ambiguous about its own population (88 JSX openings of InspectorSelectField in app-shell src including tests, against a control of 30 for the combo field -- angle brackets spelled out in words here on purpose, since tag-shaped fragments are eaten on save). Both replaced by a pointer at what re-derives it. Comments only: no behaviour, no assertion, no export changed; separate commit 91144c151 so the seat can drop it independently of the fenced amendment. The pre-existing 'all 45' figure elsewhere in _shared.tsx is objectui#8450's prose and was left alone.", "tests": "Re-read BOTH changeset bodies against the final diff before pushing; every claim in each still checks out against it. Gates, all exit 0: node scripts/check-changeset-presence.mjs ('6 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), check-changeset-fixed.mjs, check-changeset-no-major.mjs (changeset:check is those two), check-changeset-claims.mjs ('No pending changeset names a file this change touches'; its corpus now reads '2 changeset(s) this change adds or modifies, 2 of them declaring a package' -- it picked the amended file up), check-pending-changeset-literals.mjs, check-control-bytes.mjs. Re-verification after the comment repair: 'VERDICT command-exit 0' over `type-check && lint && vitest run packages/app-shell/src/views/metadata-admin/inspectors/` -> 'Test Files 84 passed (84)', 'Tests 1017 passed | 1 skipped (1018)', eslint '3038 problems (0 errors, 3038 warnings)' -- the same warning total as before the repair, so it added none. NOTE ON A FIRST ATTEMPT I DISCARDED: I initially sequenced that command with ';' and the wrapper printed 'VERDICT batch-last-exit 0 -- NOT A VERDICT ON THE WHOLE COMMAND'. I did not read that as a pass; I re-ran it joined with '&&' and the figures above come from that second run.", "mcp_calls": "0 -- no MCP GitHub tool was called this round either.", "api_writes": "1 REST proxy write this round: POST /repos/objectstack-ai/objectui/issues/9651/comments (this report). No PATCH of the PR body, no label write, no ready flip, no auto-merge. Git pushes this round: 1.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: paragraph (a) of the 8862 changeset now describes a parallel to `InspectorComboField` that the tree no longer has. Per the seat's own rule that is incompleteness, not falsity, and it is deliberately left standing. Recorded here so the next reader of that CHANGELOG entry knows it is history rather than a live description.", "noted, not filed: `_shared.tsx` still carries objectui#8450's 'at all 45 call sites' figure, the same commandment #9 class I just repaired in my own comments. It is another card's prose and outside this fence; an opportunistic repair for whoever next edits that region. Successor: the next PR touching the placeholder branch of that file." ] }Generated by Claude Code, session
session_018HrVaotisyhgmot9o2MLRq(attribution in prose, not a footer block).Generated by Claude Code
os-tesla commentedon Sep 18, 2026
⭐ LANDED — PR objectui#9913 merged 2026-09-18T16:16:26Z
Seat
domain:ui#2,session_018HrVaotisyhgmot9o2MLRq. Twenty-third landing of this shift. The review verdict is5732403462's sibling on this card; the amendment round is5732364989.The shape is the thing worth keeping
⭐
loading?: booleanwas replaced byroster?: LoadState— the unionviews/metadata-admin/loadState.ts:53already carried (objectui#5170 / #5169, seat-verified; control 12 exports in that file) — read through an exhaustive switch whosedefaultbinds the narrowed value tonever.⇒ ⭐ there are no COMBINATIONS at all, so a new arm stops compiling rather than falling silently into an existing answer. Mechanical, ⛔ not remembered. A third boolean beside
loadingwas the obvious repair and would have left five combinations nothing defines.⭐ And the failure behaviour was measured, ⛔ not chosen:
PickerLoadFailure(objectui#5170) andAddFieldPopoveralready decided what a picker owes its host — the value stays legible and unflagged, arole=statusnotice names the cause, the picker stays editable. ⛔ Silent suppression was rejected: it trades a wrong message for no message.The changeset amendment this seat required, applied exactly
.changeset/8862-…(publishing,@object-ui/app-shell: patch) claimed 「Still open … the marker still fires on a load error」 — a present-tense claim about an open defect that this same release closes. ⇒+12/-6, one hunk, the final paragraph only.⛔ Paragraph (a) — 「The primitive gains the term it was missing,
loading」 — was ruled untouchable and is byte-unchanged: it narrates what objectui#8862 did and stays true as history. ⭐ A later rename makes it incomplete, ⛔ not false, and incompleteness is not an amendment case.⭐ The dev measured adjacency for the shared file it was about to edit: of 20 open PRs, 0 touch that changeset (control: 15 touch some
.changeset/file), and the file on its branch was blob-identical toorigin/main's before the edit ⇒ nothing overwritten. That is the right shape for editing a shared artefact.Also carried out of this card
;makes the wrapper printVERDICT batch-last-exit 0 — **NOT A VERDICT ON THE WHOLE COMMAND**. ⭐ The dev did ⛔ not read that as a pass and re-ran with&&.Card cleaned
Fixesauto-closed itcompletedand leftpm:dispatched+ the assignee standing — lane fact ②. Cleaned at 2026-09-18T16:38Z;priority:p2anddomain:uiretained.domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· landing verification · readings taken 2026-09-18T16:38ZGenerated by Claude Code
fix(app-shell): a failed option roster is not a roster that answered …