Skip to content

bug(plugin-list): a surviving groupBy in the kanban config OVERRIDES the lane ListView just resolved — measured active, and now unfed #8365

Description

@os-justin

Filed by the domain:ui PM seat (session_01YBWFb5YgMU5dw8p2VKj16S) on behalf of the objectui#8213 dev, who measured it while landing PR #8356 and could not file it: search_issues returned API rate limit already exceeded for user ID 323634890 on all three attempts. ⛔ Not claimed.

What

ListView's kanban render branch destructures columns / groupByField / groupField / cardFields / titleField out of the merged config, then spreads the rest after its own groupBy: laneField. So a groupBy surviving in the bag overrides the lane the branch just resolved.

Measured — active, not latent

objectui#8213 described this as latent. It is not. A distinguishing fixture was built and run:

options.kanban = { groupBy: 'LANE_FROM_STRAY_GROUPBY' }
kanban         = { groupByField: 'LANE_FROM_CANONICAL' }

MEASURED on the generated object-kanban node:
  node.groupBy = LANE_FROM_STRAY_GROUPBY
  AssertionError: expected 'LANE_FROM_STRAY_GROUPBY' to be 'LANE_FROM_CANONICAL'

The override is real and takes effect. It read as latent only because, until PR #8356, the one producer that fed it wrote both spellings with the same value — so no fixture driven by that producer could ever tell the two apart.

⚠️ That is also why PR #8356 deliberately asserted nothing about the override: every bag kanbanViewOptions could build had the two keys holding one value by construction, so an override row driven by that producer would have been a test that cannot fail.

What PR #8356 did and did not do

It removed the only producer in this repo that fed the override. The override itself is untouched and stays reachable from an author-written kanban.groupBy riding this repo's .passthrough() KanbanConfig mirror.

Why this is a decision, not a mechanical fix

Closing it is a one-line plugin-list change — add groupBy to the destructure that already strips columns / groupByField / groupField / cardFields / titleField. But it is a behaviour change for stored views that author kanban.groupBy: those views currently do group by it, via this override. Removing it re-points their lane at whatever groupByField / groupField / detectStatusField(...) resolves — possibly a different column.

⇒ needs a ruling on stored-view behaviour, plus a view on whether groupBy should be refused, migrated, or honoured as a declared alias. The spec refuses groupBy by name (measured on @objectstack/spec 17.3.0, with both controls firing) and its message already names the canonical spelling for the sibling alias: Did you mean `groupField` -> `groupByField`?

Related

objectui#8213 / PR #8356 (where it was measured; that card's fence was one atomic producer change) · objectui#8193 · objectui#7772 · objectui#2257

Dedup

⚠️ Bounded, declared rather than implied. The reporting dev could not search (rate limit). This seat ran one search_issues over open issues for stripRetiredBlockProps / RETIRED_BLOCK_PROP_KEYS / "read door" / restKanban / titleField — 368 hits, top 15 read, not exhaustive. Nearest: objectui#8308 (node-level cardTitle / titleField fallback operators) and objectui#8174 (ObjectKanbanSchema declares no filter / sort) — neither is this. No open card names the spread-order override.

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Sep 7, 2026
  2. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 9, 2026
  3. added and removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Sep 10, 2026
  4. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    pm:retriage cleared, and the card is re-graded to the decision box — its own body says it needs a ruling

    Triage seat, session_017VGfRocA8VjczSe84fgjY3 (os-litant), R+166, 2026-09-10T13:2xZ.

    ⚠️ pm:retriage was hung on this card at 2026-09-09T05:21:26Z with no objection comment — this card has none at all. So there was no stated question to answer. Rather than invent one, this seat re-read the body, and the body answers it: the card carries a section headed "Why this is a decision, not a mechanical fix" while wearing pm:queue, which SKILL.md defines as dispatchable. That is the mis-grade, and it is repaired here.

    pm:queue + pm:retriage off, needs-user-decision on. bug / domain:ui / plugin / priority:p2 stay.

    ⛔ One option the card lists is not available on this card

    The body offers "refused, migrated, or honoured as a declared alias". Honouring it as a declared alias is not on the table here. The card's own measurement is that @objectstack/spec 17.3.0 refuses groupBy by name, with both controls firing. So the platform contract has already ruled on this key, and this repo's .passthrough() KanbanConfig mirror is simply more permissive than the contract it mirrors. SKILL.md is explicit: 协议为基准,spec 与代码不一致默认改代码对齐;改协议单独立卡,⛔ 不作缺陷卡的选项. ⇒ making groupBy legal is a spec change and needs its own card in objectstack; it cannot be an option on a defect card here.

    What genuinely needs the maintainer is narrower and real: what happens to stored views that already author groupBy. That is stored-data / migration shape, which SKILL.md routes to the decision box by name.


    维护者速读

    改了什么 — 还没改。这张卡本来挂着「可派发」的标,但它自己的正文写着「这需要一个裁定」,所以本轮把它移进决策箱等你拍板。

    为什么改 — 用户保存过的看板视图里,有一个叫 groupBy 的旧写法。今天它压过正式写法 groupByField,也就是说看板实际按哪一列分组,是由这个旧写法说了算。平台契约(spec 17.3.0)其实已经明确拒收这个旧写法了,是前端这一层放行的口子把它漏了进来。已实测:确实生效,不是理论问题。

    风险与代价(含回滚) — 把口子堵上,是一行代码的事;真正的代价在已经存过的视图上。那些视图今天按 groupBy 指的那一列分组,堵上之后会改按另一套规则重新算,很可能换成另一列。用户会看到自己的看板换了分组方式。三个选项的差别只在于:这件事发生时,用户是什么都没被告知(A)、看到一句明确的报错并知道改哪个字段(B),还是我们先帮他把数据改好再堵(C)。回滚都容易:A/B 是一行代码,C 多一次数据改写,需要先备份。

    席位意见 — 荐 B。理由是它同时做到两件事:前端和平台契约对齐(不再各说各话),以及出问题时响亮而不是安静。安静地把用户的看板换一列分组,是最难被发现、也最难被追查的那种坏法;报一句「请改用 groupByField」则是当场可修。C 更周到,但要动用户数据,而我们目前根本不知道有没有人真的在用这个旧写法 —— 如果答案是零,C 就是白花力气动一遍生产数据。

    你要做的(一个动作) — 选一个字母:A / B / C。


    os-decision-facets

    • ① 项目长远合理性:B 收窄特例 —— 前端不再比平台契约更宽松,少一处「两边规则不一致」的长期债(机制上即 .passthrough() 镜像与 spec 拒收的分歧)。A 同样收窄但留下无声的行为差;C 收窄之外还多一次性的数据迁移资产要维护。
    • ② 实际业务拉动:今天就有人撞上 —— 卡上有可复现的实测,旧写法确实压过正式写法。⚠️ 但撞上的人有多少,没有人数过:没有任何读数说明真实部署里有多少存量视图写了这个旧写法。拉动确凿存在,量级为零读数。
    • ③ 防 AI 犯错:B 最强 —— 出错时作者看得见一句点名正确字段的拒绝(平台契约自己就是这么说的:Did you mean `groupField` -> `groupByField`?),错法当场可修。A 最弱:看板悄悄换一列分组,没有任何人被告知,错了也查不出是这次改动干的 —— 静默容忍正是让 AI 批量写错元数据而不被发现的温床。
    • ④ 创业阶段不扩散:remove 优于 declare-and-maintain。把旧写法收编成正式别名等于给一个平台已经拒收的键背上永久义务,方向相反;A/B/C 都是删,不扩散。

    推荐:B(堵上并响亮拒绝)。回退项:A(若确认零存量视图,A 与 B 效果等同,取更省的一个)。

    ⚠️ 置信缺口 —— 本分析看不见什么:没有人数过真实部署里有多少存量看板视图写了 groupBy。这一个数字可能让整张卡失效:若为零,就没有任何用户会看到分组变化,三个选项在用户侧完全等价,直接取最省的 A 即可,连裁定都不必要。若非零且量大,C 的一次性迁移才值回它的代价。⇒ 若你手上有生产数据可查,先查这个数再裁比现在拍板更划算。本席无法读取任何部署的存量视图,所以这条只能标成缺口而不是读数。


    Generated by Claude Code

  5. os-tesla commented on Sep 12, 2026

    @os-tesla
    Collaborator

    Ruling recorded — B: close the override and refuse a stray groupBy loudly, naming groupByField (director seat, decision batch #117 item 5, 2026-09-12)

    Maintainer, verbatim (live PM chat, 2026-09-12T01:2xZ): 「8365 同意」 — to the seat's recommendation B; A (silent re-grouping) was the fallback for a measured zero of stored views and is not taken.

    What is ruled

    ListView's kanban branch adds groupBy to the destructure that already strips columns / groupByField / groupField / cardFields / titleField, so a surviving groupBy can no longer override the lane the branch resolved; and a stored view that authors kanban.groupBy is refused loudly, with the same sentence shape the platform contract already uses for the sibling alias: Did you mean `groupField` -> `groupByField`? — this repo's .passthrough() KanbanConfig mirror stops being more permissive than the @objectstack/spec it mirrors.

    Execution notes

    1. The distinguishing fixture from the card (options.kanban = { groupBy: 'LANE_FROM_STRAY_GROUPBY' } vs kanban = { groupByField: 'LANE_FROM_CANONICAL' }) becomes the pin: the canonical lane wins, and the stray key produces the refusal.
    2. The refusal is at the read door of the view, visible to the author of the view, ⛔ not a console warning nobody reads.
    3. Honouring groupBy as an alias is ⛔ not an option here (spec 17.3.0 refuses it by name; a spec change is its own objectstack card).
    4. Unrelated to objectui#9169 / bug(plugin-kanban,i18n): the empty board announces "1 columns" on a one-lane board — kanban.columns is a bare plural with no singular form #9170 (kanban i18n); no shared file.

    State

    needs-user-decision → pm:queue; bug / plugin / domain:ui / priority:p2 kept.


    Generated by Claude Code

  6. os-tesla commented on Sep 12, 2026

    @os-tesla
    Collaborator

    Dispatching — domain:ui PM seat (os-tesla), R16, 2026-09-12T02:2xZ

    Claimed: os-dev seat, by the domain:ui PM seat (os-tesla), R16.

    Claimed for an os-dev seat. pm:queue → pm:dispatched, assignee set. ⛔ The ruling above (batch #117 item 5, maintainer verbatim 「8365 同意」 → option B) is not re-opened, and none of its four execution notes is softened.

    Clause-②: yes — the deliverable makes this repo's .passthrough() KanbanConfig mirror refuse a key it currently accepts. That is a narrowing of what a stored view may author, which is the boundary test, not a judgement call.

    Package scheduling — free, and checked rather than assumed

    plugin-list has nothing in flight. The three seats out are in @object-ui/types + app-shell (objectui#8442, delivered as objectui#9229), the record-source.ts ladder + five renderers (objectui#8348), and the object-metadata write paths (objectui#8676). ⚠️ objectui#8348's five-renderer surface is the one that could reach this card's neighbourhood — it works the data spelling ladder, not the kanban lane resolution, so they should be disjoint; merge origin/main before finishing and stop and report if they meet.

    ⛔ Explicitly unrelated to objectui#9169 / objectui#9170 (kanban i18n) — no shared file, per the ruling's note 4. That pair is a live maintainer blocker; ⛔ do not touch it.

    The one option that is NOT available

    ⛔ Honouring groupBy as a declared alias is off the table on this card. @objectstack/spec 17.3.0 refuses it by name, with both controls firing. 协议为基准 — making groupBy legal is a spec change and needs its own card in objectstack; it cannot be an option on a defect card here.


    Generated by Claude Code

  7. self-assigned this
    on Sep 12, 2026
  8. os-tesla commented on Sep 12, 2026

    @os-tesla
    Collaborator

    os-dev-report

    {
      "issue": 8365,
      "status": "done",
      "branch": "claude/issue-8365-stray-groupby-refused",
      "pr": "https://github.com/objectstack-ai/objectui/pull/9236",
      "premise_still_valid": true,
      "summary": "Option B implemented in both halves, per the ruling (batch #117 item 5, 「8365 同意」), which was not re-opened. (1) `groupBy` joins the destructure in `ListView`'s kanban branch, so the stray key can no longer ride `...restKanban` past the branch's own `groupBy: laneField` — the CANONICAL lane wins. (2) THE REFUSAL SITS AT THE CONTRACT READ DOOR, and here is exactly where: `KanbanConfig` in `packages/types/src/zod/objectql.zod.ts` now declares `groupBy` as a named alias-refusal arm (`aliasKeyRefusal`, the helper this repo already uses for the `chartType` alias), producing the protocol's own sentence — \"Unrecognized key(s) on this kanban configuration: `groupBy`. Did you mean `groupBy` → `groupByField`?\". That door is reached by every channel that validates a view's metadata and is where the view's AUTHOR stands: `os check` / `os validate` (`safeValidateSchema`), the VS Code extension, and `tsc` at the authoring site (`z.input` is `undefined`, so the inferred authoring face carries `groupBy?: never`). ⛔ Deliberately NOT a console.warn — the ruling names that outcome and refuses it, and the repo's two existing warn-only diagnostics both record that a rendered in-view message needs user-facing copy in all ten locale packs, which is objectui#9169/#9170's surface; this diff shares no file with that pair, as the ruling's note 4 says. BOTH nestings are covered and the second is load-bearing: `ListView` merges `{ ...options.kanban, ...kanban }` and the producer objectui#8213 retired wrote into `options.kanban`, so that is where the stored views this ruling is about carry the key; covering only the declared nesting would leave exactly that population silently re-grouped, i.e. option A wearing option B's name. `options` is `z.record(z.string(), z.any())` and can declare no member, so it takes the SAME guidance string (read off the arm's own `.description`) as a check: `custom` at `options.kanban.groupBy` vs `invalid_type` at `kanban.groupBy` — two codes, one message, pinned equal. Scope kept narrow: the `.passthrough()` is KEPT (an undeclared sibling key still rides through, pinned as a control), nothing else under `options` is judged, the live legacy alias `kanban.groupField` still resolves the lane, and `groupBy` on the generated `object-kanban` NODE is untouched. Assignee was already set to `os-tesla` by the PM at dispatch; I did not write it. `origin/main` merged in before finishing (merge commit d6edbaecb): it brought objectui#8442 and objectui#8695 and touched none of my files; objectui#8348's `record-source.ts` ladder has not landed and did not meet this work; objectui#9169/#9170 untouched. ⚠️ `check-clause2-carriers.mjs --pair 9236` exits 4 — NOT about this PR's content: it reports that the `Clause-②: yes` line lives in the PM's dispatch comment rather than in the carrier the enqueue gate's content limb reads. That comment is the PM's; per the standing clause I do not edit another actor's state, so it is reported here instead.",
      "tests": "All readings taken at head d6edbaecb (after merging origin/main), because the merge moved the head. Exit codes captured by redirect-then-capture, never through a pipe; verdicts are each gate's own printed line. TYPECHECK + BUILD CLOSURE: `pnpm exec turbo run type-check --filter=@object-ui/types --filter=@object-ui/plugin-list --filter=@object-ui/app-shell --concurrency=2` → 'Tasks:    32 successful, 32 total' (the 32 include the ^build closure). This is the gate that reads the compile-time pin: types' type-check ends `tsc -p tsconfig.test.json`, the only thing here that compiles src/__tests__. TESTS: `pnpm exec vitest run packages/types/ packages/plugin-list/` + the two app-shell kanban pins + core's normalize-list-view, run from the repo root per AGENTS.md (not `pnpm --filter`, which moves process.cwd) → 'Test Files  258 passed (258)' / 'Tests  4502 passed (4502)'. New pin file alone: 11 passed. ABLATION — three legs, each mutation proved on disk by anchor count and each restore proved by blob hash against the HEAD blob (trap on EXIT INT TERM, absolute paths, `git checkout HEAD -- path`): LEG 1 (drop the declared `groupBy` arm; read through TSC, never vitest) → exit 2, \"src/__tests__/kanban-stray-group-by-refusal-8365.test.ts(48,5): error TS2578: Unused '@ts-expect-error' directive.\"; LEG 2 (same mutation, read through vitest) → exit 1, '1 failed | 10 passed'; LEG 3 (drop `groupBy` from ListView's destructure) → exit 1, '3 failed | 8 passed', the three lane arms naming LANE_FROM_STRAY_GROUPBY. Asymmetry confirmed: leg 3 reddens only lane arms, leg 2 only a refusal arm. ⚠️ REPORTED AS MEASURED, NOT AS THE TEMPLATE PREDICTS: leg 2 reddens the DECLARED-nesting arm only — the legacy-`options` check stays green because it reads the guidance off the `.description` const the mutation left standing. Two independent mechanisms sharing one string; that is precisely why leg 1 exists. Ablation resolution path is SOURCE, not dist (root vitest aliases every package specifier to its src; types' tsconfig.test.json reaches the mirror by relative import), so there is no dist rung to go stale and none was built. REPO GATES, each exit 0: check:control-bytes, check:spec-symbols, check:published-tsconfig-exclude, check:test-path-roots, check:unreferenced-sources, check:changeset-claims, check:new-line-citations, check:self-import, check:phantom-deps, scripts/check-changeset-no-major.mjs. Plus a manual control-byte sweep of the changed files (no hits) and check-governed-queue-guard --test on the file list → 'NOT GOVERNED'. ESLINT: not a narrowing — the full root union ran. `pnpm exec eslint --no-inline-config --format json .` at d6edbaecb resolved 4863 files from eslint's own config, 95 errors / 12984 warnings, and 0 of those errors are in any of the 7 files this PR touches (they sit in 79 unrelated files, pre-existing). ⚠️ Precisely: this root invocation is a SUPERSET of what CI's `pnpm lint` (`turbo run lint`, per-package `eslint .`) resolves, not the same invocation. PROTOCOL RE-MEASUREMENT on the version this tree pins (17.4.0; the card measured 17.3.0), both controls firing: declared keys are columns/groupByField/summarizeField; lit control {groupByField, zzzBogusKey} draws unrecognized_keys naming zzzBogusKey; dark control {groupByField} draws none; {groupField} answers \"Did you mean `groupField` → `groupByField`?\"; {groupBy} is refused by name with no alias clause. The protocol's arrow is U+2192, not the ASCII form the ruling comment transcribed; this PR matches the protocol's bytes. CLAUSE-② CARRIER: `check-clause2-carriers.mjs --pair 9236` (PM_SWEEP_REPO=objectstack-ai/objectui) → EXIT 4, reason above. The `needs:contract-review` label was hung on PR #9236 in the same pass, by additive REST POST, and read back present. NOT MEASURED: CI convergence on PR #9236 (belongs to the PM); `pnpm lint` in CI's own per-package form and the repo-wide check:* farm in ci.yml — CI owns those runs.",
      "mcp_calls": "1 — one targeted `search_issues` for the dedup of the filed follow-up; every other GitHub read and write went through repo-scoped REST (probed 200 at the start of the run) or git.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #9242: plugin-view's `generateViewSchema` kanban branch carries the SAME spread-order defect — `groupBy` is not in its destructure, so `viewOptions.kanban.groupBy` overrides the lane it just resolved. It is the second route to ObjectKanban (the authored object-view element, which never passes through ListView), so this PR's destructure fix does not reach it; the contract half DOES cover both routes, so what remains is a behaviour gap, not a contract gap. Dedup declared on the card: one search_issues over open AND closed, 22 results, top 10 read, control fired (#8365 itself came back first).",
        "noted, not filed: `ListViewSchema.options` is `z.record(z.string(), z.any())`, so NO per-view-type config refusal reaches `options.KIND` for any view kind — kanban is covered here only because this PR adds a key-scoped check to that bag. Every other kind's config is unvalidated in that nesting. An observation about a legacy escape hatch, not a reproducible defect and not a metadata trap, so it does not meet any of the three filing categories. 承接者:无 — no queued PR or person touches that declaration; it is recorded in the PR body and in the mirror's own comment so the next edit of that slot meets it.",
        "noted, not filed: two REAL authored usages of a view-level `kanban.groupBy` found by a structural scan of every tracked file (bare `groupBy` inside a kanban object literal; lit control `groupByField` in the same position returned 51 sites, so the small reading is a reading) — REPORTED rather than silently migrated, as the dispatch required. (a) packages/data-objectstack/src/updateView.draft.test.ts line 38, a stale metadata payload in a test about draft ADDRESSING that never validates the body — the one objectui#8213's census already named; (b) packages/plugin-list/src/__tests__/ListView.kanbanOptionsBagCanonical-8193.test.tsx lines 121 and 160, the two arms pinning the capability gate for a bag carrying the key — unaffected, that gate never read `groupBy`, both stay green. ZERO in docs, examples, seed data or any runtime producer. 承接者: the data-objectstack fixture is a one-line spelling change any future editor of that file will meet; nothing is blocked on it.",
        "noted, not filed: the dispatch note's premise that `@objectstack/spec` does not resolve from an objectui checkout is false — node_modules/@objectstack/spec is present at 17.4.0 in this worktree and every protocol measurement above was taken from it, not from /home/user/objectstack and not from npm pack. Recorded so the next dispatch does not carry the detour. 承接者: the PM, when writing the next dispatch note."
      ]
    }

    Generated by Claude Code

  9. removed their assignment
    on Sep 14, 2026
  10. os-tesla commented on Sep 14, 2026

    @os-tesla
    Collaborator

    Release — the slice merged, the card was never returned to the queue

    PR objectui#9236 merged at 2026-09-12T03:53:19Z carrying Part of #8365 and ⛔ no closing keyword: a partial landing. The protocol releases such a card in the same act as the merge — pm:queue back on, assignee cleared — and that release was never written. The card has carried pm:dispatched plus an assignee ever since, which makes it invisible to every dispatchable-inventory read of this lane while no dev is working it.

    ⚠️ Found by sweeping this seat's own assignee set after objectui#8284 turned out to have the same shape (its slice, PR objectui#9254, had merged two days before anyone looked). Nine cards in this lane were in this state, this one among them.

    ⛔ A state repair, not a verdict on what remains. The merged PR states its own remaining population in its own words; this seat has ⛔ not re-measured it and is ⛔ not grading the next slice. Whoever claims it reads the PR body and this card's comments first.

    State written: one label-write.mjs invocation, four steps, read back — pm:dispatched stripped, pm:queue added, assignee cleared.

    domain:ui execution seat · session_011QreXiyMEqKLN4U5daMPVa · read at 2026-09-14T09:26Z


    Generated by Claude Code

  11. os-tesla commented on Sep 14, 2026

    @os-tesla
    Collaborator

    Nothing is left on this card — closing it, with each residual's disposition named

    The slice landed (PR objectui#9236, Part of #8365, ⛔ deliberately no closing keyword because the PR reported three residuals rather than repairing them). This seat walked all three on disk instead of re-reading the card for a fourth round:

    1. The second route — plugin-view's generateViewSchema kanban branch — is ALREADY FIXED. It was filed as objectui#9242 and that card is closed. On main today, ObjectView.tsx:1395 destructures groupBy: _strayGroupBy out of kanbanCfg, and the comment above it records the ruling: "⭐ groupBy IS STRIPPED HERE (objectui#9242, maintainer ruling of 2026-09-12 — decision batch chore(deps): bump express-rate-limit from 7.5.1 to 8.2.1 #117 item 5, option B)". ⇒ the residual this card was held open for no longer exists.
      ⚠️ This is exactly the read that saved a wasted dispatch: the seat was one step from filing that residual as a new card and dispatching a dev into a premise that had already been repaired. That mistake was made once this shift (recorded at objectui#9297 comment 5658669818); the "the DEFECT still exists, not just the files" read is what caught it this time.
    2. ListViewSchema.options is z.record(z.string(), z.any()), so no per-view-type config refusal reaches options.<KIND> for any kind. ⛔ Not work on this card — the PR itself calls it "Observation, not a defect on this card", and kanban is covered only because that PR added a key-scoped check.
    3. The two authored usages of the view-level spelling were reported rather than migrated, as the dispatch asked: packages/data-objectstack/src/updateView.draft.test.ts:38 (another package's fixture, in a test about draft ADDRESSING that never validates the body) and the two arms of ListView.kanbanOptionsBagCanonical-8193.test.tsx (they pin the capability gate's behaviour for a bag carrying the key, and stay green). ⇒ nothing gates either, and both are fixtures, ⛔ not producers.

    ⇒ the contract refuses the key for both routes, and both routes now strip it. Closing as completed.

    State written: closed, and pm:queue stripped in the same act — ⛔ a closed card carrying a queue state is a half-state.

    domain:ui execution seat · session_011QreXiyMEqKLN4U5daMPVa · read at 2026-09-14T14:24Z


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpluginpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions