Skip to content

看板 模块 #15

Description

@SATA260

看板 模块

功能职责

看板解决的是:手头这件事上同时开了好几路对话,有的只是问答,有的要进目录改代码,很难一眼看出谁在跑、谁在等你批。于是每件要办的事做成一张卡,对应一个 Work。层级是:一个 Work 下面可以有多个目录,一个目录下面可以有多个会话。信息面板可以写在 Work 上,也可以写在某个目录上:Work 级给这个 Work 里所有会话看,目录级只给挂在这个目录下的会话看。主仓和 worktree 都只是 Work 下面的目录,不是 Work 本身。

  • 开一个 Work,不必先有仓库,也不必先有目录;开的时候就挂上一块空白的 Work 级信息面板
  • 一个 Work 下面可以挂多个目录。人指哪个已有路径就挂哪个。也可以新建一份 Git Worktree:先走已有 Git 能力建检出,建成后自动挂到当前 Work。每个目录也可以有自己的一块信息面板
  • 一个目录下面可以有多个会话。在这个工作路径下开新会话时,会话自带这个目录,不用再手动绑一次
  • 也可以在 Work 上开问答会话,创建时就不带工作路径,不要求 AI 往磁盘上写东西;问答只带 Work 级信息区
  • 每个会话可以自己挂一条 GitHub Issue、多条 GitHub PR。挂在会话上,不挂在信息面板上
  • 一个会话只绑定一种引擎:自有 Agent、Claude Code 或 Codex
  • 看一张 Work 卡时,先列出它有哪些工作目录;每个目录下的会话默认折叠,只看到有哪些会话
  • 折叠着的会话只显示摘要(复用已有会话摘要:用户输入的第一句话)、这条会话是不是在跑、这条会话上待决策的条数。点进去才加载对话正文,走已有的会话加载,看板不另做一套
  • 目录上和整张卡上显示:正在跑的会话数量、待决策的数量
  • 这张卡上的待批可以集中列出,点进去仍走各引擎自己的审批
  • 开回合时带上 Work 级信息区;会话挂了目录再带上该目录的信息区;再带上这个会话已挂的 Issue 快照和 PR。这些不写成仓库里的文件
  • 自有 Agent 挂到这张卡后,还会另装用户习惯(全局)和这件事自己的记忆;Claude 和 Codex 不碰记忆,但两级信息面板它们都看

边界

  • 这不叫项目,也不等于一个 Git 仓库。一张卡是手头要办的一件事。可以全程没有仓库、没有工作目录,只问答。
  • 信息面板有两级,都可以写,互不覆盖。Work 级一块,挂在这个 Work 上,这个 Work 里所有会话开回合都带上。目录级一块,挂在某一个目录上,只有这个目录下面的会话才带上。都是一块空白,人爱写提示词、规范还是目标都行,不拆成固定栏目。不能挂到会话上。不是记忆。记忆仍只由自有 Agent 装载;不要把记忆正文抄进信息区,也不要把信息区当成记忆来写。
  • Issue 和 PR 挂在某一个会话上,不挂 Work,不挂任何一级信息面板,不挂目录。一个会话最多一条 Issue,PR 可以多条。不挂也合法。只拉快照给人看,不关 Issue、不开 PR、不合入、不按它们自动开跑。解开不影响两级信息区,也不影响别的会话上挂的东西。
  • 同一张卡上的两个会话可以挂不同的 Issue 和 PR。换工具是新会话,旧会话上的 Issue、PR 不跟着走,要挂再挂。
  • 目录挂在 Work 下面。一个 Work 可以有多个目录。目录可以有自己的信息面板,但没有 Issue 或 PR;那些在会话上。
  • 写代码的会话属于某一个目录。在该目录下创建会话时,目录已经带上,不是先开会话再绑路径。一个目录下面可以有多个会话,可以同时写,只提示有几路在写,不排队、不加锁。同目录上的改动切不开到某个会话。要隔离就新建一份 Git Worktree(走已有 Git 能力),建成后自动挂到这个 Work;也可以人自己指定另一个已有目录挂上来。
  • 会话不能同时属于两个目录。换工具是在同一个目录下开新会话,新会话同样自带这个目录,继续读 Work 级信息区和这个目录的信息区;Issue、PR 要在新会话上重新挂。
  • 问答会话创建时就不带目录。没有工作路径,不该假定 AI 会改文件,只带 Work 级信息区,也可以挂 Issue、PR。不带任何目录级信息区。不能事后再给问答会话补绑一个目录;要写代码就在目标目录下新开一条。
  • 不是这个 Work 挂上的路径,不能当会话的工作目录。卡上的目录名单以人挂上的为准:想加谁的已有目录就加谁的。新建 Git Worktree 不是本模块的能力,走已有 Git;Git 建成后本模块只负责把那条路径挂到当前 Work。Git 没建成,就不挂。从哪份仓库开,由人指定。若挂的是 Git 目录,此刻在哪条分支、脏不脏、是不是 worktree,都现问 Git、现显示。人在本地用 Git 随便切分支,目录不跟某条分支绑死,本模块也不记下分支。人从卡上拿掉目录,或磁盘上没了,这个目录就不能再开会话。本模块不另造一份和 Git 对不上的目录库存。
  • 看板只展示目录、折叠会话、正在跑的会话数量、待决策的数量。不装会话正文,不另写摘要。摘要复用已有会话管理里那条:用户输入的第一句话。点进会话才加载真实内容,走已有会话回放,不新开一套会话仓库。不把「有没有在跑」收成是/否,要报数量。
  • 收件箱只统一「这张卡上谁在等你」,不把三种引擎的问票收成同一种结构。裁决交给对应引擎已有的审批。
  • 这件事的记忆是这张卡的记忆,个人习惯是用户全局记忆。都不按目录再拆一层。没挂目录的问答同样走这两层记忆,只要引擎是自有 Agent。
  • 只有自有 Agent 会装记忆。Claude 和 Codex 不装、不注入记忆,继续用它们自己的说明和配置。
  • 本模块不读写记忆正文、不建专题、不索引对话。自有 Agent 的对话检索按这张卡隔离;Claude/Codex 的实录不进这张卡的检索。
  • 人自己的编辑器和 Agent 同时写同一个已挂目录是允许的,卡上要看得见。
  • 本模块不负责在 GitHub 上创建或合入 PR,只把已经挂上的 PR 拉来给人看。也不负责跑开发服务、Agent 之间传话。
  • 挂了目录的 Claude / Codex 会话,工作目录就是那个目录。没挂目录就没有工作路径,不要退回某个进程级仓库根。

内部拆分

事项(Work)

管手头这件事:开 Work、标题。开 Work 时挂上一块空白的 Work 级信息面板。不管信息区里写了什么,不管下面挂了几个目录,不管每个目录下有哪些会话,不管记忆正文。

type Work struct {
	ID    string
	Title string
}

func Create(title string) (Work, error)         // 开一个 Work,同时挂上一块空白的 Work 级信息面板;不必有仓库或目录。
func Get(workID string) (Work, error)           // 读取这个 Work。
func List() ([]Work, error)                     // 列出手上的 Work。
func Rename(workID, title string) (Work, error) // 改这个 Work 的标题。

信息面板(Info)

管两级信息区:Work 级一块,目录级每个目录一块。人都是自由往里写提示词或任何说明。不管会话上挂的 Issue 和 PR,不管记忆。

type Info struct {
	WorkID   string
	Checkout string // 空表示 Work 级;有路径表示这个目录级
	Body     string // 信息区,人自由写。不是记忆
}

func GetWork(workID string) (Info, error)                   // 读取这个 Work 级信息面板。
func ApplyWork(workID, body string) (Info, error)           // 写下或改掉 Work 级信息区。
func GetDir(workID, checkout string) (Info, error)          // 读取这个目录级信息面板。
func ApplyDir(workID, checkout, body string) (Info, error)  // 写下或改掉这个目录级信息区。

议题(Issue)

管某一个会话上挂的那一条 GitHub Issue 和只读快照。一个会话最多一条。不管信息区,不管 PR,不写回 GitHub。

type IssueRef struct {
	SessionID string
	Repo      string // owner/name
	Number    int
	Title     string
	Body      string
	SyncedAt  string // 快照时间;空表示还没拉过
}

func Bind(sessionID, repo string, number int) (IssueRef, error) // 把一条 GitHub Issue 挂到这个会话上,未拉过也可以先记下编号。已有一条则换掉。
func Refresh(sessionID string) (IssueRef, error)                // 向 GitHub 拉标题和正文,盖写快照。
func Unbind(sessionID string) error                             // 从这个会话上解开 Issue,两级信息区和这个会话上的 PR 还在。
func Snapshot(sessionID string) (IssueRef, error)               // 读已落下的快照,不访问 GitHub。

拉取请求(PullRequest)

管某一个会话上挂的 GitHub PR 列表和只读快照,用来看这个会话已经对应哪些拉取请求。可以挂多条。不管信息区,不管 Issue,不在 GitHub 上开或合。

type PullRef struct {
	SessionID string
	Repo      string // owner/name
	Number    int
	Title     string
	State     string // 现从 GitHub 看到的状态,如 open、merged
	SyncedAt  string // 快照时间;空表示还没拉过
}

func Attach(sessionID, repo string, number int) (PullRef, error) // 把一条 GitHub PR 挂到这个会话上,未拉过也可以先记下编号。
func Detach(sessionID, repo string, number int) error            // 从这个会话上拿掉这条 PR,两级信息区和这个会话上的 Issue 还在。
func List(sessionID string) ([]PullRef, error)                   // 列出这个会话上已挂的 PR。
func Refresh(sessionID string) ([]PullRef, error)                // 向 GitHub 拉已挂 PR 的标题和状态,盖写快照。
func Snapshot(sessionID string) ([]PullRef, error)               // 读已落下的快照,不访问 GitHub。

目录(Checkout)

管这个 Work 下面的一个工作路径。一个 Work 可以有多个目录;会话挂在目录下面,一个目录可以有多个会话。不管信息区里写了什么,不管 Issue、不管 PR、不管对话正文,不加写锁,也不把目录绑死在某条分支上。一个 Work 可以一个目录都没有。挂目录时带上一块空白的目录级信息面板。不建 Git Worktree;那是已有 Git 的事,建成后把路径交给本块挂上。

type CheckoutKind string // folder | primary | worktree

type Checkout struct {
	WorkID string
	Kind   CheckoutKind
	Path   string // 挂上的本地路径
}

func Attach(workID, path string) (Checkout, error)              // 把一条已有路径挂到这个 Work 上,同时挂上一块空白的目录级信息面板。不建 worktree;Git 刚建成的路径也走这里。
func Get(workID, path string) (Checkout, error)                 // 读取这个已挂目录。
func Detach(workID, path string) error                          // 从卡上拿掉这个目录,不删磁盘上的文件。
func List(workID string) ([]Checkout, error)                    // 列出这张卡上已挂的目录。
func Head(workID, path string) (branch string, err error)       // 现问 Git 这个目录此刻在哪条分支;不记下。
func Writers(workID, path string) ([]string, error)             // 列出这个目录下面正在跑的会话,供卡片提示并发。

挂载(Placement)

管在哪个 Work、哪个目录下开会话,以及会话用哪个引擎。在某个工作路径下开的会话,创建时就带上这个目录,不用再绑。问答会话创建时就不带目录。自有 Agent 要标上这个 Work,好让已有记忆按这件事装载。不管回合怎么跑,不管记忆正文,不管各引擎内部编号,不管会话上挂的 Issue 和 PR。开回合的包里,Work 级信息区总是带上;有目录再带目录级信息区;Issue 和 PR 来自这个会话。

type Engine string // native | claude | codex

type Packet struct {
	WorkInfo  string    // Work 级信息区
	DirInfo   string    // 目录级信息区;问答会话为空
	IssueBody string    // 这个会话已挂 Issue 的快照;没挂则为空
	Pulls     []PullRef // 这个会话已挂 PR 的标题和状态
}

func StartInDir(workID, checkout string, engine Engine) (sessionID string, err error) // 在这个已有工作路径下开新会话,会话自带这个目录,不用再绑。自有 Agent 同时标上这个 Work 作为记忆范围。
func StartTalk(workID, engine Engine) (sessionID string, err error)                   // 开问答会话,创建时就不带工作路径。自有 Agent 同时标上这个 Work 作为记忆范围。
func OfSession(sessionID string) (workID, checkout string, engine Engine, err error)  // 读会话属于哪个 Work、哪个目录;checkout 空就是问答。
func ListByWork(workID string) ([]string, error)                                      // 列出这个 Work 里的全部会话,含问答。
func ListByCheckout(workID, checkout string) ([]string, error)                        // 列出这个目录下面的会话。
func Packet(sessionID string) (Packet, error)                                         // 取出 Work 级信息区、所在目录的信息区(若有),以及这个会话上的 Issue 和 PR;不含记忆。

看板(Board)

管一张 Work 卡上要给人看的东西:有哪些工作目录,每个目录下折叠着哪些会话,正在跑的会话有几条,待决策有几条。会话行只带摘要,摘要复用已有会话管理里用户输入的第一句话。不管对话正文,不写信息区,不裁审批。点进会话不归本块,走已有会话加载。

