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. 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

  2. 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"
      ]
    }
  3. 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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions