Repository navigation
One board: the delivery board and the task board are one board, and a repository task is its issue #1265
Description
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-368rendersTasks/Delivery, and:373mountsDeliveryView. So they are already in one place, but nothing is shared. - Delivery:
core/src/host/delivery-board.js:16DELIVERY_COLUMNS = ['raw','ready','working','review','done-today'], staged at:136-142(columnOfDelivery: a PR is alwaysreview,agent-working→working,agent-ready→ready, elseraw), filled at:535. The client copy of the labels iscore/src/client/delivery-view.js:33, drawn at:377. Read-only snapshot: it writes nothing to GitHub. - Tasks:
core/src/host/tasks.jsstate machineopen → claimed → working → blocked → result-ready → accepted(:25-33), folded to three columns bycore/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-55says "one host shows one repository, and a union of…";resolveRepo()at:122answers 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
- One card model. A card carries
source: 'github' | 'task'and a key —owner/name#123for GitHub, the task id otherwise. The board renders one list of cards; theDeliveryCard(delivery-view.js:400) and the task row become one component with two sources. - One column set. Use the three task columns and keep the delivery stages as attributes, not a second axis:
待办 / 进行中 / 完成fromcolumnOffor both. Mapping from GitHub: an issue with no open PR and no assignee → 待办;agent-workingor 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-todaystops being a column and becomes a filter over 完成. - Link, do not duplicate. When a Bot records work on an issue, the record is created with
repo#numberas its link and appears under that card; the issue card exists the moment the snapshot has it, with no record at all. Delivery board:linkedTotreats any#12in a PR body as a link, andverdictOfonly reads the first 100 comments #1189 already links PRs to issues by closing keyword — reuse that, do not build a second matcher. - More than one repository.
delivery.repo(a string, Delivery board reads the package repository: an instance shows another repository numbers and says nothing #1250) becomesdelivery.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. - 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
- 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.
- Two repositories configured → cards from both appear, each carrying its own badge; hardcoding one repository key makes the second repository's cards vanish.
- With
GITHUB_TOKENabsent, task cards still render and the board says the GitHub side is unavailable — it does not render an empty board. - A closed/merged issue from today appears in 完成 only under the
done-todayfilter, 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
Birfy commentedon Oct 26, 2026
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)。三处正文没写、但会决定形状的事
resolveRepo(delivery-repo.js:122)收一个setting,instanceRepoSetting也是单值;source(config/remote/package)今天是一个值。改成delivery.repos后source、NO_REPO_MESSAGE都得按仓库各存一份,否则两仓时来源会串成一行。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 行。Birfy commentedon Oct 26, 2026
Written-by: Dev4
Claimed #1265 — branch
dev/feat/1265-board-cards, first step: the shared card identity (owner/name#numberfor 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.Birfy commentedon Oct 26, 2026
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-guardneeds the checkpoint'sissueto be one the pull request body closes, which aPart of #1265body cannot name, so each step needs its own issue; steps 2, 3 and 4 can be split the same way when they start.Birfy commentedon Oct 26, 2026
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) andcore/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
div.bt-mem.bt-board[role=dialog][aria-modal]and its size:width:min(1080px, 100vw - 32px),height:min(760px, 100vh - 48px)(KANBAN_CSS).div.bt-board-views[role=tablist]and the twobutton[data-view](kanban.js:366-368). The header keepsh2#bt-board-headingand the close button;h2text becomesBoard.data-viewvalue:div.bt-del-body[data-view=board]. Any test assertingdata-view=delivery(or the tab buttons) is updated in the same PR — those arecore/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 idsnot-started | in-progress | done(core/src/shared/task-board.js:5). Keepsection.bt-del-columnand setdata-columnto those ids; adddata-stage(raw/ready/working/review/done-today) on cards so assertions about delivery stage keep working without a fifth column.In reviewandDone todaystop being columns and become filter chips in a new row above the columns:div.bt-del-filterswithbutton.bt-soft.bt-soft-sm[data-filter=all|review|done-today]andaria-pressed. A filter narrows the three columns; it never reorders them.h3.bt-board-column-headwithspan.bt-board-count.Nothing here..3. Card — one component, two sources
Reuse
article.bt-del-cardas the card (it already carriesdata-kind,data-number,data-badge). Task cards getdata-kind="task"anddata-task="<shortId>"(theshortIdhelper already exists in kanban.js) and carry nodata-number. Fields, top to bottom, in this DOM order:span.bt-del-card-top→span.bt-del-id(#123, or#abc123for a task) +span.bt-del-badge[data-badge](unchanged values:inc,ci-red,pass-enqueue,pass-merge,no-verdict,ci-running,checks-unknown).span.bt-del-title.span.bt-del-labels→span.bt-del-label[data-label].span.bt-del-owner[data-kind=human|bot]— the Bot name isbot, and it is the claim, so it must be the same string the task record holds.span.bt-del-progress[role=progressbar][aria-valuenow][aria-valuemin=0][aria-valuemax=100]with aibar — only when a task record exists; a GitHub card with no claim shows nothing here rather than 0%.span.bt-del-linked("linked to #N") andspan.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 itstitleattribute; the card itself does not grow a second row for it.4. Multiple repositories — one more level, only when needed
div.bt-del-columnsper repository, each wrapped insection.bt-del-repo-block[data-repo="owner/name"][data-state]and preceded byh3.bt-del-repo-head(name +repoSourceText()output).[data-repo-block]wrapper: the header linep.bt-del-repo[data-repo][data-repo-source]already names it. Assertions can therefore count[data-repo-block]to tell the two layouts apart.position:sticky; top:0inside 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 valuesidle | loading | failed | stale | partial(delivery-view.js:323-380). No sixth state.section.bt-del-repo-blockalso carriesdata-state, and the body state becomespartialwhen some repositories read and others did not. A single-repository instance behaves exactly as today (body state carries it) — that is the compatibility rule.bt-del-asofkeepsas of {time}, thetoday is partialsuffix,refreshing…, and the retry countdownbt-del-hint[data-retry-at]. Do not rewrite those strings; the countdown test depends on them.6. Layout and accessibility
flex-wrap:wrapat every width; it must not push the columns below the fold.autoFocus), and nothing else in the frame changes.aria-label="${id} ${title}"; filter buttons carryaria-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),ownershort 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:147asserts the five column ids — becomes the three.core/test/delivery-board.test.js:353assertsObject.keys(snapshot.columns) === DELIVERY_COLUMNS— becomes the three, plusdata-stageon cards covering raw/ready/working/review/done-today.core/test/kanban.test.jscovers 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-progressand thebotowner kind.Birfy commentedon Oct 26, 2026
Chief, 2026-10-26 — one correction to the plan above, from #1272's revision (the owner: "这个workflow是通用的,可以自定义的").
The three columns (
待办 / 进行中 / 完成) are therepo-devpreset'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-devships stagestodo / ready / working / review / donewithreviewanddone-todaymarkedcolumn: 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.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.
fix(delivery): a partial board's totals stay unknown (#1297)
feat(workflow): the repo-dev preset and the fact providers (ThinkFlow…
feat(delivery): read more than one repository on the delivery board (T…