type SessionView struct {
	SessionID string
	Engine    Engine
	Summary   string // 复用已有会话摘要,即用户输入的第一句话;不是另写一份
	Running   bool   // 这个会话此刻是否在跑
	Pending   int    // 这个会话上还没批的决策条数
}

type CheckoutView struct {
	Checkout      Checkout
	Info          Info   // 这个目录级信息面板
	CurrentBranch string // 现问 Git,不记下;不是 Git 目录则为空
	Sessions      []SessionView // 默认折叠,只露摘要和状态
	Running       int           // 这个目录下正在跑的会话数量
	Pending       int           // 这个目录下待决策的数量
	Dirty         bool
	SharedWriters int // 这个目录下面并行在写的会话数;大于 1 就要提示
}

type Card struct {
	Work      Work
	Info      Info          // Work 级信息面板
	Talking   []SessionView // 没挂目录、只在问答的会话,同样只露摘要
	Checkouts []CheckoutView
	Running   int // 这张卡上正在跑的会话数量
	Pending   int // 这张卡上待决策的数量
}

func Cards() ([]Card, error)           // 列出所有 Work 卡,每张带目录概况、正在跑的会话数量、待决策的数量。
func Card(workID string) (Card, error) // 打开一张卡:工作目录、各目录下折叠的会话摘要、正在跑的会话数量、待决策的数量。不装任何会话正文。

收件箱(Inbox)

管一张卡上跨会话的待批,含问答里弹出的问票,也含某个目录上写代码的问票。把人的裁决交给对应引擎。不把三种引擎收成一种问票。

type InboxItem struct {
	ID        string
	SessionID string
	WorkID    string
	Checkout  string // 问答可空
	Engine    Engine
	Summary   string
	Paths     []string // 能拿到的涉及路径;问答通常为空
}

func List(workID string) ([]InboxItem, error)          // 列出这张卡上等你的审批。
func Open(itemID string) (sessionID string, err error) // 回到发出这条问票的会话。
func Decide(itemID string, approved bool, scope string, choice string, values []string) error // 按问票所属引擎把人的作答交给该引擎已有的审批。

流程

用户先开一个 Work,可在 Work 级信息区里写;可以立刻开问答,创建时就不带目录。需要改代码了,往这个 Work 下挂目录,也可在目录级信息区里写,再在某个目录下开会话——会话自带这个目录。每个会话自己挂 Issue 和 PR。问答只带 Work 级信息区;目录下的会话两级都带。自有 Agent 另外再装记忆,Claude / Codex 只看信息面板和本会话挂载、不装记忆。审批在这张卡的收件箱里看。换工具就在同一个目录下再开一条,同样自带这个目录,Issue、PR 要重新挂。

// 用户开一个 Work,写 Work 级信息区,开问答
Work.Create(...)
Info.ApplyWork(...)
Placement.StartTalk(...) // 问答,不带目录
Issue.Bind(...)
Issue.Refresh(...)
PullRequest.Attach(...)
PullRequest.Refresh(...)
Board.Card(...)
Placement.Packet(...)
Turn.Start(...) // 自有 Agent 装记忆;包里是 Work 级信息区,加上这个会话的 Issue、PR

// 用户要动手改代码,挂上一个已有目录,写目录级信息区
Checkout.Attach(...)
Info.ApplyDir(...)

// 用户新建一份 Git Worktree,建成后自动挂到当前 Work,再在这个目录下开会话
Git.AddWorktree(...)
Checkout.Attach(...)
Placement.StartInDir(...) // engine=claude,自带这个目录
Placement.StartInDir(...) // engine=codex,同一个目录,同样自带
Board.Card(...)
Checkout.Writers(...)
Placement.Packet(...)
Turn.Start(...) // Claude/Codex 不装记忆;带 Work 级和目录级信息区,以及这个会话自己挂的 Issue、PR

// 用户在这张卡的收件箱处理审批
Inbox.List(...)
Inbox.Decide(...)
Approval.Decide(...)
Turn.Continue(...)

// 用户换工具:在同一目录下再开一条,自带这个目录;Issue、PR 要重新挂
Placement.StartInDir(...)
Issue.Bind(...)

// 用户看这张卡:有哪些目录、目录下折叠的会话、正在跑几条、待决策几条
Board.Card(...)

// 用户点进一条会话,才加载对话正文(已有会话机制,看板不另装)
Session.Get(...)

// 用户重连,再看这张卡,仍不装会话正文
Board.Card(...)
Inbox.List(...)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    moduleSingle-module objects, interfaces, and design

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions