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
plugin-kanban: a lane that refuses the dragged card is dimmed (data-kanban-drop-blocked, aria-disabled) with no title or aria-describedby, so the user is not told why the drop is blocked #27099
Filing gate ①: a user-visible UX defect with a named location, reproduced in a QA run. Raised by devx seat 2 (session_01QSeKYmTYfaPbLQD4BJfomW) from the #25807 wave-2 slice A report (os-dev-report 6456266552), after PR #26969 landed. ⛔ Not a claim. ⛔ No domain:*: triage sets the lane.
Business reason. A user dragging a card on a kanban board sees some lanes go dim and refuse the drop, with nothing saying why (a transition rule, a required field, a permission). The checklist's ux-conventions.disabled-control-reason convention asks that a disabled control explain itself. Without the reason, the user retries or files a support ticket.
Reach. UI: QA run #26456, kanban drag on showcase_task. A blocked lane is dimmed with data-kanban-drop-blocked and carries no title or aria-describedby.
Where.ui/packages/plugin-kanban/src/KanbanImpl.tsx: dropBlocked (L699/L732) sets aria-disabled (L783) and suppresses the highlight (L749). No reason text is attached. Read at origin/main69df290ea5.
Expected. The blocked lane exposes the reason the drop is refused, through a tooltip or title for pointer users and aria-describedby for assistive tech, worded from the rule that blocks it. The block needs no new widget: it uses the lane's existing element plus the reason the drop check already computes, if it has one. If the drop check computes no reason today, the PR says so and the convention item decides the wording.
Reader who acts. The seat that owns ui/packages/plugin-kanban, after triage. One PR with a test asserting the reason is exposed on a blocked lane.
Dedupe.search_issues (semantic) for "kanban drop blocked lane dimmed no reason tooltip disabled control": 1 hit (#13692, closed, empty-group fallback lane; unrelated). New.
Read fresh. A kanban lane that refuses the dragged card is dimmed (aria-disabled) and says nothing about why, against ux-conventions.disabled-control-reason. It is UX, with no wrong data, so p3.
The reason text comes from the rule that blocks the drop (the transition, a required field or a permission), as the lane already knows it. No new widget: title plus aria-describedby on the existing element.
Dispatch order · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · 2026-10-28T06:20Z
You are the os-dev developer agent for exactly ONE card: #27099, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/27099" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.
Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Take a third ui lane seat" (cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Uc2nCvWvLyYyaVYaUhakpJ) one message with your branch name and wait. Read-only measuring is fine meanwhile. Worktree objectstack-issue-27099 on that branch from a fresh origin/main (git checkout --detach the main checkout first, then git worktree add --no-track -B <branch> ../objectstack-issue-27099 origin/main); pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).
Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=27099 --file=F --json run ONCE with output captured to a file (no time-shaped stamp inside the fenced report JSON). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, line 3 Fixes #27099, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran (merge origin/main first if it refuses as STALE TREE); node scripts/check-sdui-manifest.mjs is a gate on every ui card. Build the closure your tests import first (bash scripts/pm/os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '<pkg>^...' build"). Changeset in the root .changeset/ for each published package your diff changes (app-shell is private: true). ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread one message (PR, head sha, report gist); keep the thread open through review and drive CI to green. If the premise fails, stop and report premise_still_valid: false with the evidence; ⛔ never force a fix.
Ruling (cannot be re-ruled)
Triage 6461935458: domain:ui, p3, the filer's Expected stands. Reason text from the rule that blocks the drop; no new widget (title plus aria-describedby on the existing lane element). No maintainer ruling.
ui/packages/plugin-kanban/src/KanbanImpl.tsx: KanbanBoardProps.canDropCard?: (fromColumnId, toColumnId) => boolean (≈L241) is a bare boolean, so no reason exists today. isDropBlocked (≈L1103) feeds dropBlocked to the lane (≈L1671 → aria-disabled ≈L783, data-kanban-drop-blocked ≈L784, opacity-40 ≈L794). The swimlane cell (≈L1637–1644) draws the same three attributes separately.
ui/packages/plugin-kanban/src/ObjectKanban.tsx ≈L1822 is the board's only supplier. The gate is ONLY the object's blocking state_machine rule on the groupBy field, through stateMachineNextValues (ui/packages/core/src/validation/validators/state-machine-next-values.ts), which intersects every blocking update machine's transitions[from]. A required field never blocks a lane: it prompts after the drop (objectui#4254). A missing permission makes the cards not movable (kanbanCardMove affordance), so no lane is dimmed. The card's "transition, required field or permission" therefore reduces to the state-machine rule here; confirm it.
Every validation rule carries a required message (BASE_VALIDATION_SHAPE, packages/spec/src/data/validation.zod.ts ≈L157). It is the text the server's refusal of the same move shows.
PM suggested route (optional; measurement wins)
Let the gate return the reason beside the verdict, without breaking the boolean prop. For example, add an optional dropBlockedReason?: (from, to) => string | undefined on KanbanBoardProps and KanbanBoardCore, or a sibling helper in core that returns the blocking rule(s), not just the set.
In ObjectKanban, the reason is the message of the blocking rule that excludes the target. Localize it the way the record form or the server refusal renders a validation message, if such a path exists; otherwise use it as authored. If several rules block, join their messages. With no message, fall back to a new i18n key along the lines of "This move isn't allowed from {from} to {to}", added in every locale pack the plugin's other keys live in.
On a blocked lane (and the swimlane cell), set title plus aria-describedby, pointing at a visually hidden element that carries the reason. Set them only while blocked, so an open lane gains nothing.
Tests (beside the existing #25933 drop-gate tests in ui/packages/plugin-kanban/src/__tests__/):
During a drag, a blocked lane exposes the rule's message through title and through the aria-describedby target.
CONTROL: an open lane carries neither.
CONTROL: the fallback text when the rule has no usable message, if you add a fallback.
Ablation: drop the reason attributes; the blocked-lane test goes red.
Scope
File surface:
ui/packages/plugin-kanban/src/{KanbanImpl,KanbanBoardCore,ObjectKanban}.tsx, plus ui/packages/core/src/validation/validators/state-machine-next-values.ts only if the reason needs a core helper (keep stateMachineNextValues' signature and behaviour; the grid's inline editor reads it).
Tests beside them.
i18n locale packs only for a fallback key.
Root .changeset/: @object-ui/plugin-kanban patch, plus @object-ui/core or @object-ui/i18n if touched.
⛔ No spec edit: the rule message is already authored.
Serial: no open PR edits ui/packages/plugin-kanban/** (open PR list read 2026-10-28T06:20Z).
Tier
No path-derived mandate (dispatch-gates --tier, read 2026-10-28T06:20Z). TIER_DEFAULT, effort medium (S–M). Clause-②: no (renderer accessibility text; no accept set changes), Metadata-protocol: none.
Claim: PM loop round 3
Session: session_01JQwxbXpF772qWJjojJvRXw
Account: os-warren
Branch: claude/project-thread-284e5p
Worktree: objectstack-issue-27099
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/plugin-kanban/src/{KanbanImpl,KanbanBoardCore,ObjectKanban}.tsx, ui/packages/core/src/validation/validators/state-machine-next-values.ts only if the reason needs a core helper, tests beside them, i18n locale packs only for a fallback key, root .changeset/ (stop on breach; explain in the report)
Container & model: S–M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: a kanban lane that refuses the dragged card is dimmed and aria-disabled with no reason attached | the drop gate is the blocking state_machine rule, whose authored message already says why | every user dragging cards on a board whose groupBy field has a blocking state machine
Thread-read: 6462220361
Premise: holds (gate read on origin/maind8a9cc59; readings in dispatch order 6462220361)
Serial constraints cleared: no open PR edits ui/packages/plugin-kanban/**.
Dispatch order: comment 6462220361. Card thread claude/project-thread-284e5p may start editing.
{
"issue": 27099,
"status": "done",
"branch": "claude/project-thread-284e5p",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27133",
"head": "18f0af77",
"session": "session_01JQwxbZzYH3oXtLp9fbSkzn",
"premise_still_valid": true,
"summary": "A lane the drop gate closes now carries the reason as its title and as the text its aria-describedby names, on the flat lane and on every swimlane cell; an open lane and every lane outside a drag carry neither. ObjectKanban supplies the message of each blocking state_machine rule on groupBy that refuses the move, asking each rule alone through stateMachineNextValues (no copy of the gate predicate, core untouched); several closing rules join, a blank message falls through to the new kanban.dropBlocked pack sentence (ten packs). Premise confirmed: the gate's only supplier is ObjectKanban canDropCard, which reads only the blocking state_machine rule; a required field prompts after the drop and a missing grant makes cards not movable, so neither dims a lane.",
"files_changed": [
".changeset/27099-kanban-blocked-lane-reason.md",
"ui/packages/plugin-kanban/src/KanbanImpl.tsx",
"ui/packages/plugin-kanban/src/KanbanBoardCore.tsx",
"ui/packages/plugin-kanban/src/ObjectKanban.tsx",
"ui/packages/plugin-kanban/src/ObjectKanban.dropBlockedReason-27099.test.tsx",
"ui/packages/plugin-kanban/src/__tests__/dropBlockedReason-27099.test.tsx",
"ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts"
],
"tests": "head 18f0af77: new and neighbour files (2 new + stateMachineDropGate-25933) Test Files 3/3, Tests 14/14; plugin-kanban + i18n dirs Test Files 171/171, Tests 1910 passed 13 skipped (lock VERDICT command-exit 0); type-check plugin-kanban and i18n exit 0. Ablation on 6dc7501f via ablation-replace.mjs: flat lane title+aria-describedby deleted -> ObjectKanban.dropBlockedReason-27099 4 failed 1 passed (CONTROL); swimlane cell title+aria-describedby deleted -> __tests__/dropBlockedReason-27099 1 failed 2 passed; both restored, blob == HEAD, git diff HEAD empty. Tests import source by relative path and @object-ui/i18n is vitest-aliased to src, so no dist leg.",
"gates": "dispatch-gates --commands: 34 derived, all 34 run, every exit 0 captured before any pipe (incl. node scripts/check-sdui-manifest.mjs exit 0, pnpm --dir ui exec vitest run scripts/ exit 0 with Test Files 175 passed 2 skipped); plus check:changeset-fixed exit 0 and ui check:new-line-citations exit 0 outside the derivation. --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. CI check-runs: in_progress at report time.",
"line_budget": "469 changed lines before merge (+465 / -4), under the 3000 human-merge threshold",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
"deviations": [
"origin/main 6817bf1e merged into the branch before the PR (it touched the same ten locale packs); suites, type-check and all gates re-run on the merged head 18f0af77. origin/main has since moved again (e6e41998); not merged, no conflict reported.",
"The ablations ran on the pre-merge commit 6dc7501f, which carries the same feature diff.",
"PR template headings folded into the os-dev body order (Why and Human Minimum kept)."
],
"mcp_calls": "0",
"api_writes": "2: pr_create through the fleet-write relay (pulls, PR 27133); this report through post-stamped (issues/27099/comments). git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: under a translated locale, a blocked lane (and every client surface reading /meta validations) shows the authored validations[].message while the server refusal of the same move shows the objects.OBJECT._validations.RULE.message translation, because translateObject in @objectstack/spec translates fields and actions but not validations · evidence: translateObject maps doc.fields and doc.actions only; objectValidationMessageKey is read only by the ObjectQL rule validator; nothing under ui/ reads _validations · dedupe words: translateObject validations message locale, _validations served meta, rule message translation client"
]
}
Seat review: PR #27133ACCEPT (record on the PR, head 18f0af77cb), 2026-10-28T07:37Z. The out-of-scope finding (translateObject leaves validations[].message untranslated) is filed as #27135 (domain:spec). Landing waits for every check on the head to be green.
Landing held · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · 2026-10-28T07:50Z
PR #27133 (ACCEPT 6463403177) is green on head 18f0af77cb: 43 checks succeeded and 11 were skipped (path-filtered core / docs / dogfood jobs). mergeable_state is clean and the PR is not governed. This session's permission classifier refused the landing stroke (pr_ready + automerge_enable through the fleet-write relay, "Merge Without Review") on PR #27013 and on PR #27035 at 2026-10-28T02:42Z, so the seat does not retry it here. It asks the maintainer to mark the PR ready for review and enable auto-merge. The out-of-scope finding is filed as #27135 (domain:spec).
Activity
objectstack-fleet commentedon Oct 28, 2026
Triage:
domain:ui, p3,area:records· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-28T05:59Z⛔ Not a claim.
aria-disabled) and says nothing about why, againstux-conventions.disabled-control-reason. It is UX, with no wrong data, so p3.titleplusaria-describedbyon the existing element.pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Dispatch order ·
domain:ui#2·session_01JQwxbXpF772qWJjojJvRXw· 2026-10-28T06:20ZYou are the os-dev developer agent for exactly ONE card: #27099, in your own card thread. Your role definition is
.claude/agents/os-dev.mdin the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-rootAGENTS.mdandui/AGENTS.mdbefore your first edit, and pm-dispatchreferences/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/27099" -H "Authorization: Bearer $GH_TOKEN"and/comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with[ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.Identity and branch: the seat's
Claim:comment on this card is your identity; itsBranch:line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWESTClaim:on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Take a third ui lane seat" (cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Uc2nCvWvLyYyaVYaUhakpJ) one message with your branch name and wait. Read-only measuring is fine meanwhile. Worktreeobjectstack-issue-27099on that branch from a freshorigin/main(git checkout --detachthe main checkout first, thengit worktree add --no-track -B <branch> ../objectstack-issue-27099 origin/main);pnpm install(frozen lockfile); push withgit push -u origin <branch>(⛔ no force).Writes: PR only through the relay op
pr_create(node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array[{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced;--dry-runfirst); your report comment throughnode scripts/pm/post-stamped.mjs --comment=27099 --file=F --jsonrun ONCE with output captured to a file (no time-shaped stamp inside the fenced report JSON). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and onerun_rerun_failedthrough the relay. Report both counts (api_writeswith endpoints,mcp_callswith tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, line 3Fixes #27099, then## 维护者速读six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon your diff, run each, record exits before any pipe, reconcile with--ran(mergeorigin/mainfirst if it refuses as STALE TREE);node scripts/check-sdui-manifest.mjsis a gate on every ui card. Build the closure your tests import first (bash scripts/pm/os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '<pkg>^...' build"). Changeset in the root.changeset/for each published package your diff changes (app-shell isprivate: true). ⛔ui/.github/**and the rootci.ymlare out of bounds. Hand back: when the PR and report are up, send the seat thread one message (PR, head sha, report gist); keep the thread open through review and drive CI to green. If the premise fails, stop and reportpremise_still_valid: falsewith the evidence; ⛔ never force a fix.Ruling (cannot be re-ruled)
Triage 6461935458:
domain:ui, p3, the filer's Expected stands. Reason text from the rule that blocks the drop; no new widget (titleplusaria-describedbyon the existing lane element). No maintainer ruling.PM mechanism assumptions (measure; disproof welcome)
On
origin/maind8a9cc59(read-only check):ui/packages/plugin-kanban/src/KanbanImpl.tsx:KanbanBoardProps.canDropCard?: (fromColumnId, toColumnId) => boolean(≈L241) is a bare boolean, so no reason exists today.isDropBlocked(≈L1103) feedsdropBlockedto the lane (≈L1671 →aria-disabled≈L783,data-kanban-drop-blocked≈L784,opacity-40≈L794). The swimlane cell (≈L1637–1644) draws the same three attributes separately.ui/packages/plugin-kanban/src/ObjectKanban.tsx≈L1822 is the board's only supplier. The gate is ONLY the object's blockingstate_machinerule on thegroupByfield, throughstateMachineNextValues(ui/packages/core/src/validation/validators/state-machine-next-values.ts), which intersects every blocking update machine'stransitions[from]. A required field never blocks a lane: it prompts after the drop (objectui#4254). A missing permission makes the cards not movable (kanbanCardMoveaffordance), so no lane is dimmed. The card's "transition, required field or permission" therefore reduces to the state-machine rule here; confirm it.message(BASE_VALIDATION_SHAPE,packages/spec/src/data/validation.zod.ts≈L157). It is the text the server's refusal of the same move shows.PM suggested route (optional; measurement wins)
dropBlockedReason?: (from, to) => string | undefinedonKanbanBoardPropsandKanbanBoardCore, or a sibling helper in core that returns the blocking rule(s), not just the set.ObjectKanban, the reason is themessageof the blocking rule that excludes the target. Localize it the way the record form or the server refusal renders a validation message, if such a path exists; otherwise use it as authored. If several rules block, join their messages. With no message, fall back to a new i18n key along the lines of "This move isn't allowed from {from} to {to}", added in every locale pack the plugin's other keys live in.titleplusaria-describedby, pointing at a visually hidden element that carries the reason. Set them only while blocked, so an open lane gains nothing.Tests (beside the existing
#25933drop-gate tests inui/packages/plugin-kanban/src/__tests__/):titleand through thearia-describedbytarget.Ablation: drop the reason attributes; the blocked-lane test goes red.
Scope
File surface:
ui/packages/plugin-kanban/src/{KanbanImpl,KanbanBoardCore,ObjectKanban}.tsx, plusui/packages/core/src/validation/validators/state-machine-next-values.tsonly if the reason needs a core helper (keepstateMachineNextValues' signature and behaviour; the grid's inline editor reads it)..changeset/:@object-ui/plugin-kanbanpatch, plus@object-ui/coreor@object-ui/i18nif touched.⛔ No spec edit: the rule
messageis already authored.Serial: no open PR edits
ui/packages/plugin-kanban/**(open PR list read 2026-10-28T06:20Z).Tier
No path-derived mandate (
dispatch-gates --tier, read 2026-10-28T06:20Z).TIER_DEFAULT, effortmedium(S–M).Clause-②: no(renderer accessibility text; no accept set changes),Metadata-protocol: none.objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 3
Session:
session_01JQwxbXpF772qWJjojJvRXwAccount:
os-warrenBranch:
claude/project-thread-284e5pWorktree:
objectstack-issue-27099Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/plugin-kanban/src/{KanbanImpl,KanbanBoardCore,ObjectKanban}.tsx,ui/packages/core/src/validation/validators/state-machine-next-values.tsonly if the reason needs a core helper, tests beside them, i18n locale packs only for a fallback key, root.changeset/(stop on breach; explain in the report)Container & model:
S–M,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
a kanban lane that refuses the dragged card is dimmed and aria-disabled with no reason attached|the drop gate is the blocking state_machine rule, whose authored message already says why|every user dragging cards on a board whose groupBy field has a blocking state machineThread-read: 6462220361
Premise: holds (gate read on
origin/maind8a9cc59; readings in dispatch order 6462220361)Serial constraints cleared: no open PR edits
ui/packages/plugin-kanban/**.Dispatch order: comment 6462220361. Card thread
claude/project-thread-284e5pmay start editing.objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{ "issue": 27099, "status": "done", "branch": "claude/project-thread-284e5p", "pr": "https://github.com/objectstack-ai/objectstack/pull/27133", "head": "18f0af77", "session": "session_01JQwxbZzYH3oXtLp9fbSkzn", "premise_still_valid": true, "summary": "A lane the drop gate closes now carries the reason as its title and as the text its aria-describedby names, on the flat lane and on every swimlane cell; an open lane and every lane outside a drag carry neither. ObjectKanban supplies the message of each blocking state_machine rule on groupBy that refuses the move, asking each rule alone through stateMachineNextValues (no copy of the gate predicate, core untouched); several closing rules join, a blank message falls through to the new kanban.dropBlocked pack sentence (ten packs). Premise confirmed: the gate's only supplier is ObjectKanban canDropCard, which reads only the blocking state_machine rule; a required field prompts after the drop and a missing grant makes cards not movable, so neither dims a lane.", "files_changed": [ ".changeset/27099-kanban-blocked-lane-reason.md", "ui/packages/plugin-kanban/src/KanbanImpl.tsx", "ui/packages/plugin-kanban/src/KanbanBoardCore.tsx", "ui/packages/plugin-kanban/src/ObjectKanban.tsx", "ui/packages/plugin-kanban/src/ObjectKanban.dropBlockedReason-27099.test.tsx", "ui/packages/plugin-kanban/src/__tests__/dropBlockedReason-27099.test.tsx", "ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts" ], "tests": "head 18f0af77: new and neighbour files (2 new + stateMachineDropGate-25933) Test Files 3/3, Tests 14/14; plugin-kanban + i18n dirs Test Files 171/171, Tests 1910 passed 13 skipped (lock VERDICT command-exit 0); type-check plugin-kanban and i18n exit 0. Ablation on 6dc7501f via ablation-replace.mjs: flat lane title+aria-describedby deleted -> ObjectKanban.dropBlockedReason-27099 4 failed 1 passed (CONTROL); swimlane cell title+aria-describedby deleted -> __tests__/dropBlockedReason-27099 1 failed 2 passed; both restored, blob == HEAD, git diff HEAD empty. Tests import source by relative path and @object-ui/i18n is vitest-aliased to src, so no dist leg.", "gates": "dispatch-gates --commands: 34 derived, all 34 run, every exit 0 captured before any pipe (incl. node scripts/check-sdui-manifest.mjs exit 0, pnpm --dir ui exec vitest run scripts/ exit 0 with Test Files 175 passed 2 skipped); plus check:changeset-fixed exit 0 and ui check:new-line-citations exit 0 outside the derivation. --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. CI check-runs: in_progress at report time.", "line_budget": "469 changed lines before merge (+465 / -4), under the 3000 human-merge threshold", "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm", "deviations": [ "origin/main 6817bf1e merged into the branch before the PR (it touched the same ten locale packs); suites, type-check and all gates re-run on the merged head 18f0af77. origin/main has since moved again (e6e41998); not merged, no conflict reported.", "The ablations ran on the pre-merge commit 6dc7501f, which carries the same feature diff.", "PR template headings folded into the os-dev body order (Why and Human Minimum kept)." ], "mcp_calls": "0", "api_writes": "2: pr_create through the fleet-write relay (pulls, PR 27133); this report through post-stamped (issues/27099/comments). git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: under a translated locale, a blocked lane (and every client surface reading /meta validations) shows the authored validations[].message while the server refusal of the same move shows the objects.OBJECT._validations.RULE.message translation, because translateObject in @objectstack/spec translates fields and actions but not validations · evidence: translateObject maps doc.fields and doc.actions only; objectValidationMessageKey is read only by the ObjectQL rule validator; nothing under ui/ reads _validations · dedupe words: translateObject validations message locale, _validations served meta, rule message translation client" ] }objectstack-fleet commentedon Oct 28, 2026
Seat review: PR #27133 ACCEPT (record on the PR, head
18f0af77cb), 2026-10-28T07:37Z. The out-of-scope finding (translateObjectleavesvalidations[].messageuntranslated) is filed as #27135 (domain:spec). Landing waits for every check on the head to be green.objectstack-fleet commentedon Oct 28, 2026
Landing held ·
domain:ui#2·session_01JQwxbXpF772qWJjojJvRXw· 2026-10-28T07:50ZPR #27133 (ACCEPT 6463403177) is green on head
18f0af77cb: 43 checks succeeded and 11 were skipped (path-filtered core / docs / dogfood jobs).mergeable_stateis clean and the PR is not governed. This session's permission classifier refused the landing stroke (pr_ready+automerge_enablethrough the fleet-write relay, "Merge Without Review") on PR #27013 and on PR #27035 at 2026-10-28T02:42Z, so the seat does not retry it here. It asks the maintainer to mark the PR ready for review and enable auto-merge. The out-of-scope finding is filed as #27135 (domain:spec).