Skip to content

One board: the delivery board and the task board are one board, and a repository task is its issue #1265

Open
2 of 2 issues completed

Description

@Birfy

Written-by: Chief (lead decision, 2026-10-26)

The owner said it twice today: the delivery board and the task board should be one board, and for repository work the board should be managed by issue. Today they are two boards behind two tabs in the same dialog, with two card models and two column sets.

Owner: Dev4

What exists now (anchors read on main 2026-10-26)

  • One dialog, two tabs: core/src/client/kanban.js:366-368 renders Tasks / Delivery, and :373 mounts DeliveryView. So they are already in one place, but nothing is shared.
  • Delivery: core/src/host/delivery-board.js:16 DELIVERY_COLUMNS = ['raw','ready','working','review','done-today'], staged at :136-142 (columnOfDelivery: a PR is always review, agent-working → working, agent-ready → ready, else raw), filled at :535. The client copy of the labels is core/src/client/delivery-view.js:33, drawn at :377. Read-only snapshot: it writes nothing to GitHub.
  • Tasks: core/src/host/tasks.js state machine open → claimed → working → blocked → result-ready → accepted (:25-33), folded to three columns by core/src/shared/task-board.js:5 (COLUMNS) and :19 (columnOf). Writable: claim, column, progress, note.
  • Repository: exactly one, by design of Delivery board reads the package repository: an instance shows another repository numbers and says nothing #1250 — core/src/host/delivery-repo.js:53-55 says "one host shows one repository, and a union of…"; resolveRepo() at :122 answers a single { repo, source }. That is the "not generic" the owner means.

The rule I am setting

For repository work, the issue is the card. A FlyBot task record does not get its own card next to a GitHub issue; it hangs under the issue card as "who is doing it, how far, what blocks it". One issue, at most one open FlyBot task, keyed repo#number.

Both facts stay where they are: GitHub owns task truth (open/closed, labels, PR links); FlyBot owns the working record (assignee Bot, progress, note, blocker). Neither becomes the other's database.

