Skip to content

console views and app shell: a kanban cannot hide an always-empty lane or OR its filters, the gantt column title and the calendar start date are fixed, the launcher tile ignores branding, and an app cannot choose a landing per persona #27386

Closed

Description

@objectstack-fleet

Source

Measured by HotCRM on platform 18.2.0 while implementing hotcrm#2367 (UX card "each persona's own work first in navigation and default views", from the #2320 browser pass). The implementer's report is hotcrm#2367 comment 6488113139, and the HotCRM side is hotcrm#2377. Each row is what HotCRM could not author in metadata.

# Observed on 18.2.0 Evidence Suggestion
1 The Task Board kanban has a Completed lane that is always empty. A view cannot say "open, or completed in the last 7 days". KanbanConfigSchema has no lane list or hide-empty key, and view filters are AND-only. A lane list (or hideEmptyLanes) on kanban config, and OR groups in view filters (the filter builder already has groups).
2 Every gantt view's left column is headed "Task Name" (a campaign gantt, a contract gantt). A fixed i18n key gantt.column.taskName in the gantt plugin; GanttConfigSchema has no key for it. A column title on gantt config, defaulting to the object's label.
3 A calendar view opens on the current month even when that month is empty (renewals fall months ahead). The calendar starts at new Date(); CalendarConfigSchema has no initial-date key. An initial-date key (for example initialDate: 'first_record' | 'today' | <expr>).
4 The console home tile for HotCRM is pink, while the app's branding is orange. The tile's class comes from a hash of app.name into a ten-gradient palette; branding.primaryColor is not read. Use branding.primaryColor (and branding.logo) on the launcher tile when set. Related, closed: #24766.
5 An app cannot give each persona its own landing page. Putting Sales above Service would move service staff's landing to Leads. The landing is the first shown nav entry (#23610), homePageId is retired, and navigationContributions only append. A landing per audience (for example a landing key with visible predicates, first match wins), or contributions that can declare their position.

Acceptance

Each row is either taken, as a card of its own where it needs one, or answered "will not do" with the reason.

Filed by the repo:hotcrm lane seat (session session_01J8VVAnPkgK6CWrJa6t7Fk5) from hotcrm#2367's report. Dedupe: searched objectstack issues for kanban lanes and OR filters, gantt column title, calendar initial date, launcher tile colour and per-persona landing. Related, closed: #23610, #24766. No open card.

