Description
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe
Filing gate: ③ triage-directed. The order on #27386 said: take what is ui-only, card the rest. The card thread measured each row on origin/main (report 6518223407, out_of_scope_findings). Rows 2 and 4 are taken in PR #28017.
Reach: any board grouped on a select field whose view filter keeps an option empty. HotCRM's Task Board always shows an empty Completed lane (#27386 row 1).
Reader who acts: domain:spec first (the config key), then domain:ui (plugin-kanban's lane builder).
Duplicate search: search_issues "kanban hide empty lanes choose lane list hideEmptyLanes" → #13692 (fallback lane for empty values, closed) and #27099 (drop-blocked lane), neither this.
Observed
KanbanConfigSchema declares groupByField, summarizeField, titleField and columns only. A board grouped on a select field draws one lane per option, including a lane the view's own filter always keeps empty.
Wanted
- Spec: a lane key on the kanban config: an ordered lane list (the option values to draw, in order), or a boolean
hideEmptyLanes. Landing:packages/specui/view.zod.tsKanbanConfigSchema. - Console:
ui/packages/plugin-kanban's lane builder reads it. The spec half lands first.
Dedupe words: kanban hideEmptyLanes, kanban lane list, kanban empty column hide.
Related: #27386 (row 1).
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 30, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Claim: PM loop round 2
Session:
session_01L9qibx5r5nhDMwpRs4SL6DAccount:
os-teslaBranch:
claude/project-thread-iomqnj(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-28018Domain:
domain:specSeat:
domain:spec#2File surface:
packages/spec/src/ui/view.zod.ts(KanbanConfigSchemaregion and its publish check), tests, regenerated spec artefacts and reference docs, changeset (@objectstack/specminor). The ui half is a follow-updomain:uicard the seat files when the key lands; ⛔ noui/**and no governed path (stop on breach; explain in the report)Container & model:
S,mode:thread,model: TIER_DEFAULT(Clause-②: yes⇒ default judgment tier to build; the contract review atCONTRACT_REVIEW_TIERis the seat's own, on the maintainer's 「你有 fable,可以自己契约复审」),effort: highClause-②: yes
Metadata-protocol: added
Responsibility: n/a — not a defect card
Thread-read: none
Premise: holds (re-read at
origin/main9f13ee81; the declaration named on the card still lacks the key)Serial constraints cleared: open PR #28138 (seat 4,
calendar.initialDate) editsview.zod.tsin the calendar region: ordinary concurrency, the later lander mergesmainRuling-ref: none on the shape; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); the card's filing gate ③ (triage-directed)
Dispatched 2026-10-30T15:12Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
objectstack-fleet commentedon Oct 30, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Claim amendment · file surface · #28018 · 2026-10-30T15:13Z
Amends claim 6526161926 (same session, same branch
claude/project-thread-iomqnj). The card thread measured that spec cannot see the object, so the publish refusal of a lane value that is not an option ofgroupByFieldcan only live wheregroupByFieldis already checked against the object:packages/lint/src/validate-list-view-field-refs.ts(the reference-integrity suite the runtime publish gate runs). The seat takes it in this PR (option A).File surface adds:
packages/lint/src/validate-list-view-field-refs.ts(or a sibling rule), its rule-explanations entry, tests,@objectstack/lintchangeset. Serial constraints for the added files: none (no open PR touches them, open PR list read at this stamp).objectstack-fleet commentedon Oct 30, 2026
domain:spec#2 · os-tesla · session_01L9qibxL3MYcTjfshb8eeK3
os-dev-report
{ "issue": 28018, "status": "done", "branch": "claude/project-thread-iomqnj", "pr": "https://github.com/objectstack-ai/objectstack/pull/28196", "session": "session_01L9qibxL3MYcTjfshb8eeK3", "premise_still_valid": true, "head": "ea4b11b5", "summary": "KanbanConfigSchema gains optional kanban.lanes: an ordered list of stored option values of groupByField (SystemIdentifier entries, min 1, no default; a record whose value is not listed lands in Uncategorized, never hidden). The object-aware half lives in @objectstack/lint validate-list-view-field-refs.ts (surface amended by the seat, option A): list-view-kanban-lane-unknown (error, a lane that is not an inline option of the group field, with Did-you-mean) and list-view-kanban-lane-repeated (error, at the second entry, judged with no field map); both explained in os explain. liveness kanban row notes lanes at planned strength.", "tests": "head ea4b11b5: lint test 179 files 7650 passed 5 skipped; spec vitest src/ui + form reconciliation + type-alias pin 167 files 5950 passed; lint + spec typecheck exit 0; new files lint 11/11, spec 5/5. Ablation via ablation-replace.mjs: membership short-circuit -> 3 failed/8 passed; repeat never fires -> 3 failed/8 passed; both legs restored to HEAD blob 34aa7c35, git diff HEAD empty.", "gates": { "derived": 104, "ran": 104, "not_measured": 0, "unrun": 0, "nonzero": [], "reconcile": "dispatch-gates --ran: 104 derived, 104 run, 0 NOT-MEASURED, 0 UNRUN", "local_only": [ "node scripts/check-dev-prereqs.mjs :: exit 1 (ui/ packages unbuilt locally; diff touches no ui/**)" ], "ran_list": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-adr-links.mjs :: exit 0", "node scripts/check-adr-links.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-adoption.mjs :: exit 0", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/check-dev-prereqs.mjs --self-test :: exit 0", "node scripts/check-doc-route-spelling.mjs --advisory :: exit 0", "node scripts/check-doc-route-spelling.mjs --self-test :: exit 0", "node scripts/check-docs-section-name.mjs :: exit 0", "node scripts/check-docs-section-name.mjs --self-test :: exit 0", "node scripts/check-dts-emitted.mjs --self-test :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-issue-citations.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0", "node scripts/check-section-landing-index.mjs :: exit 0", "node scripts/check-section-landing-index.mjs --self-test :: exit 0", "node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0", "node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0", "node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "node scripts/release-pending-publish.mjs --self-test :: exit 0", "pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0", "pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0", "pnpm --filter @objectstack/spec run check:api-surface :: exit 0", "pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0", "pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0", "pnpm --filter @objectstack/spec run check:docs :: exit 0", "pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm --filter @objectstack/spec run check:empty-state :: exit 0", "pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0", "pnpm --filter @objectstack/spec run check:export-origins :: exit 0", "pnpm --filter @objectstack/spec run check:exported-any :: exit 0", "pnpm --filter @objectstack/spec run check:generated :: exit 0", "pnpm --filter @objectstack/spec run check:liveness :: exit 0", "pnpm --filter @objectstack/spec run check:llms-txt :: exit 0", "pnpm --filter @objectstack/spec run check:skill-examples :: exit 0", "pnpm --filter @objectstack/spec run check:skill-refs :: exit 0", "pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0", "pnpm --filter @objectstack/spec run check:variant-docs :: exit 0", "pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:ci-filter-parity :: exit 0", "pnpm check:closed-ceremony-text :: exit 0", "pnpm check:corpus-claim-drift :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:dispatcher-error-vocabulary :: exit 0", "pnpm check:doc-anchors :: exit 0", "pnpm check:doc-authoring :: exit 0", "pnpm check:doc-frontmatter :: exit 0", "pnpm check:docs-audit-scope :: exit 0", "pnpm check:docs-redirects :: exit 0", "pnpm check:docs-single-h1 :: exit 0", "pnpm check:docs-spec-enumerations :: exit 0", "pnpm check:docs-transcript-drift :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:dts-closure :: exit 0", "pnpm check:dual-build-cjs-loads :: exit 0", "pnpm check:engine-double-contract :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:keyed-text-bounds :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:merge-driver :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:objectql-double-limit :: exit 0", "pnpm check:org-identifier :: exit 0", "pnpm check:page-declaration-shape :: exit 0", "pnpm check:platform-checklist :: exit 0", "pnpm check:platform-object-tenancy-census :: exit 0", "pnpm check:pm-changeset-deadline-census :: exit 0", "pnpm check:pm-prior-rulings :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:published-readme-links :: exit 0", "pnpm check:query-options-erasure :: exit 0", "pnpm check:react-page-adapter-contract :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:registry-log-declared :: exit 0", "pnpm check:rest-log-spy-declared :: exit 0", "pnpm check:role-word :: exit 0", "pnpm check:skill-identifier-liveness :: exit 0", "pnpm check:slot-lookup :: exit 0", "pnpm check:sourcemap-no-sources-content :: exit 0", "pnpm check:spec-docblock-symbol-anchors :: exit 0", "pnpm check:spec-parsed-alias :: exit 0", "pnpm check:system-context-census :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:type-check-debt :: exit 0", "pnpm check:undeclared-dep-imports :: exit 0", "pnpm check:vendor-version-stamps :: exit 0", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0" ], "ci": "in_progress at report time; seat reads PR check-runs" }, "line_budget": { "files": 12, "additions": 457, "deletions": 11, "changed_lines": 468, "generated": [ "content/docs/references/api/protocol.mdx", "content/docs/references/data/object.mdx", "content/docs/references/ui/view.mdx", "packages/spec/authorable-surface/ui.json" ] }, "files_changed": [ ".changeset/28018-kanban-lanes.md", "content/docs/references/api/protocol.mdx", "content/docs/references/data/object.mdx", "content/docs/references/ui/view.mdx", "packages/lint/src/index.ts", "packages/lint/src/rule-explanations.ts", "packages/lint/src/validate-list-view-field-refs.ts", "packages/lint/src/validate-list-view-kanban-lanes.test.ts", "packages/spec/authorable-surface/ui.json", "packages/spec/liveness/view.json", "packages/spec/src/ui/kanban-config-lanes.test.ts", "packages/spec/src/ui/view.zod.ts" ], "deviations": [ "packages/lint/src/index.ts (2 export lines + comment) is outside the amended surface: rule-explanations.test.ts requires every explained rule id to be a barrel-exported constant.", "Duplicate-lane check placed in lint, not as a superRefine in view.zod.ts: the superRefine made the spec build refuse (new sites in 16 entries of the shrink-only dropped-refinements.baseline.json); spec keeps only JSON-Schema-projectable checks (minItems, identifier pattern).", "Session-context post-PR step (assign os-tesla, request review via MCP) not performed: FRAME forbids MCP writes, assigning and requesting review; the seat lands." ], "decision_for_maintainer": "Shape taken: ordered lane list kanban.lanes. Not taken: hideEmptyLanes boolean, because it hides lanes as data moves (an empty lane stops being a drop target) and cannot order lanes; the list answers both choose and order for the HotCRM task board.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); 0 write tools", "api_writes": "2 — fleet-write relay pr_create (objectstack-ai/objectstack#28196); post-stamped os-dev-report comment on #28018", "open_questions": [], "out_of_scope_findings": [ "carrier: the seat · follow-up domain:ui card: ui/packages/plugin-kanban ObjectKanban.tsx#effectiveColumns reads kanban.lanes (draw only the listed lanes, in order; off-lane cards to Uncategorized), then the liveness kanban note drops planned · dedupe words: kanban lanes renderer, plugin-kanban lane list, kanban.lanes console", "carrier: the seat · Skills-docs follow-up owed: skills/objectstack-ui/rules/list-views.md kanban example teaches lanes once the console reads it · dedupe words: kanban lanes skill, list-views kanban lanes", "carrier: none · noted, not filed · membership not judged for picklist-bound or option-less group fields (stated in docblock and os explain)" ] }