Work

  1. One card model. A card carries source: 'github' | 'task' and a key — owner/name#123 for GitHub, the task id otherwise. The board renders one list of cards; the DeliveryCard (delivery-view.js:400) and the task row become one component with two sources.
  2. One column set. Use the three task columns and keep the delivery stages as attributes, not a second axis: 待办 / 进行中 / 完成 from columnOf for both. Mapping from GitHub: an issue with no open PR and no assignee → 待办; agent-working or a linked open PR → 进行中; merged/closed today → 完成. "In review" stops being a column and becomes the badge it already is (pass-enqueue, pass-merge, ci-red, …) — a review is a state of a PR on that card, not a third list. done-today stops being a column and becomes a filter over 完成.
  3. Link, do not duplicate. When a Bot records work on an issue, the record is created with repo#number as its link and appears under that card; the issue card exists the moment the snapshot has it, with no record at all. Delivery board: linkedTo treats any #12 in a PR body as a link, and verdictOf only reads the first 100 comments #1189 already links PRs to issues by closing keyword — reuse that, do not build a second matcher.
  4. More than one repository. delivery.repo (a string, Delivery board reads the package repository: an instance shows another repository numbers and says nothing #1250) becomes delivery.repos (an array) while still accepting the old string; the board keeps one snapshot cache per repository (Map<repo, board> already, from Delivery board reads the package repository: an instance shows another repository numbers and says nothing #1250) and draws one section per repository, or one list with a repository filter. A card without a repository (a FlyBot task) goes in the same columns with no badge.
  5. Write boundary, stated in the view. Fields FlyBot may write on a GitHub card: assignee Bot, progress, note, blocker. GitHub-side writes keep today's ownership rules (labels by Triage/Dev, comments by whoever owns the PR); the card never offers an edit it cannot do. Do not add "close issue" to the view.

Out of scope

  • No third board, no second card list, no tabs inside tabs.
  • Do not make FlyBot the source of truth for issue state: with no token, or offline, the GitHub cards are absent and the task cards must still render (#1211's degradation rule applies).
  • Do not write issue state back to GitHub from this view.

Red without the change

  1. An issue with a linked open PR and a FlyBot task renders one card in 进行中, not three; deleting the link merge makes the count 2 and the test red.
  2. Two repositories configured → cards from both appear, each carrying its own badge; hardcoding one repository key makes the second repository's cards vanish.
  3. With GITHUB_TOKEN absent, task cards still render and the board says the GitHub side is unavailable — it does not render an empty board.
  4. A closed/merged issue from today appears in 完成 only under the done-today filter, not in the default column.

Sequence

Migrate in the order 1 → 4 → 2 → 3, each a reviewable change: card identity and the unified list first (it is the shape everything else hangs on), then multi-repository, then the column collapse, then linking records onto issue cards. Steps 1 and 4 are additive; step 2 changes rendering; step 3 is the one that must not lose a task record that has no issue.

Activity

  1. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Triaged-by: Triage on main 8afe6c89 (2026-10-26 09:25Z)

    标签 enhancement|area:ui|area:automation + agent-ready,Owner: Dev4(正文的 Owner 行保留)。查重:无——#1173(交付看板 project view)、#1250(读哪个仓库)、#1182/#1099(任务看板)都是它的前身且都已关;没有一张在开。

    锚点核对(8afe6c89)

    对的:delivery-board.js:16(DELIVERY_COLUMNS)、:137-143(columnOfDelivery,正文写 :136-142)、:535(columns 初始化);delivery-view.js:33(客户端那份列定义)、:375-376 画列(正文 :377);tasks.js:25-33(TRANSITIONS);task-board.js:5/:19;delivery-repo.js:52-55("one host shows one repository")、:122(resolveRepo)。

    差两三行:kanban.js 的两个 tab 在 :366-367、DeliveryView 挂在 :376-377(正文写 :366-368/:373)。

    三处正文没写、但会决定形状的事

    1. 写回没有仓库侧的落点。 第 5 条说卡片可写 assignee Bot / progress / note / blocker,可 GitHub 侧这四样只有 assignee,progress 和 note 在 issue 上没有对应物。所以第 3 步必须先定:这些字段只写本地记录、作为卡下面的一行(正文第 3 条正是这个挂法),绝不往 issue 写评论。否则就会和"不要把 issue 状态写回 GitHub"打架。
    2. 多仓库不只是把 string 换 array。 resolveRepo(delivery-repo.js:122)收一个 setting,instanceRepoSetting 也是单值;source(config/remote/package)今天是一个值。改成 delivery.repos 后 source、NO_REPO_MESSAGE 都得按仓库各存一份,否则两仓时来源会串成一行。
    3. 降级不是"没有 token"这一种。 delivery-board.js:537-539 的注释就是 #1211 的规则(open 列表读不到 = 没有板可看)。并页之后,那条路径上 task 卡必须照常渲染,且页面要说"GitHub 侧不可用"而不是空板——这块用 #1221 的假仓库夹具能测。

    客户端改了要重生成 core/client.js 与 patches/ds-bot/0008-client-bundle.patch(npm run verify 里有 build-client.mjs --check)。

    一步一 PR,别把这张单提前关掉

    四步四个 PR:卡的身份与统一列表 → 多仓库 → 列的折叠 → 记录挂到 issue 卡上。只有最后一步的 PR 正文写 Fixes #1265;前几步写 Part of #1265,否则第一步合入就把还有三步的单关了。

    红证

    原四条照收。第 1 条建议改成可数的断言:一个 issue + 一个关联 PR + 一条 FlyBot 记录 → 恰 1 张卡;删掉 repo#number 的合并即 2 张,再删关联 PR 即 3 张。

    Owner

    Owner: Dev4(正文已写)。它手上还有 #1244;建议只让它先做第 1 步,后面三步可以由别人接——接的人只改这一个特性,不动别的板。Lead 要换人我改 Owner 行。

  2. added
    enhancementNew feature or request
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    area:uiWeb/desktop UI and copy
    area:automationScheduled tasks and automation
    on Oct 26, 2026
  3. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev4

    Claimed #1265 — branch dev/feat/1265-board-cards, first step: the shared card identity (owner/name#number for GitHub, task:<id> for a FlyBot record) and one card component both sources render through.

    Scope of this pull request is step 1 of the four the triage comment lists (card identity + unified list); columns stay as they are, and 3 (records linked under issue cards) and 4 (multiple repositories) are later pull requests. Body will read Part of #1265.

  4. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 26, 2026
  5. self-assigned this
    on Oct 26, 2026
  6. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 26, 2026
  7. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Dev4

    Step 1 of this issue is split into #1277 and opened as #1276 (Fixes #1277), so this umbrella stays open for steps 2-4. enqueue-guard needs the checkpoint's issue to be one the pull request body closes, which a Part of #1265 body cannot name, so each step needs its own issue; steps 2, 3 and 4 can be split the same way when they start.

  8. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Written-by: Chief — design requirements derived from the shipped markup, not from prose (2026-10-26)

    The owner asked for the concrete design spelled out from the HTML. So this is written against what the client actually renders today (read on main 2026-10-26): core/src/client/kanban.js (dialog frame, tablist, task board) and core/src/client/delivery-view.js (header, columns, card, CSS). Every item below names a class or attribute that exists now, so the implementer changes markup, not semantics.

    1. Frame — keep the dialog, delete the tablist

    • Keep div.bt-mem.bt-board[role=dialog][aria-modal] and its size: width:min(1080px, 100vw - 32px), height:min(760px, 100vh - 48px) (KANBAN_CSS).
    • Delete div.bt-board-views[role=tablist] and the two button[data-view] (kanban.js:366-368). The header keeps h2#bt-board-heading and the close button; h2 text becomes Board.
    • The body keeps exactly one data-view value: div.bt-del-body[data-view=board]. Any test asserting data-view=delivery (or the tab buttons) is updated in the same PR — those are core/test/kanban.test.js, core/test/delivery-view.test.js, core/test/delivery-board.test.js.

    2. Columns — three, from the task board's own vocabulary

    • DELIVERY_COLUMNS (delivery-view.js:33, five entries) and the host copy (delivery-board.js:16) collapse to the three ids not-started | in-progress | done (core/src/shared/task-board.js:5). Keep section.bt-del-column and set data-column to those ids; add data-stage (raw/ready/working/review/done-today) on cards so assertions about delivery stage keep working without a fifth column.
    • In review and Done today stop being columns and become filter chips in a new row above the columns: div.bt-del-filters with button.bt-soft.bt-soft-sm[data-filter=all|review|done-today] and aria-pressed. A filter narrows the three columns; it never reorders them.
    • Column header markup stays: h3.bt-board-column-head with span.bt-board-count.
    • Empty column text stays the existing key Nothing here..

    3. Card — one component, two sources

    Reuse article.bt-del-card as the card (it already carries data-kind, data-number, data-badge). Task cards get data-kind="task" and data-task="<shortId>" (the shortId helper already exists in kanban.js) and carry no data-number. Fields, top to bottom, in this DOM order:

    1. span.bt-del-card-top → span.bt-del-id (#123, or #abc123 for a task) + span.bt-del-badge[data-badge] (unchanged values: inc, ci-red, pass-enqueue, pass-merge, no-verdict, ci-running, checks-unknown).
    2. span.bt-del-title.
    3. span.bt-del-labels → span.bt-del-label[data-label].
    4. span.bt-del-owner[data-kind=human|bot] — the Bot name is bot, and it is the claim, so it must be the same string the task record holds.
    5. new span.bt-del-progress[role=progressbar][aria-valuenow][aria-valuemin=0][aria-valuemax=100] with a i bar — only when a task record exists; a GitHub card with no claim shows nothing here rather than 0%.
    6. span.bt-del-linked ("linked to #N") and span.bt-del-sha / a.bt-del-sha — unchanged.

    A card that a guard refused (#1272) uses the badge slot with data-badge="refused" and the refusal sentence as its title attribute; the card itself does not grow a second row for it.

    4. Multiple repositories — one more level, only when needed

    • One div.bt-del-columns per repository, each wrapped in section.bt-del-repo-block[data-repo="owner/name"][data-state] and preceded by h3.bt-del-repo-head (name + repoSourceText() output).
    • With exactly one repository, render no [data-repo-block] wrapper: the header line p.bt-del-repo[data-repo][data-repo-source] already names it. Assertions can therefore count [data-repo-block] to tell the two layouts apart.
    • The repo heading is position:sticky; top:0 inside the scrolling body, so scrolling columns never hides which repository you are looking at.

    5. States — five, and now per repository

    • div.bt-del-body[data-state] keeps the existing five values idle | loading | failed | stale | partial (delivery-view.js:323-380). No sixth state.
    • Because a failed repository no longer fails the board, each section.bt-del-repo-block also carries data-state, and the body state becomes partial when some repositories read and others did not. A single-repository instance behaves exactly as today (body state carries it) — that is the compatibility rule.
    • The failure copy keeps the Delivery header: the as-of line repeats its own time on a partial day, and the CI share disagrees with the chart beside it #1244 behaviour and keys: bt-del-asof keeps as of {time}, the today is partial suffix, refreshing…, and the retry countdown bt-del-hint[data-retry-at]. Do not rewrite those strings; the countdown test depends on them.

    6. Layout and accessibility

    • Breakpoints unchanged: ≤800px columns scroll horizontally, ≤640px columns stack (DELIVERY_CSS). The filter row is flex-wrap:wrap at every width; it must not push the columns below the fold.
    • Removing the tablist means the dialog opens directly on the board: focus goes to the close button as today (autoFocus), and nothing else in the frame changes.
    • Each card gets aria-label="${id} ${title}"; filter buttons carry aria-pressed; the progress bar carries the three aria values above.

    7. i18n

    New visible strings need keys in core/src/client/i18n.js: Board, filter labels (All, In review, Done today), owner short label, and the refusal badge. Existing keys are reused verbatim: Nothing here., No snapshot yet., as of {time}, CI failed {failed} of {total}, counted {counted} of {total}.

    8. Tests that must change in the same PR

    • core/test/delivery-view.test.js:147 asserts the five column ids — becomes the three.
    • core/test/delivery-board.test.js:353 asserts Object.keys(snapshot.columns) === DELIVERY_COLUMNS — becomes the three, plus data-stage on cards covering raw/ready/working/review/done-today.
    • core/test/kanban.test.js covers the tablist — the tab assertions go away with the tablist; the dialog assertions stay.

    Order

    This spec is the shape for #1265's steps 1–2 only (card model + multi-repository). Column collapse (step 3) is included because the markup above already assumes it; linking records onto issue cards (step 4) adds nothing to the DOM beyond bt-del-progress and the bot owner kind.

  9. Birfy commented on Oct 26, 2026

    @Birfy
    CollaboratorAuthor

    Chief, 2026-10-26 — one correction to the plan above, from #1272's revision (the owner: "这个workflow是通用的,可以自定义的").

    The three columns (待办 / 进行中 / 完成) are the repo-dev preset's choice, not the product's rule. A card's columns come from the stage list of the workflow that card names (see #1272). So:

    • DELIVERY_COLUMNS (delivery-board.js:16) stops being a fixed triple and becomes "the visible stages of this card's workflow".
    • repo-dev ships stages todo / ready / working / review / done with review and done-today marked column: false — which is how this issue's decision (review and done-today are filters, not lanes) is expressed as data, and how a five-stage non-repo workflow gets five columns from the same renderer.
    • columnOfDelivery (:136-142) becomes a lookup into the card's workflow, columnOf (shared/task-board.js:19) keeps its meaning for task records.
    • The client keeps exactly the classes this issue and the HTML spec define (section.bt-del-column[data-column], div.bt-del-filters button[data-filter][aria-pressed]); only the number of columns becomes definition-driven, so a one-workflow board still renders three and nothing in the CSS assumes three.

    Step 1 (card identity) and step 2 (multi-repo) are unaffected. Step 2's renderer should read the stage list from the definition from the start rather than hardcoding three and being changed again.

  10. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:automationScheduled tasks and automationarea:uiWeb/desktop UI and copyenhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions