You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
platform-objects/ui: options declared on a text field is honoured by analytics dimension labels and grid grouping but not by the text cell, so one declaration reads two ways #28792
Filing gate: ① the finding the director's ruling on #27855 (comment 6556744206, letter C, maintainer 「28590 27855 同意」 2026-11-01T10:29Z) carries to the server seat for the enforce-or-remove channel. Filed by domain:server seat 5. Graded by this seat: p3 (no shipped object is known to rely on the split; a display inconsistency, not a wrong stored value), under 「定级你可以自己决定」 (Leehom, 2026-11-01).
Reach: any author who declares options on a text field (the spec allows options on every field type).
Reader who acts: a domain:server seat (packages/lint or the console text cell under ui/, depending on the route chosen).
What happens (read, not measured)
Analytics resolves a dimension's display label from a declared non-empty options list on ANY field type, deliberately (packages/services/service-analytics/src/dimension-labels.ts, header lines 9–16: "the condition is the declared list itself, never the field's type").
Grid grouping reads the same list for its group headers (per the ruling's reading).
The text cell renders the stored value and ignores options.
So a text field with options shows backlog in its cell and Backlog in a chart or group header. The declaration is honoured at two faces and silently ignored at the third.
Wanted (enforce or remove; the route is the dispatching seat's)
Either:
enforce: the text cell renders the matching option label when the field declares options (stored value untouched; unmatched values render as themselves), or
remove: authoring refuses or warns on options declared on a type whose main face does not read them, with the analytics and grouping faces left as they are or aligned.
Measure first: how many shipped objects (packages/platform-objects, examples/**, platform apps) declare options on a text field. The #27855 ruling chose NOT to add display-only options to sys_notification.topic, so that field is not a reason to enforce.
Dedupe: searched options text field label and options "text field" dimension; closed #18923 is the dimension-labels header wording only. New.
Claim: PM loop round 4 · card thread · 2026-11-01T20:52Z
Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5kmCTHo1Zn4w3D8EBp
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-00j4m6 (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#2
Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. With 2026-11-01T12:11Z 「定级你可以自己决定」; graded p3 by seat 5 at filing and handed to seat 2. Seat's route: enforce (the read-only text cell renders a declared option's label), not remove.
File surface: the read-only text display cell under ui/ (one declared-list test, shared with grouping if a helper exists); its tests; changeset
Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: medium
Clause-②: no expected (a console rendering change, no published member, no spec path); the dev measures and declares
Metadata-protocol: none (expected; the dev measures and declares)
Responsibility: the dev measures shipped text fields declaring options, then makes the text cell render the matching option label (stored value untouched, unmatched as itself), red first with ablation; the seat reviews and lands
Thread-read: none
Premise: named by the #27855 ruling (6556744206) and filed by seat 5
Serial constraints cleared: none; overlap re-read on the cell file before pr_create
Resume-from: none — fresh from origin/main
State: → pm:dispatched, assigned to os-project-manager, recorded by this claim.
os-dev-report
{
"issue": 28792,
"status": "done",
"branch": "claude/project-thread-00j4m6",
"pr": "#28817",
"session": "session_01FEkQ5kmCTHo1Zn4w3D8EBp",
"premise_still_valid": true,
"summary": "Route enforce, as ruled. TextCellRenderer (ui/packages/fields/src/index.tsx) now draws the matching option's label when the field carries a non-empty options array (the analytics test, Array.isArray(options) && options.length above 0) and the stored value is a string or number; unmatched values read as themselves, no list or an empty list is byte-identical, arrays and expanded records keep the coerced text, edit inputs and forms untouched. The select cell's matcher (exact, then case-insensitive) moved to a module-private findDeclaredOption that both cells call; labels arrive already translated because generateColumns and DetailSection run translateOptions over any non-empty list whatever the type. Stop condition not met: options reach the cell through the existing CellRendererProps.field prop; TextFieldMetadata does not name options, so the cell reads it structurally the way LookupCellRenderer reads its static options, with no published prop or type widened. Measured shipped count at base c064d3a: 0 non-select fields declare options (platform-objects 0 of 8 option declarations, examples 0 of 53, packages/apps 0 of 0, plugins+services 0 of 32; hotcrm a598256 0 fields of 80), positive control 2 of 2 hits. Assignee untouched; PR carries no assignees.",
"tests": "Red first at base: TextCellRenderer.declaredOptions-28792.test.tsx committed alone (bb5573c) failed 2 of 7 ('expected backlog to be Backlog', 'expected in_progress to be In progress'), 5 controls green. Green at 32dc019: 7/7; with cell-truncation, formulaFaces.textFace-12357, fieldFaceSeam-13216, select-label-association-e2e: 5 files 39/39. Full fields suite at 7e05456 (pnpm --dir ui exec vitest run --maxWorkers=2 packages/fields/): Test Files 292 passed, 1 skipped; Tests 4399 passed, 7 skipped; lock VERDICT command-exit 0. Type-check ui/packages/fields pnpm run type-check (includes tsconfig.test.json over src/**/*.test.tsx): exit 0 after building the @object-ui/fields^... closure. eslint on both changed files: 0 errors. Ablation from committed HEAD 32dc019 via scripts/ablation-replace.mjs: final return text option-label branch replaced with String(safe) plus marker; anchor 1 to 0, marker 0 to 1 (grep -c 1 on disk), blob 630a20cf9f49 to 5fee7b9df6c0; test file 2 failed 5 passed (exactly the label cases); restored blob equals HEAD 630a20cf9f49, git diff HEAD empty. No build between mutation and run: the ui suite imports the cell from source (../index).",
"gates": "Local: node scripts/pm/dispatch-gates.mjs --commands at head 7e05456 derived 34 families; all 34 run with exit captured before any pipe, all exit 0; --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED (a DERIVED zero, all 34 recorded an exit code). Record: /mnt/project-files/pm-orders/server/28792-gates-ran.txt. CI families: read from PR 28817 check-runs; in_progress at report time (PR opened minutes ago), not re-run locally. test:repo not run locally (diff touches no repo-project test), declared to CI.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "6 — issue_read x3 (get, get_comments x2), list_pull_requests x1, pull_request_read get x1, ToolSearch loads excluded; no MCP write tool",
"api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, run 41352855015, read-back identical, no labels, no assignees); this os-dev-report comment via post-stamped (POST /repos//issues/28792/comments)",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: TextField readonly branch (read-only form face) still prints the stored value for a text field with options; edit inputs and forms are out of scope by the order; no shipped field declares the list, so reach is none · carrier: none",
"noted, not filed: the cell reuses the select matcher (case-insensitive), while analytics and grid grouping match exactly, so a stored BACKLOG reads Backlog in the cell and BACKLOG in a group header, the same split the select cell already has · carrier: none"
]
}
Activity
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
Claim: PM loop round 4 · card thread · 2026-11-01T20:52Z
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01FEkQ5kmCTHo1Zn4w3D8EBpAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-00j4m6(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. With 2026-11-01T12:11Z 「定级你可以自己决定」; graded p3 by seat 5 at filing and handed to seat 2. Seat's route: enforce (the read-only text cell renders a declared option's label), not remove.
File surface: the read-only text display cell under
ui/(one declared-list test, shared with grouping if a helper exists); its tests; changesetContainer & model:
M,mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: mediumClause-②: no expected (a console rendering change, no published member, no spec path); the dev measures and declares
Metadata-protocol: none (expected; the dev measures and declares)
Responsibility: the dev measures shipped text fields declaring options, then makes the text cell render the matching option label (stored value untouched, unmatched as itself), red first with ablation; the seat reviews and lands
Thread-read: none
Premise: named by the #27855 ruling (6556744206) and filed by seat 5
Serial constraints cleared: none; overlap re-read on the cell file before pr_create
Resume-from: none — fresh from
origin/mainState: →
pm:dispatched, assigned toos-project-manager, recorded by this claim.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_01FEkQ5kmCTHo1Zn4w3D8EBp
os-dev-report
{
"issue": 28792,
"status": "done",
"branch": "claude/project-thread-00j4m6",
"pr": "#28817",
"session": "session_01FEkQ5kmCTHo1Zn4w3D8EBp",
"premise_still_valid": true,
"summary": "Route enforce, as ruled. TextCellRenderer (ui/packages/fields/src/index.tsx) now draws the matching option's label when the field carries a non-empty options array (the analytics test, Array.isArray(options) && options.length above 0) and the stored value is a string or number; unmatched values read as themselves, no list or an empty list is byte-identical, arrays and expanded records keep the coerced text, edit inputs and forms untouched. The select cell's matcher (exact, then case-insensitive) moved to a module-private findDeclaredOption that both cells call; labels arrive already translated because generateColumns and DetailSection run translateOptions over any non-empty list whatever the type. Stop condition not met: options reach the cell through the existing CellRendererProps.field prop; TextFieldMetadata does not name options, so the cell reads it structurally the way LookupCellRenderer reads its static options, with no published prop or type widened. Measured shipped count at base c064d3a: 0 non-select fields declare options (platform-objects 0 of 8 option declarations, examples 0 of 53, packages/apps 0 of 0, plugins+services 0 of 32; hotcrm a598256 0 fields of 80), positive control 2 of 2 hits. Assignee untouched; PR carries no assignees.",
"tests": "Red first at base: TextCellRenderer.declaredOptions-28792.test.tsx committed alone (bb5573c) failed 2 of 7 ('expected backlog to be Backlog', 'expected in_progress to be In progress'), 5 controls green. Green at 32dc019: 7/7; with cell-truncation, formulaFaces.textFace-12357, fieldFaceSeam-13216, select-label-association-e2e: 5 files 39/39. Full fields suite at 7e05456 (pnpm --dir ui exec vitest run --maxWorkers=2 packages/fields/): Test Files 292 passed, 1 skipped; Tests 4399 passed, 7 skipped; lock VERDICT command-exit 0. Type-check ui/packages/fields pnpm run type-check (includes tsconfig.test.json over src/**/*.test.tsx): exit 0 after building the @object-ui/fields^... closure. eslint on both changed files: 0 errors. Ablation from committed HEAD 32dc019 via scripts/ablation-replace.mjs: final return text option-label branch replaced with String(safe) plus marker; anchor 1 to 0, marker 0 to 1 (grep -c 1 on disk), blob 630a20cf9f49 to 5fee7b9df6c0; test file 2 failed 5 passed (exactly the label cases); restored blob equals HEAD 630a20cf9f49, git diff HEAD empty. No build between mutation and run: the ui suite imports the cell from source (../index).",
"gates": "Local: node scripts/pm/dispatch-gates.mjs --commands at head 7e05456 derived 34 families; all 34 run with exit captured before any pipe, all exit 0; --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED (a DERIVED zero, all 34 recorded an exit code). Record: /mnt/project-files/pm-orders/server/28792-gates-ran.txt. CI families: read from PR 28817 check-runs; in_progress at report time (PR opened minutes ago), not re-run locally. test:repo not run locally (diff touches no repo-project test), declared to CI.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "6 — issue_read x3 (get, get_comments x2), list_pull_requests x1, pull_request_read get x1, ToolSearch loads excluded; no MCP write tool",
"api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, run 41352855015, read-back identical, no labels, no assignees); this os-dev-report comment via post-stamped (POST /repos//issues/28792/comments)",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: TextField readonly branch (read-only form face) still prints the stored value for a text field with options; edit inputs and forms are out of scope by the order; no shipped field declares the list, so reach is none · carrier: none",
"noted, not filed: the cell reuses the select matcher (case-insensitive), while analytics and grid grouping match exactly, so a stored BACKLOG reads Backlog in the cell and BACKLOG in a group header, the same split the select cell already has · carrier: none"
]
}
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
Landing record · #28792 · PR #28817
domain:serverseat 2 ·session_0186vfjHHMsRXC9EUT9hTFMo· 2026-11-01T22:07Z3e05ed1egit merge-base --is-ancestor 3e05ed1e origin/maingit ls-remote origin refs/heads/gh-readonly-queue/*matching 28817git grep -c findDeclaredOption origin/main -- ui/packages/fields/src/index.tsxoptionsdeclared on a text field is honoured by analytics dimension labels and grid grouping but not by the text cell, so one declaration reads two ways #28792): a text field that declaresoptionsnow shows the matching option's label in its read-only cell (lists and record details), as charts and group headers already did. The stored value is untouched, a value that names no option shows as itself, and a field with no list is drawn as before.optionstoday.Release: claim
6563734011(session_01FEkQ5kmCTHo1Zn4w3D8EBp, branchclaude/project-thread-00j4m6) · landed.pm:dispatchedremoved; assignee cleared; the card closed on merge.Generated by Claude Code