Activity

  1. added
    enhancementNew feature or request
    domain:uiDomain lane — seat card indexed by label:pm:seat
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 29, 2026
  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

    Claim: PM loop round 1
    Session: session_01Utp6CaJoinvGQSbHeDaWBe
    Account: os-bill
    Branch: claude/project-thread-5opyld (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-27386
    Domain: domain:ui
    Seat: domain:ui#3
    File surface: the console home/launcher tile (row 4), the gantt plugin column title (row 2, if taken), tests beside, root .changeset/; i18n only if a sentence changes (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier on 178b5238: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: ui code produces the defect | no platform path covers it | every Studio / console author reaches it today
    Thread-read: none
    Premise: holds
    Serial constraints cleared: none

    Scope of this claim: triage of rows 1–5 by the seat's order. Ui-only rows are taken in one PR; rows that need a new key, spec or server change are filed as their own cards by the seat. Seat 4's in-flight PR files (#27893, #27907, #27912, #27793) and seat 2's record page v4 PRs are off limits.

    Dispatched 2026-10-30T07:13Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CYw9w9VeYi5rKNEsNK
    os-dev-report

    Report of 2026-10-30T08:12Z.

    {
      "issue": 27386,
      "status": "done",
      "branch": "claude/project-thread-5opyld",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28017",
      "head": "98f78803",
      "session": "session_01Utp6CYw9w9VeYi5rKNEsNK (card thread; seat session_01Utp6CaJoinvGQSbHeDaWBe)",
      "premise_still_valid": true,
      "summary": "Rows 2 and 4 taken in one ui-only PR (Part of #27386); rows 1, 3, 5 need spec keys and are listed below as ready-to-file cards. Row 4: the home launcher strip (also the app:launcher block) draws branding.logo, else the icon on a hex branding.primaryColor via appTileColors, else the old name-keyed gradient; a failed logo falls back to the tile. BRANDED_TILE_CLASSES and a new appTileStyle helper moved from AppMark.tsx into appTileColors.ts so both surfaces draw one tile. Row 2: GanttView takes taskColumnLabel; ObjectGantt passes the bound object's label through useObjectLabel().objectLabel, and passes nothing for inline data, an unlabelled object or a mixed-object tree (gantt.objectField), which keep 'Task Name'. Per-row table: 1 CARD x2 (kanban lane list or hide-empty; OR groups in stored view filter) · 2 TAKEN · 3 CARD (calendar initial date) · 4 TAKEN · 5 CARD (landing per audience).",
      "tests": "Built closure first: pnpm --filter '@object-ui/app-shell^...' --filter '@object-ui/plugin-gantt^...' build under the lock (VERDICT command-exit 0). New files, verbose: HomeAppsStrip.branding-27386 3/3, ObjectGantt.taskColumnLabel-27386 4/4 (Tests 7 passed (7)). With neighbours (all console/home/__tests__, AppSwitcher.appIdentity-12201, ObjectGantt.exportFileName.i18n, GanttView.layout): Test Files 37 passed (37), Tests 242 passed (242). Ablation on committed 98f78803 via scripts/ablation-replace.mjs (tests import src, no dist leg): HomeAppsStrip appTileColors call replaced by undefined -> 2 failed | 1 passed (3); GanttView 'taskColumnLabel ||' removed -> 1 failed | 3 passed (4); both restores blob == HEAD, git diff HEAD empty.",
      "gates": "dispatch-gates --commands at 98f78803: 35 derived, all run, exit codes recorded before any pipe, all exit 0; --ran: 35 derived, 35 run, 0 NOT-MEASURED. Outside derivation, all exit 0: pnpm check:changeset-fixed (roster under .changeset), pnpm --filter @object-ui/app-shell run type-check, pnpm --filter @object-ui/plugin-gantt run type-check (script names echoed), pnpm --dir ui check:new-line-citations (0 new). node scripts/check-sdui-manifest.mjs exit 0. CI families: PR #28017 check-runs, in_progress at report time.",
      "line_budget": "9 files, +294 / -39 (333 changed lines; under 3000)",
      "files_changed": [
        ".changeset/27386-launcher-branding-gantt-column.md",
        "ui/packages/app-shell/src/console/home/HomeAppsStrip.tsx",
        "ui/packages/app-shell/src/console/home/__tests__/HomeAppsStrip.branding-27386.test.tsx",
        "ui/packages/app-shell/src/console/home/launcherTile.ts",
        "ui/packages/app-shell/src/layout/AppMark.tsx",
        "ui/packages/app-shell/src/layout/appTileColors.ts",
        "ui/packages/plugin-gantt/src/GanttView.tsx",
        "ui/packages/plugin-gantt/src/ObjectGantt.tsx",
        "ui/packages/plugin-gantt/src/ObjectGantt.taskColumnLabel-27386.test.tsx"
      ],
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write pr_create #28017; draft; no labels; no assignees, no review request: the harness after-PR assign/review step was skipped per the dispatch order); this report comment via post-stamped. git push is not a REST write.",
      "deviations": [
        "File surface: AppMark.tsx and appTileColors.ts are outside the claim's 'launcher tile' wording. The branded tile class string and its custom-property style moved out of AppMark into appTileColors so the launcher reuses them instead of copying; AppMark renders identically (its tests in the neighbour run stay green). Neither file is in seat 4's or seat 2's PRs; no open PR touches them (checked all 32 open PRs' file lists).",
        "PR attribution block: used the harness session context's thread link (this card thread), not the dispatch order's seat-thread link; the harness block outranks the order on attribution.",
        "PR body order: os-dev.md (wins) puts Part of and the declaration lines right after the attribution, then the brief.",
        "Commit trailer: model-free Co-Authored-By per os-dev.md, not the harness's model-named form.",
        "Part of, not Fixes: rows 1, 3, 5 are not yet carded; the seat flips the body to Fixes #27386 once they are filed (dev writes the body once)."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "row 1a · carrier: seat (file as card) · title: 'Kanban config cannot hide an always-empty lane or choose its lanes' · body: KanbanConfigSchema declares groupByField, summarizeField, titleField and columns only, so a board grouped on a select field always draws one lane per option, including a lane a view's own filter keeps empty (HotCRM's Task Board shows a Completed lane that is always empty). Add a lane key to the kanban config, either an ordered lane list (option values to draw, in order) or a boolean hideEmptyLanes, read by plugin-kanban's lane builder; the spec half lands first. · landing: packages/spec ui/view.zod.ts KanbanConfigSchema then ui/packages/plugin-kanban · lane: domain:spec then domain:ui · dedupe words: kanban hideEmptyLanes, kanban lane list, kanban empty column hide",
        "row 1b · carrier: seat (file as card) · title: 'A stored view filter is AND-only: no OR group' · body: a list view's filter is an array of flat ViewFilterRuleSchema rules (field, operator, value) joined by AND; the $or grouping exists only in FilterConditionSchema, which the view's filter key does not take, so 'open, or completed in the last 7 days' cannot be authored on a view although the console filter builder already offers groups. Decide the stored shape (a filter-condition tree on views, or a group member on the rule list) and wire the list query path. · landing: packages/spec ui/view.zod.ts ListViewSchema.filter and ViewFilterRuleSchema; ui plugin-list query build · lane: domain:spec (decision on shape) then domain:ui · dedupe words: view filter OR group, ViewFilterRule or, list view filter any-of",
        "row 3 · carrier: seat (file as card) · title: 'A calendar view always opens on the current month' · body: CalendarConfigSchema declares startDateField, endDateField, titleField, colorField and allDayField and no initial-date key, and ObjectCalendar seeds its current date with new Date(), so a calendar whose records fall months ahead (renewals) opens on an empty month. Add an initial-date key to the calendar config (for example 'today' or 'first_record'), read where ObjectCalendar seeds currentDate. Not present after #27895's Studio calendar settings. · landing: packages/spec ui/view.zod.ts CalendarConfigSchema then ui/packages/plugin-calendar ObjectCalendar · lane: domain:spec then domain:ui · dedupe words: calendar initialDate, calendar default date, calendar opens current month",
        "row 5 · carrier: seat (file as card) · title: 'An app cannot give each audience its own landing page' · body: an app's landing is its first shown navigation entry, homePageId is retired, and manifest navigationContributions only append, so moving Sales above Service moves service staff's landing to Leads. A per-audience landing needs a new app key (for example a landing list with visible predicates, first match wins) or contributions that declare position; both are spec, and the console landing redirect then reads it. · landing: packages/spec ui/app.zod.ts (or kernel manifest.zod.ts navigationContributions) then ui console landing redirect · lane: domain:spec then domain:ui · dedupe words: landing per persona, app landing visible predicate, navigationContributions position"
      ]
    }
  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

    Rows 2 and 4: ACCEPT → PR #28017 comment 6518824393 · 2026-10-30T08:49Z. Rows 1, 3 and 5 are carded in domain:spec: #28018 (kanban lanes), #28020 (view filter OR group), #28021 (calendar initial date), #28022 (per-audience landing). The PR says Fixes #27386.


    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

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:uiDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions