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
[Decision] A greyed-out record action can only say 对此记录不可用 — re-ask for an authored disabled reason now that a second app needs it (re-file per #24034 B′) #26619
Filing gate: ② a decision only the maintainer can make — a spec widening that ruling B′ on #24034 closed as not planned (#24018), with the re-file condition it named: "Re-file after GA when a second page needs it, on a measured ask." 18.0.0 went GA on 2026-10-20; the measured ask is below.
⚠️ Drafted on the session's default model because the Fable quota was exhausted (HTTP 429, 2026-10-27 ~10:20Z); the maintainer chose 「现在用当前模型起草」 in the hotclm project thread. Pending Fable re-review before ruling (maintainer rule 2026-10-25 「决策卡应该使用fable」).
Reader who acts: the maintainer rules; on A, domain:spec then the console (ui/).
⛔ Not a claim. priority:p2 · domain:spec · area:records · pm:awaiting-maintainer added. It is a spec widening (an authored disabled reason on a record action), re-filed under #24034 B′'s re-file condition. The card says it is pending a Fable re-review before ruling (it was drafted while the Fable quota was exhausted). That re-review is the filer's to run; triage does not grade the letter.
Director seat, summon #51 continuation (ledger objectstack#12708), served on claude-fable-5-1 — this reading is the Fable re-review the card's header says it is pending. Presented to the maintainer in batch #413 as item 1 of 3. Seat increment (re-verified on origin/mainfe53769a; the card is not restated):
Half-state read, not written: needs-user-decision and pm:awaiting-maintainer together; the second leaves in the ruling's stroke.
Facets (the card's block, re-read): ① a greyed action that says why is the terminal shape (Salesforce Dynamic Actions, ServiceNow UI Action conditions); without the key every app paints its own reason into description or a field; ② two apps, both measured; ③ display-only — a wrong reason never changes the server's refusal; ④ one permanent I18nLabel key, paired with disabled, translation slot beside confirmText.
Ruling: batch #413 item 1 · letter A · maintainer 「同意」 2026-10-27T13:10Z
Recorded by the director seat (summon #51 continuation, session_01KrKXQVvgYk2AJ4VkqMobyb, served on claude-fable-5-1 — the Fable re-review the card's header asked for is the brief 6441887048). The maintainer answered batch #413 with 「同意」 — every seat letter. Letter A: ActionSchema gains a display-only, translatable disabled reason beside disabled — an I18nLabel key (the spec lane names it; the card's candidate is disabledReason), empty string refused, translation slot under objects.<object>._actions.<action> with confirmText / successMessage, read by the console's hover title and accessible description of a greyed action, 「对此记录不可用」 as the default when absent. It never enters the server's admission (the refusal stays the hook's / the permission's) and the lint does not read it as a predicate. This re-rules #24034 B′ (6228318638) under the re-file condition B′ itself set — GA shipped (18.0.0, 2026-10-20) and a second consumer measured (hotclm#169, beside cloud#2910). Not B.
Workload (card column): spec · 1 PR (key, i18n extract, lint, docs) + ui · 1 PR (hover / aria) · S each; precedent PR #25643 (+753/−74, 34 files) is larger. Clause-②: yes (widening), Metadata-protocol: added — a protocol addition, so p1 under 「协议新增定 p1」 (6435014712); the Skills-docs: line is owed on the spec PR (6431004737). hotclm#169 lands its greying on disabled now and adds the reason line after the release that carries the key; #24018 stays closed, this card is the execution carrier.
Execution: needs-user-decision → pm:queue in this act (domain:spec, priority:p2 → priority:p1, area:records); pm:awaiting-maintainer removed in the same stroke. Ruling-ref: names this comment.
Claim: PM loop round 1 · 2026-10-27T14:20Z
Session: session_01WGufzfhobWEgUUrjz4rrbh
Account: os-support-ai
Branch: claude/project-thread-yxepsj (card thread session_01WGufzfHuAwrecfjUDtkwcm. The platform assigns the thread's branch and the thread can push only that one, so the branch is written as measured instead of claude/issue-26619-<slug>.)
Worktree: objectstack-issue-26619
Domain: domain:spec
Seat: domain:spec#1
File surface: two PRs, in order. PR 1 (Part of #26619): packages/spec/src/ui/action.zod.ts (an optional, display-only I18nLabel disabled reason beside disabled, the key named by the dev, the empty string refused), the i18n extract slot under objects.<object>._actions.<action> beside confirmText / successMessage, the spec tests and generated artifacts, the docs and skills lines owed under #26369 B, and changesets. PR 2 (Fixes #26619, after PR 1 merges): the console readers of a greyed action's hover title and accessible description, as the dev measures them (at least ui/packages/components/src/renderers/layout/containers.tsxdisabledReasonFor, renderers/action/disabled-reason.tsx, plugin-detailrecord-quick-actions.tsx and app-shellDeclaredActionsBar.tsx), with their tests and a changeset. "Not available for this record" stays the default when the key is absent. The key never enters server admission, and the lint never reads it as a predicate. Stop on a breach and explain it in the report.
Container & model: M, mode:thread, model: TIER_DEFAULT, effort: high
Clause-②: yes (widening)
Metadata-protocol: added
Responsibility: n/a. A decision card, ruled A (re-rules #24034 B′ under its own re-file condition).
Thread-read: 6442737825
Premise: holds (read at origin/main1685a06d: ActionSchema carries disabled and no authored reason, and the console's disabledReasonFor returns only the generic sentence)
Serial constraints cleared: yes. No open PR or claim names action.zod.tsdisabled.
Ruling-ref: 6442737825
{
"issue": 26619,
"status": "done",
"branch": "claude/project-thread-yxepsj",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26786",
"session": "session_01WGufzfHuAwrecfjUDtkwcm",
"premise_still_valid": true,
"summary": "PR 1 of 2 (Part of #26619, the spec half). ActionSchema gains `disabledReason`, an optional display-only I18nLabel beside `disabled`: the string arm refuses '' and whitespace through the shared NON_BLANK_STRING, so the published JSON Schema carries the refusal (minLength 1, pattern \\\\S) and the dropped-refinement ledger gains no row; the map arm is InlineLocaleMapSchema unchanged. Name kept as the card's candidate: it pairs with `disabled`, names the content not one of its three render surfaces, and is the console's own word already (useDisabledReason / disabledReasonFor); disabledMessage / disabledTooltip / disabledHint / disabledText rename onto it. The translation slot sits at objects.OBJECT._actions.ACTION and globalActions.ACTION beside confirmText / successMessage, translateAction overlays it, os i18n extract emits it (pushOptional, nothing seeded when absent), the Studio action form gains a row (4 locales), liveness row `planned` until the console PR; never read by the runtime or the lint. Skills-docs line: follow-up owed (skills/** is Tier H and kept out of the spec half). PR 2 (console, Fixes #26619) waits for this one to merge.",
"tests": "On aa552ced/1d2b0252 under os-verify-lock: spec test Test Files 733 passed (733), Tests 21985 passed 1 todo; spec typecheck exit 0; lint test Test Files 167 passed, Tests 7423 passed (first run red on validate-translation-references.test.ts coverage table, fixed by adding the disabledReason leaf-copy row); lint typecheck exit 0; cli unit i18n-extract + i18n-coverage Test Files 10 passed, Tests 100 passed; cli typecheck exit 0 after fixing 4 TS18048 in the new test; pnpm check:i18n OK; eslint over the 10 changed files exit 0; spec check:generated 2 of 14 stale then --fix (docs, liveness counts). No ablation: the new behaviour is pinned by new tests, not a changed one.",
"gates": "dispatch-gates --commands on c81d8630e (after merging origin/main): 109 families, 106 exit 0, 3 exit 3 PREREQUISITE NOT MET (spec check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage; no full build) = NOT MEASURED locally. --ran: 109 accounted for, 106 run, 3 NOT-MEASURED. CI check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "6 — mcp__github__issue_read x6 (reads only; no MCP write tool used)",
"api_writes": "2 relay strokes — fleet-write pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/26786/assignees), then pr_request_reviewers (POST /pulls/26786/requested_reviewers) with this os-dev-report comment (POST /issues/26619/comments); git push is not a REST write",
"open_questions": [],
"out_of_scope_findings": [
"carrier: seat 1 · Skills-docs follow-up owed: skills/objectstack-ui/rules/actions.md, skills/objectstack-ui/evals/views-apps-actions-pages.json (both at 0 token headroom), skills/objectstack-i18n/SKILL.md (_actions leaf list) · noted, not filed",
"carrier: none · a blank value inside an inline locale map is not refused (InlineLocaleMapSchema as every I18nLabel takes it; refusing it would need a refinement outside the projection's closed list) · noted in Acceptance notes"
]
}
PR #26786 (PR 1 of 2 for this card) merged at 13dc413f. 13dc413f is an ancestor of origin/main. On 24 of the 27 paths the PR touched, the merge tree matches the accepted head c9d6d356 byte for byte. The other 3 differ only by main's own hunks:
An action can now say why it is greyed: disabledReason sits beside disabled in ActionSchema. It is translatable (i18n-extract emits the key, the translation bundle carries the slot, and the reference validator checks it) and documented in actions.mdx. The liveness row stays planned with authorWarn until the console reads it.
Still open on this card: PR 2 (the console renders the reason, Fixes #26619) flips the liveness row to live, drops authorWarn/authorHint, and turns the lint block into a silence pin. The card stays pm:dispatched until PR 2 lands. The skills row is #26820.
{
"issue": 26619,
"status": "done",
"branch": "claude/project-thread-yxepsj",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26869",
"session": "session_01WGufzfHuAwrecfjUDtkwcm",
"premise_still_valid": true,
"summary": "PR 2 of 2 (Fixes #26619, the console half; the spec half #26786 merged at 13dc413f). While an action's DECLARED disabled predicate is TRUE, the record header (inline and overflow, containers.tsx#disabledReasonFor), the record section bar (record-quick-actions.tsx), the declared actions bar (DeclaredActionsBar.tsx) and the four action:* renderers (disabled-reason.tsx#useDisabledReason) show its declared disabledReason as hover title and accessible description (a menu item's second line). 'Not available for this record' stays the default when none or a blank one is declared. Translation: new useObjectLabel.actionDisabledReason (the actionSuffixes pattern, same addresses translateAction and i18n extract use) and useActionTextLocalizer resolve it only when declared: the bundle's sentence, else the authored I18nLabel collapsed to the active language. The action:* renderers read the action as authored and honour the inline map. UIActionSchema.disabledReason is derived from the spec. The liveness row flips to live with the readers as evidence, drops authorWarn/authorHint, and the lint block becomes a silence pin. The describe() texts and the actions page say the console shows it. Changeset: patch on every moved package, Clause-② no, Metadata-protocol none.",
"tests": "Under os-verify-lock on f6a479a30/d96cc5bbb: spec test 735/735 files (22041 tests); lint test 168/168 (7432); spec check:liveness OK; check:generated 2 of 14 stale then --fix (docs, liveness counts); check-doc-authoring exit 0; ui type-check of types, i18n, react, components, plugin-detail and app-shell exit 0 after building @object-ui/app-shell^...; ui vitest over packages/types, i18n, react, components, plugin-detail and app-shell/src/views/__tests__: 1442 files passed, 1 failed (useObjectLabel-identity-5564 member count 27 -> 28, a pin that moves with the new resolver, fixed in d96cc5bbb, 5/5); the five new suites plus the -11811/-11839 siblings 93 tests green; eslint over the 16 changed ui files 0 errors. Ablation: each new suite's declared-reason and bundle cases assert text the pre-change readers never produced.",
"gates": "Derived dispatch-gates not run for this PR; the per-package suites and type-checks above stand in. Known red on main: ui/packages/core actionKeys.pin.test.ts (SPEC_ACTION_KEYS lacks disabledReason since #26786), owned by seat 1's hotfix #26859; this branch takes it by a main merge once it lands and does not add the row. CI check-runs: queued at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "6 — mcp__github__pull_request_read x3, mcp__github__get_job_logs x2 (CI on #26786), subscribe_pr_activity x1; reads and subscriptions only, no MCP write tool used",
"api_writes": "2 relay strokes — fleet-write pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/26869/assignees), then pr_request_reviewers (POST /pulls/26869/requested_reviewers) with this os-dev-report comment (POST /issues/26619/comments); git push is not a REST write",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · the four action:* renderers render `label` and now `disabledReason` as authored (inline locale map only, no bundle lookup); a bundle lookup there needs a key object they do not carry · noted in Acceptance notes"
]
}
PR #26869 (PR 2 of 2, the console half) merged at eab4bbaf, which is an ancestor of origin/main. All 28 paths the PR touched match the accepted head 4b5b87d2 byte for byte in the merge tree.
With both halves in, a greyed action says why it is greyed. disabledReason sits beside disabled on an action, as a string or an inline locale map, and is translatable at objects.OBJECT._actions.ACTION.disabledReason / globalActions.ACTION.disabledReason.
The console shows it wherever an action is greyed: the record header and its overflow menu, record quick actions, the declared actions bar, and the four action:* renderers. A button carries it as a hover title and an accessible description. A menu item shows it as a second line. An action that declares no reason shows "Not available for this record".
The liveness row is live, and the planned-property warning is gone.
The card closed through Fixes #26619, and this seat removes pm:dispatched. The skills row is #26790. Along the way, #26859 restored the actionKeys inventory row that PR 1 left red on main.
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: decision card, intake labels · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T11:03Z⛔ Not a claim.
priority:p2·domain:spec·area:records·pm:awaiting-maintaineradded. It is a spec widening (an authored disabled reason on a record action), re-filed under #24034 B′'s re-file condition. The card says it is pending a Fable re-review before ruling (it was drafted while the Fable quota was exhausted). That re-review is the filer's to run; triage does not grade the letter.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Brief-claimed-by: session_01KrKXQVvgYk2AJ4VkqMobyb · batch #413 · 2026-10-27T12:28Z
Director seat, summon #51 continuation (ledger objectstack#12708), served on
claude-fable-5-1— this reading is the Fable re-review the card's header says it is pending. Presented to the maintainer in batch #413 as item 1 of 3. Seat increment (re-verified onorigin/mainfe53769a; the card is not restated):leave_organizationdeclaresdisabledover a roll-up; no new spec key) and named the re-file condition.packages/spec/src/ui/action.zod.tscarriesdisabled(:89 carry-over, :859 the CEL shorthand) and theconfirmText/successMessagetemplate slots (:881–882), and no reason key (grep disabledReason packages/spec/src→ 0). The second consumer is real: hotclm#169 greys 送审 / 发起签署 / 生效 on conditions that today surface only as a 422 after the click; the first is cloud#2910 (closed, the sole-owner action). Precedent for the workload column holds: PR feat(automation): flow-level requiredPermissions, checked at every flow start door #25643 (requiredPermissionson flows) +753/−74, 34 files; this key is display-only and smaller.needs-user-decisionandpm:awaiting-maintainertogether; the second leaves in the ruling's stroke.descriptionor a field; ② two apps, both measured; ③ display-only — a wrong reason never changes the server's refusal; ④ one permanent I18nLabel key, paired withdisabled, translation slot besideconfirmText.disabledReason: an I18nLabel besidedisabled, display only, empty string refused, translation key underobjects.<object>._actions.<action>, the console's hover and accessible description read it, 「对此记录不可用」 stays the default). Not B (the re-file condition B′ itself set is met; refusing now makes the condition meaningless). Workload: spec · 1 PR (key + i18n extract + lint not treating it as a predicate) + ui · 1 PR (hover/aria) · S each;Clause-②: yes (widening),Metadata-protocol: added→ p1 under the standing yardstick (6435014712) once queued; theSkills-docs:line owed.Prior rulings read: [Decision] cloud#2910 (the organisation page): which of four framework widenings to build — an invite action on sys_organization, an action disabled-reason key, an "only owner" predicate root, a record-scoped leave confirm? #24034 B′ (6228318638), spec: ActionSchema declares
disabledbut no authored reason, so a header action cannot say why it is unavailable (e.g. "You are the only owner; transfer ownership first") #24018 (not_planned under it), 「协议新增定 p1」; ADR-0032 (template slots); thread: none.objectstack-fleet commentedon Oct 27, 2026
Ruling: batch #413 item 1 · letter A · maintainer 「同意」 2026-10-27T13:10Z
Recorded by the director seat (summon #51 continuation,
session_01KrKXQVvgYk2AJ4VkqMobyb, served onclaude-fable-5-1— the Fable re-review the card's header asked for is the brief 6441887048). The maintainer answered batch #413 with 「同意」 — every seat letter. Letter A:ActionSchemagains a display-only, translatable disabled reason besidedisabled— an I18nLabel key (the spec lane names it; the card's candidate isdisabledReason), empty string refused, translation slot underobjects.<object>._actions.<action>withconfirmText/successMessage, read by the console's hover title and accessible description of a greyed action, 「对此记录不可用」 as the default when absent. It never enters the server's admission (the refusal stays the hook's / the permission's) and the lint does not read it as a predicate. This re-rules #24034 B′ (6228318638) under the re-file condition B′ itself set — GA shipped (18.0.0, 2026-10-20) and a second consumer measured (hotclm#169, beside cloud#2910). Not B.Workload (card column): spec · 1 PR (key, i18n extract, lint, docs) + ui · 1 PR (hover / aria) · S each; precedent PR #25643 (+753/−74, 34 files) is larger.
Clause-②: yes (widening),Metadata-protocol: added— a protocol addition, so p1 under 「协议新增定 p1」 (6435014712); theSkills-docs:line is owed on the spec PR (6431004737). hotclm#169 lands its greying ondisablednow and adds the reason line after the release that carries the key; #24018 stays closed, this card is the execution carrier.Execution:
needs-user-decision→pm:queuein this act (domain:spec,priority:p2→priority:p1,area:records);pm:awaiting-maintainerremoved in the same stroke.Ruling-ref:names this comment.objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 1 · 2026-10-27T14:20Z
Session:
session_01WGufzfhobWEgUUrjz4rrbhAccount:
os-support-aiBranch:
claude/project-thread-yxepsj(card threadsession_01WGufzfHuAwrecfjUDtkwcm. The platform assigns the thread's branch and the thread can push only that one, so the branch is written as measured instead ofclaude/issue-26619-<slug>.)Worktree:
objectstack-issue-26619Domain:
domain:specSeat:
domain:spec#1File surface: two PRs, in order. PR 1 (
Part of #26619):packages/spec/src/ui/action.zod.ts(an optional, display-only I18nLabel disabled reason besidedisabled, the key named by the dev, the empty string refused), the i18n extract slot underobjects.<object>._actions.<action>besideconfirmText/successMessage, the spec tests and generated artifacts, the docs and skills lines owed under #26369 B, and changesets. PR 2 (Fixes #26619, after PR 1 merges): the console readers of a greyed action's hover title and accessible description, as the dev measures them (at leastui/packages/components/src/renderers/layout/containers.tsxdisabledReasonFor,renderers/action/disabled-reason.tsx,plugin-detailrecord-quick-actions.tsxandapp-shellDeclaredActionsBar.tsx), with their tests and a changeset. "Not available for this record" stays the default when the key is absent. The key never enters server admission, and the lint never reads it as a predicate. Stop on a breach and explain it in the report.Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: highClause-②: yes (widening)
Metadata-protocol: added
Responsibility: n/a. A decision card, ruled A (re-rules #24034 B′ under its own re-file condition).
Thread-read: 6442737825
Premise: holds (read at
origin/main1685a06d:ActionSchemacarriesdisabledand no authored reason, and the console'sdisabledReasonForreturns only the generic sentence)Serial constraints cleared: yes. No open PR or claim names
action.zod.tsdisabled.Ruling-ref: 6442737825
objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26619, "status": "done", "branch": "claude/project-thread-yxepsj", "pr": "https://github.com/objectstack-ai/objectstack/pull/26786", "session": "session_01WGufzfHuAwrecfjUDtkwcm", "premise_still_valid": true, "summary": "PR 1 of 2 (Part of #26619, the spec half). ActionSchema gains `disabledReason`, an optional display-only I18nLabel beside `disabled`: the string arm refuses '' and whitespace through the shared NON_BLANK_STRING, so the published JSON Schema carries the refusal (minLength 1, pattern \\\\S) and the dropped-refinement ledger gains no row; the map arm is InlineLocaleMapSchema unchanged. Name kept as the card's candidate: it pairs with `disabled`, names the content not one of its three render surfaces, and is the console's own word already (useDisabledReason / disabledReasonFor); disabledMessage / disabledTooltip / disabledHint / disabledText rename onto it. The translation slot sits at objects.OBJECT._actions.ACTION and globalActions.ACTION beside confirmText / successMessage, translateAction overlays it, os i18n extract emits it (pushOptional, nothing seeded when absent), the Studio action form gains a row (4 locales), liveness row `planned` until the console PR; never read by the runtime or the lint. Skills-docs line: follow-up owed (skills/** is Tier H and kept out of the spec half). PR 2 (console, Fixes #26619) waits for this one to merge.", "tests": "On aa552ced/1d2b0252 under os-verify-lock: spec test Test Files 733 passed (733), Tests 21985 passed 1 todo; spec typecheck exit 0; lint test Test Files 167 passed, Tests 7423 passed (first run red on validate-translation-references.test.ts coverage table, fixed by adding the disabledReason leaf-copy row); lint typecheck exit 0; cli unit i18n-extract + i18n-coverage Test Files 10 passed, Tests 100 passed; cli typecheck exit 0 after fixing 4 TS18048 in the new test; pnpm check:i18n OK; eslint over the 10 changed files exit 0; spec check:generated 2 of 14 stale then --fix (docs, liveness counts). No ablation: the new behaviour is pinned by new tests, not a changed one.", "gates": "dispatch-gates --commands on c81d8630e (after merging origin/main): 109 families, 106 exit 0, 3 exit 3 PREREQUISITE NOT MET (spec check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage; no full build) = NOT MEASURED locally. --ran: 109 accounted for, 106 run, 3 NOT-MEASURED. CI check-runs: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "6 — mcp__github__issue_read x6 (reads only; no MCP write tool used)", "api_writes": "2 relay strokes — fleet-write pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/26786/assignees), then pr_request_reviewers (POST /pulls/26786/requested_reviewers) with this os-dev-report comment (POST /issues/26619/comments); git push is not a REST write", "open_questions": [], "out_of_scope_findings": [ "carrier: seat 1 · Skills-docs follow-up owed: skills/objectstack-ui/rules/actions.md, skills/objectstack-ui/evals/views-apps-actions-pages.json (both at 0 token headroom), skills/objectstack-i18n/SKILL.md (_actions leaf list) · noted, not filed", "carrier: none · a blank value inside an inline locale map is not refused (InlineLocaleMapSchema as every I18nLabel takes it; refusing it would need a refinement outside the projection's closed list) · noted in Acceptance notes" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Landed ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-27T18:09ZPR #26786 (PR 1 of 2 for this card) merged at
13dc413f.13dc413fis an ancestor oforigin/main. On 24 of the 27 paths the PR touched, the merge tree matches the accepted headc9d6d356byte for byte. The other 3 differ only bymain's own hunks:content/docs/references/data/object.mdxandpackages/lint/src/lint-liveness-properties.test.tscarry theexternalIdregrade from chore(ui-merge): clear the objectui leftovers after the ui/ merge (#25753) #26598.packages/spec/src/system/translation.zod.tscarries the 2–3 letter locale subtag from feat(spec)!: a single-word language name (Chinese, English, Japanese) is refused as a locale #26770. None of this PR's 6disabledReasonlines in that file moved.An action can now say why it is greyed:
disabledReasonsits besidedisabledinActionSchema. It is translatable (i18n-extractemits the key, the translation bundle carries the slot, and the reference validator checks it) and documented inactions.mdx. The liveness row staysplannedwithauthorWarnuntil the console reads it.Still open on this card: PR 2 (the console renders the reason,
Fixes #26619) flips the liveness row tolive, dropsauthorWarn/authorHint, and turns the lint block into a silence pin. The card stayspm:dispatcheduntil PR 2 lands. The skills row is #26820.objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26619, "status": "done", "branch": "claude/project-thread-yxepsj", "pr": "https://github.com/objectstack-ai/objectstack/pull/26869", "session": "session_01WGufzfHuAwrecfjUDtkwcm", "premise_still_valid": true, "summary": "PR 2 of 2 (Fixes #26619, the console half; the spec half #26786 merged at 13dc413f). While an action's DECLARED disabled predicate is TRUE, the record header (inline and overflow, containers.tsx#disabledReasonFor), the record section bar (record-quick-actions.tsx), the declared actions bar (DeclaredActionsBar.tsx) and the four action:* renderers (disabled-reason.tsx#useDisabledReason) show its declared disabledReason as hover title and accessible description (a menu item's second line). 'Not available for this record' stays the default when none or a blank one is declared. Translation: new useObjectLabel.actionDisabledReason (the actionSuffixes pattern, same addresses translateAction and i18n extract use) and useActionTextLocalizer resolve it only when declared: the bundle's sentence, else the authored I18nLabel collapsed to the active language. The action:* renderers read the action as authored and honour the inline map. UIActionSchema.disabledReason is derived from the spec. The liveness row flips to live with the readers as evidence, drops authorWarn/authorHint, and the lint block becomes a silence pin. The describe() texts and the actions page say the console shows it. Changeset: patch on every moved package, Clause-② no, Metadata-protocol none.", "tests": "Under os-verify-lock on f6a479a30/d96cc5bbb: spec test 735/735 files (22041 tests); lint test 168/168 (7432); spec check:liveness OK; check:generated 2 of 14 stale then --fix (docs, liveness counts); check-doc-authoring exit 0; ui type-check of types, i18n, react, components, plugin-detail and app-shell exit 0 after building @object-ui/app-shell^...; ui vitest over packages/types, i18n, react, components, plugin-detail and app-shell/src/views/__tests__: 1442 files passed, 1 failed (useObjectLabel-identity-5564 member count 27 -> 28, a pin that moves with the new resolver, fixed in d96cc5bbb, 5/5); the five new suites plus the -11811/-11839 siblings 93 tests green; eslint over the 16 changed ui files 0 errors. Ablation: each new suite's declared-reason and bundle cases assert text the pre-change readers never produced.", "gates": "Derived dispatch-gates not run for this PR; the per-package suites and type-checks above stand in. Known red on main: ui/packages/core actionKeys.pin.test.ts (SPEC_ACTION_KEYS lacks disabledReason since #26786), owned by seat 1's hotfix #26859; this branch takes it by a main merge once it lands and does not add the row. CI check-runs: queued at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "6 — mcp__github__pull_request_read x3, mcp__github__get_job_logs x2 (CI on #26786), subscribe_pr_activity x1; reads and subscriptions only, no MCP write tool used", "api_writes": "2 relay strokes — fleet-write pr_create (POST /repos/objectstack-ai/objectstack/pulls + POST /issues/26869/assignees), then pr_request_reviewers (POST /pulls/26869/requested_reviewers) with this os-dev-report comment (POST /issues/26619/comments); git push is not a REST write", "open_questions": [], "out_of_scope_findings": [ "carrier: none · the four action:* renderers render `label` and now `disabledReason` as authored (inline locale map only, no bundle lookup); a bundle lookup there needs a key object they do not carry · noted in Acceptance notes" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Landed ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-27T20:36ZPR #26869 (PR 2 of 2, the console half) merged at
eab4bbaf, which is an ancestor oforigin/main. All 28 paths the PR touched match the accepted head4b5b87d2byte for byte in the merge tree.With both halves in, a greyed action says why it is greyed.
disabledReasonsits besidedisabledon an action, as a string or an inline locale map, and is translatable atobjects.OBJECT._actions.ACTION.disabledReason/globalActions.ACTION.disabledReason.The console shows it wherever an action is greyed: the record header and its overflow menu, record quick actions, the declared actions bar, and the four
action:*renderers. A button carries it as a hover title and an accessible description. A menu item shows it as a second line. An action that declares no reason shows "Not available for this record".The liveness row is
live, and the planned-property warning is gone.The card closed through
Fixes #26619, and this seat removespm:dispatched. The skills row is #26790. Along the way, #26859 restored theactionKeysinventory row that PR 1 left red onmain.