# Matt 工作流 × 飞书 Skills × Trellis Coding 闭环总结 > 状态:六个 Feishu workflow skills 已落地;Coding 入队、Trellis 映射、Ticket 部分收口和 Spec 最终收口已通过真实 Base POC。 > 更新时间:2026-07-27 > 实际验证项目:`/Users/yuxuanhui/Documents/ai-workflow/project/经销商政策` > 范围:工作进入、需求澄清、Spec、Tickets、Triage、Inline/Trellis Coding 路由、实现证据、部分收口、归档和最终对账。具体编码规范仍由目标项目自己的 Trellis specs 和 coding skills 决定。 ## 1. 最终结论 这次工作补齐了 Matt 工作流在飞书上的实现侧闭环。现在不只是把需求和计划放进 Base/Wiki,还可以从 Base 选择当前用户负责的工作,路由到 Inline 或 Trellis,实现后按 Ticket 逐步验收,最后在严格门禁下关闭父 Spec。 当前完整模型是: - **Feishu Base** 管业务状态、责任人、父子关系、依赖、进度和可查询队列。 - **Feishu Wiki / Docs** 管 Spec、Triage Notes、Agent Brief、Human Brief 等长文档。 - **Trellis** 管复杂开发的本地任务生命周期、规划产物、执行快照、checkpoint、归档和 journal。 - **repo** 管代码、测试、commit、ADR、领域知识和可回放的实现证据。 - **用户** 决定开始哪项工作,并对每张 Ticket 和最终 Spec 分别执行人类 review。 - **Agent** 负责查询、分析、路由、组织证据和提出精确 patch,但不能绕过人类 review 决定终态。 - **lark-cli bot** 执行 API;当前已验证人类用户写入 `最后更新人`;`负责人`始终表示工作责任,不被 bot 或操作归因覆盖。 ```mermaid flowchart LR U["用户
选择、review、确认外部写入"] --> Skills["六个 Feishu workflow skills
流程编排与门禁"] Skills --> Auth["lark-cli 身份门禁
bot + user verified"] Auth -->|"--as bot"| Base["Feishu Base
状态、关系、队列、责任"] Auth -->|"--as bot"| Wiki["Feishu Wiki / Docs
Spec 与长叙述"] Skills --> Trellis["Trellis
复杂任务生命周期与本地上下文"] Skills --> Repo["Repository
代码、测试、commit、ADR"] Wiki -->|"产物文档"| Base Base -->|"record IDs + 验收边界"| Trellis Base -->|"Tickets / frontier"| Trellis Trellis -->|"checkpoint / archive"| Skills Repo -->|"验证证据 / 代码引用"| Skills Skills -->|"最小 patch + 回读"| Base ``` ## 2. 六个 Feishu workflow skills 六个 skills 都是全局 skill,source of truth 位于 `~/.agents/skills//`。它们都配置了 `policy.allow_implicit_invocation: false`,因此涉及真实飞书读写时要求显式调用,不靠模糊意图静默修改外部系统。 | Skill | 负责的阶段 | 主要输入 | 主要产物 | 明确不负责 | |---|---|---|---|---| | `setup-workflow-skills-feishu` | 仓库接入 | Base URL、Wiki 根 URL、Reuse/Bootstrap 选择 | repo tracker 合约;Bootstrap 时创建 schema、setup Wiki 文档和 POC 记录 | 不在 Reuse 模式修 schema;不猜资源地址 | | `to-spec-feishu` | 对话 → 可执行规格 | 已澄清对话、代码库、领域词汇、测试 seam | 一份 Wiki Spec + 一条 Base `PRD/Spec` | 不采访式重新澄清;不在 Wiki 未回读前创建 Base Spec | | `to-tickets-feishu` | Spec → 垂直切片 | 已批准 Spec、Ticket 粒度、依赖边 | 每个切片一条 Base `实现 Ticket` | V1 不为 Ticket 创建 Wiki;不关闭或修改父 Spec | | `triage-feishu` | Issue/PR 分诊 | 外部请求、代码验证、维护者决定、报告人反馈 | Base Issue 状态 + 可选 Wiki Triage dossier | 不在推荐阶段写状态;不重复创建 dossier | | `start-work-feishu` | Coding 入队 | 当前用户、Spec/Ticket 队列、用户选择、路由确认 | Inline 会话绑定,或 `1 Spec = 1 task` 的 Trellis mapping;Base `进行中`回写 | 不实现代码;不初始化 Trellis;不启动 sibling Tickets | | `close-work-feishu` | Coding 收口 | Spec/Tickets、repo/Trellis 证据、人类 review、确认 patch | Ticket 部分收口、Spec 最终收口、失败后的幂等对账 | 不根据测试或 archive 自动完成;不替用户选择完成项 | ### 2.1 Skill 之间的调用关系 ```mermaid flowchart TD Setup["setup-workflow-skills-feishu
建立项目级 tracker 合约"] --> Intake{"工作从哪里进入?"} Intake -->|"已澄清想法 / 对话"| Spec["to-spec-feishu
Wiki Spec + Base Spec"] Intake -->|"Issue / PR / 表单"| Triage["triage-feishu
分类、验证、澄清、Brief"] Triage -->|"needs-info"| Feedback["等待报告人反馈"] Feedback -->|"最后反馈时间 > 最后分诊时间"| Triage Triage -->|"ready-for-agent 且需要正式规格"| Spec Triage -->|"ready-for-agent 且已足够明确"| Start Triage -->|"ready-for-human"| Human["人类处理"] Triage -->|"wontfix / out-of-scope"| Stop["保留关闭原因与审计记录"] Spec --> Tickets["to-tickets-feishu
Base Ticket 依赖图"] Spec --> Start["start-work-feishu
选择并开始"] Tickets --> Start Start --> Coding["Inline 或 Trellis Coding"] Coding --> Close["close-work-feishu
部分 / 最终 / 对账"] Close -->|"仍有未完成 Ticket"| Coding Close -->|"全部门禁满足"| Done["Base Spec 已完成
工作流闭环"] ``` ## 3. 端到端工作流总图 下面这张图把需求侧、规划侧、实现侧和收口侧放在同一条链路中。 ```mermaid flowchart TD A["仓库首次接入"] --> B["Setup:验证 Base/Wiki、生成 docs/agents 合约"] B --> C{"请求入口"} C -->|"外部 Issue / PR / 表单"| D["Triage:验证事实与分类"] D --> D1{"维护者 outcome"} D1 -->|"needs-info"| D2["Wiki Triage Notes
等待反馈"] D2 --> D D1 -->|"ready-for-human"| D3["Human Brief / 人类处理"] D1 -->|"wontfix"| D4["关闭说明
必要时 repo .out-of-scope"] D1 -->|"ready-for-agent"| E{"是否需要正式 Spec?"} C -->|"已澄清对话"| F["to-spec:确认测试 seam"] E -->|"是"| F E -->|"否,范围足够小"| J F --> G["Wiki Spec 写入并 fetch 回读"] G --> H["Base Spec:ready-for-agent"] H --> I{"是否需要拆 Ticket?"} I -->|"是"| I1["to-tickets:垂直切片 + blocker 图"] I -->|"否,简单 Spec"| J["start-work:工作队列"] I1 --> J J --> K["用户选择 record ID"] K --> L["写前重读:负责人、状态、父项、依赖、更新时间"] L --> M{"Inline 还是 Trellis?"} M -->|"Inline"| N["冻结当前会话 record IDs"] M -->|"Trellis"| O["查找/创建唯一 task
持久化 mapping 与 artifacts"] N --> P["用户确认选择、路由和开始 patch"] O --> P P --> Q["Base 写进行中 + 实现 + 最后更新人"] Q --> R["record-get 回读"] R -->|"Trellis 新任务"| S["task.py start → in_progress"] R -->|"Inline"| T["Coding 与验证"] S --> T T --> U["收集 diff、测试、浏览器验收、未运行项、代码引用"] U --> V["close-work:逐 Ticket 验收映射"] V --> W["人类 review 并选择可关闭 Ticket IDs"] W --> X["Ticket 最小 patch + 逐条回读"] X --> Y{"所有子 Tickets 已完成?"} Y -->|"否"| T Y -->|"是"| Z{"最终 Spec 门禁"} Z -->|"Inline:Spec 证据 + 人类最终 review"| Z1["展示 Spec-only patch"] Z -->|"Trellis:再加 archive + status=completed"| Z1 Z1 --> Z2["用户第二次确认 Spec patch"] Z2 --> Z3["更新 Spec + record-get 回读"] Z3 --> End["最终闭环完成"] ``` ## 4. 四类事实源与产物关系 ### 4.1 产物关系总图 ```mermaid flowchart LR External["外部 Issue / PR / 表单"] -->|"来源链接 / 外部编号 / 报告人"| Issue["Base:需求/Issue"] Issue -->|"产物文档"| TriageDoc["Wiki:Triage — 标题"] Spec["Base:PRD/Spec"] -->|"所属父项"| Issue Spec -->|"产物文档"| SpecDoc["Wiki:Spec — 标题"] Ticket1["Base:实现 Ticket A"] -->|"所属父项"| Spec Ticket2["Base:实现 Ticket B"] -->|"所属父项"| Spec Ticket2 -->|"前置依赖"| Ticket1 Spec -->|"specRecordId,一对一"| Task["Trellis task"] Ticket1 -.->|"ticketRecordIds / snapshot"| Task Ticket2 -.->|"ticketRecordIds / snapshot"| Task Task --> PRD["prd.md
Spec 来源与验收边界"] Task --> Design["design.md
复杂任务技术设计"] Task --> Impl["implement.md
Ticket frontier + checkpoint"] Task --> JSONL["implement.jsonl / check.jsonl
必要 spec / research 清单"] Task --> Archive["archive task.json
status=completed"] Repo["repo:代码 / 测试 / ADR / commits"] -->|"验证证据 / 代码引用"| Ticket1 Repo -->|"验证证据 / 代码引用"| Ticket2 Archive -->|"最终生命周期门禁"| Spec Ticket1 -->|"全部严格已完成"| Spec Ticket2 -->|"全部严格已完成"| Spec ``` ### 4.2 哪个系统保存什么 | 信息 | 事实来源 | 说明 | |---|---|---| | 当前业务状态、负责人、完成度、优先级、时间 | Base | 面向队列、筛选、自动化和团队可见性 | | 父子层级和 blocker 边 | Base `所属父项`、`前置依赖` | 使用真实 record ID;用于计算 frontier | | Spec 与 Triage 长叙述 | Wiki / Docs | 可读、可追加、可审计 | | 复杂任务的本地生命周期 | Trellis | planning、in_progress、archive、journal | | 当前复杂任务的执行快照 | Trellis `prd.md`、`implement.md` | 可从 Base/Wiki 刷新,不反向覆盖 Base 业务事实 | | 代码、测试、commit、ADR、领域知识 | repo | 版本控制事实来源 | | 完成证明 | Base `验证证据` + `代码引用` | 内容来自 repo、真实命令、浏览器验收和 Trellis checkpoint | | 被拒绝 enhancement 的持久决定 | repo `.out-of-scope/` | Wiki 只记录 outcome/link,不复制第二份 canonical decision | ## 5. Setup、Spec、Tickets 与 Triage 的逻辑 ### 5.1 Setup:建立可执行 tracker 合约 ```mermaid flowchart TD A["读取仓库现状"] --> B["用户提供 Base URL + Wiki 根 URL"] B --> C{"Setup 模式"} C -->|"Reuse,推荐"| D["只读验证身份、资源和完整 schema"] C -->|"Bootstrap"| E["展示缺失字段、Wiki setup 标题、POC 标题"] D --> F{"29 字段兼容?"} F -->|"否"| G["停止;询问切换 Bootstrap 或单独 repair 授权"] F -->|"是"| H["展示 repo-local 合约草案"] E --> I["用户确认外部写入"] I --> J["只创建缺失字段"] J --> K["创建 Wiki setup 文档并 fetch"] K --> L["创建 POC Base 记录并 record-get"] L --> H H --> M["生成 AGENTS/CLAUDE Agent skills 区块"] M --> N["生成 docs/agents/issue-tracker.md"] N --> O["生成 domain / triage 合约"] ``` Setup 的关键产物是项目级 `docs/agents/issue-tracker.md`。后续所有 Feishu skills 必须先读它,从中取得真实 Base、table、view、主字段、Wiki root、字段取值、身份和完成门禁,不能从别的项目或相似标题推断资源。 ### 5.2 to-spec:一份长规格 + 一条可查询记录 ```mermaid sequenceDiagram participant U as 用户 participant S as to-spec-feishu participant W as Wiki / Docs participant B as Base S->>S: 读取对话、代码库、领域词汇和 ADR S-->>U: 提议最高可用测试 seam U->>S: 确认 seam S->>B: 按真实主字段精确查重 S->>W: 创建 Spec — 标题 S->>W: append 完整 Spec XML S->>W: docs +fetch alt Wiki 内容完整 S->>B: 创建 PRD/Spec 记录 S->>B: record-get 回读全部字段 else Wiki 写入或 fetch 不完整 S-->>U: 停止,不创建 Base Spec end ``` Wiki Spec 包含 Problem Statement、Solution、User Stories、Implementation Decisions、Testing Decisions、Out of Scope 和 Further Notes。Base Spec 保存 `ready-for-agent`、Wiki 链接、摘要、验收标准和可选来源 Issue。 ### 5.3 to-tickets:两遍发布依赖图 ```mermaid flowchart TD Spec["已批准 Spec"] --> Slice["拆成单上下文可验证的 tracer-bullet"] Slice --> Edge["声明真实 blocker"] Edge --> Quiz["用户确认粒度、拆分、合并和依赖"] Quiz -->|"调整"| Slice Quiz -->|"批准"| Parent["解析父 Spec record ID"] Parent --> Search["逐标题查重"] Search --> Pass1["Pass 1:按依赖顺序创建 Ticket
暂不写 blocker"] Pass1 --> IDs["冻结返回的 record IDs"] IDs --> Pass2["Pass 2:写所属父项 + 前置依赖"] Pass2 --> Verify["逐条回读父项、blocker 集、验收和最后更新人"] Verify --> Frontier["frontier = 状态可开始
且每个 blocker 恰好为已完成"] ``` Ticket V1 不创建 Wiki 文档,`产物文档`留空。实现者通过 `所属父项`找到父 Spec,再从父 Spec 的 `产物文档`取得完整规格。 ### 5.4 triage:请求进入工程流程的 on-ramp ```mermaid stateDiagram-v2 [*] --> 待分诊 待分诊 --> NeedsTriage: 维护者开始评估 state "needs-triage" as NeedsTriage state "needs-info" as NeedsInfo state "ready-for-agent" as ReadyAgent state "ready-for-human" as ReadyHuman NeedsTriage --> NeedsInfo: 信息不足,写 Triage Notes NeedsInfo --> NeedsTriage: 最后反馈时间晚于最后分诊时间 NeedsTriage --> ReadyAgent: 事实与验收足够,可委派 NeedsTriage --> ReadyHuman: 需要判断、权限或手工工作 NeedsTriage --> wontfix: 不实施 ReadyAgent --> 进行中: 被 start-work 认领 ReadyHuman --> 进行中: 人类认领 wontfix --> [*] ``` Triage 的推荐与验证阶段只读。维护者确认 outcome 后,才写唯一 `类别`、唯一 canonical `状态`、`最后分诊时间`、摘要、证据、下一步和 `最后更新人`。需要长叙述时,一个 Issue 只创建并持续 append 一份 `Triage — <标题>` Wiki dossier。 ## 6. `start-work-feishu`:Coding 入队与路由 ### 6.1 队列口径 Skill 会冻结当前已验证用户的 open ID,然后分别完整分页查询: - Spec:`产物类型=PRD/Spec`,且 **Spec 自身** `负责人=当前用户`。 - Ticket:`产物类型=实现 Ticket`,且 **Ticket 自身** `负责人=当前用户`。 子 Ticket 归当前用户不会让父 Spec 自动进入 Spec 队列。父 Spec 只作为 Ticket 的上下文展示。 | 分组 | 条件 | 是否可选择 | |---|---|---| | 可恢复 | `进行中`或`阻塞` | 可以,但阻塞项必须展示原因,不暗示已解除 | | 可开始 | `ready-for-agent`;Ticket 无 blocker 或所有 blocker 都恰好为`已完成` | 可以 | | 待收口 | `待评审` | 不重新开始,转 `close-work-feishu` | | 被阻塞 | `ready-for-agent`,但任一 blocker 不是`已完成` | 不可开始 | | 排除 | 草拟、triage、人类专属和终态记录 | 不进入 coding queue | `wontfix`、`out-of-scope`、`已取代`虽然是终态,但不能自动证明依赖要求已交付,因此不算满足 blocker。 ### 6.2 Start skill 内部逻辑 ```mermaid flowchart TD A["显式调用 start-work-feishu"] --> B["读取项目 tracker 合约与 Trellis workflow"] B --> C["验证 bot + user,冻结当前 user open ID"] C --> D["field-list 验证 29 字段和枚举"] D --> E["完整分页查询 Spec 队列"] E --> F["完整分页查询 Ticket 队列"] F --> G["按 record ID 读取父项与 blocker"] G --> H["计算可恢复 / 可开始 / 待收口 / 被阻塞"] H --> I["用户选择 number 或 record ID"] I --> J["重读选中项、父 Spec、全部子 Tickets、blockers"] J --> K{"负责人、状态、关系或更新时间漂移?"} K -->|"是"| H K -->|"否"| L{"建议路由"} L -->|"小、明确、单上下文"| M["Inline:冻结本会话 IDs 与快照"] L -->|"跨模块、多会话、长验收链"| N["Trellis:扫描现有 mapping"] N --> O{"匹配 task 数量"} O -->|"多个"| Stop["停止,报告冲突"] O -->|"一个"| Resume["恢复已有 task"] O -->|"零个"| Create["task.py create --no-start"] Resume --> Persist["刷新 prd / implement / mapping"] Create --> Persist Persist --> ReadMap["回读完整 meta.feishuTracker"] M --> Confirm["一次确认:工作项 + 路由 + Base patch"] ReadMap --> Confirm Confirm --> Patch["按 record ID 写进行中 / 实现 / 最后更新人"] Patch --> ReadBase["逐条 record-get 回读"] ReadBase --> Route{"路由"} Route -->|"Inline"| Coding["交给当前会话实现"] Route -->|"新建或 planning task"| Start["task.py start"] Start --> VerifyTask["回读 task.json status=in_progress"] ``` ### 6.3 Inline 与 Trellis 的判断 | 路由 | 适用情况 | 本地持久化 | |---|---|---| | Inline | 范围窄、方案已知、一个健康上下文可完成、最低验证可在当前会话完成 | 只在当前对话冻结 record IDs,不创建 mapping 文件 | | Trellis | 跨模块、多阶段、多个稳定决策、durable research、多会话或验收链较长 | 创建或恢复唯一 task,持久化 Spec mapping、规划产物和 Ticket snapshot | 这里的 Inline 是“完全不创建 Trellis task”。它和 Trellis 内部 Codex `dispatch_mode=inline`不是同一概念;后者仍然已经处在 Trellis task 内。 ### 6.4 `1 Spec = 1 Trellis task` mapping ```json { "meta": { "feishuTracker": { "schemaVersion": 1, "specRecordId": "rec_xxx", "ticketRecordIds": ["rec_aaa", "rec_bbb"], "selectedRecordIds": ["rec_aaa"], "specWikiUrl": "https://...", "trackerContractPath": "docs/agents/issue-tracker.md", "boundAt": "ISO-8601", "lastRefreshAt": "ISO-8601" } } } ``` - `specRecordId` 是一对一绑定的稳定键。 - `ticketRecordIds` 是刷新时全部子 Tickets 的排序去重快照,不是 Base 的替代事实源。 - `selectedRecordIds` 是这次用户直接选择的 Spec 或 Ticket。 - `boundAt` 首次绑定后不变;`lastRefreshAt` 随成功刷新更新。 - mapping 不保存 Base token、凭证或固定个人 open ID。 - 只更新 `meta.feishuTracker`,保留 `task.json`其他字段和未知未来键。 ### 6.5 开始 patch ```json { "状态": "进行中", "工作流阶段": "实现", "最后更新人": [{"id": ""}] } ``` - 选择 Spec:只写 Spec。 - 选择 Ticket:写选中的 Ticket 和它的父 Spec。 - 不修改`负责人`、`完成度`、sibling Tickets、证据、blocker 或下一步。 - Trellis 新任务只有在 mapping 持久化成功、Base 写入成功且回读一致后才执行 `task.py start`。 ## 7. Trellis Coding 生命周期 ```mermaid stateDiagram-v2 [*] --> planning: task.py create --no-start planning --> in_progress: task.py start in_progress --> in_progress: implement / check / update-spec / commit in_progress --> Detached: task.py finish state "session detached\n任务未完成" as Detached Detached --> in_progress: 其他 session 可继续 in_progress --> completed: task.py archive completed --> Archived: 移入 archive tree state "archive/task.json\nstatus=completed" as Archived ``` 关键边界: - `task.py finish`只清除当前 session 的任务指针,不能作为 Base 完成信号。 - 复杂任务通常经历 `implement → check → update-spec → 工作 commit → finish-work/archive → journal`。 - `after_archive`最多作为提醒或待对账信号;显式 `close-work-feishu`才有资格在回读后声称 Base 已闭环。 - Ticket 部分收口不要求 archive;父 Spec 最终收口才要求归档 task 且`status=completed`。 ## 8. `close-work-feishu`:部分收口、最终收口与对账 ### 8.1 三个内部 routing | Routing | 进入条件 | 结果 | |---|---|---| | 部分收口 | 有一个或多个 Ticket 已有直接证据,并完成对应人类 review | 只关闭用户选中的 Ticket;Spec 保持`进行中` | | 最终收口 | 全部 Ticket 已完成,Spec 验收有证据,人类完成最终 review;Trellis 路径还必须 archive | 用户再次确认 Spec-only patch 后关闭 Spec | | 对账重放 | 上次写入部分成功、回读失败或记录已在目标终态 | 只补差异;证据完整时返回`already synchronized`,不重复追加 | ### 8.2 Close skill 完整逻辑 ```mermaid flowchart TD A["显式调用 close-work-feishu"] --> B["用 active/archived mapping 或 Inline 会话定位 Spec ID"] B --> C["验证身份、schema、Spec 类型"] C --> D["按父 Spec 完整分页查询全部子 Tickets"] D --> E["比较 live Ticket 集与 Trellis snapshot"] E --> F["汇总验收标准、repo diff、测试、浏览器验收、未运行项、代码引用、checkpoint"] F --> G["逐 Ticket 映射验收证据"] G --> H{"分类"} H --> H1["建议可收口"] H --> H2["待补证 / 待验证"] H --> H3["明确未完成 / 阻塞"] H1 --> I["展示精确 Ticket patch"] H2 --> Wait["保持原状态,说明最小下一步"] H3 --> Wait I --> J["用户声明已完成人类 review
并选择允许关闭的 record IDs"] J --> K["写前逐条重读,检查 owner、状态、父项、依赖、验收、证据、更新时间"] K --> L{"是否漂移?"} L -->|"是"| I L -->|"否"| M["批量最小 patch,最多 200 条且串行"] M --> N["逐条 record-get 回读"] N --> O{"失败 / ignored / mismatch?"} O -->|"是"| Reconcile["输出成功、失败、待对账清单
禁止关闭 Spec"] O -->|"否"| P["重新完整查询所有子 Tickets"] P --> Q{"是否全部严格为已完成?"} Q -->|"否"| Partial["部分收口完成
Spec 保持进行中"] Q -->|"是"| R{"Spec 级最终门禁"} R -->|"未满足"| Hold["Spec 不变,报告缺失门禁"] R -->|"满足"| S["展示独立 Spec-only patch"] S --> T["用户第二次确认"] T --> U["立即重读 Spec;漂移则确认失效"] U --> V["写 Spec + record-get 回读"] V --> Done["最终收口回执"] ``` ### 8.3 Ticket 完成 patch ```json { "状态": "已完成", "工作流阶段": "交付", "完成度": 1, "验证证据": "<保留旧内容并追加一次 Ticket 专属 closure section>", "代码引用": "<保留并去重的真实引用>", "阻塞原因": null, "下一步": null, "最后更新人": [{"id": ""}] } ``` 每个 closure section 至少记录:人类 review、验收到证据的映射、真实验证命令和结果、明确未运行项及原因。不能用 Agent review、测试通过、Trellis `finish`或 archive 单独替代人类 review。 ### 8.4 Spec 最终门禁 ```mermaid flowchart LR A["全部子 Tickets
状态恰好为已完成"] --> Gate{"Spec 最终门禁"} B["Spec 每条验收标准
都有直接证据"] --> Gate C["用户完成 Spec
最终代码/功能 review"] --> Gate D["Trellis 路径:task 位于 archive
且 status=completed"] --> Gate E["不存在 Ticket write/read-back 失败"] --> Gate Gate -->|"全部满足"| Patch["展示 Spec-only patch"] Patch --> Confirm["用户单独确认"] Confirm --> Done["写入 + 回读后才算闭环"] ``` `wontfix`、`out-of-scope`、`已取代`不自动算作“全部 Ticket 已完成”。无 Ticket 的简单 Spec 可以跳过 Ticket 门禁,但仍需 Spec 验收证据、人类最终 review,以及适用的 Trellis archive 门禁。 ## 9. 身份、确认与写后回读协议 ```mermaid sequenceDiagram participant U as 用户 participant S as Feishu skill participant A as lark-cli auth participant B as Feishu Base U->>S: 显式调用 skill S->>A: auth status --json --verify A-->>S: bot verified + user verified + user.openId S->>S: 冻结 CURRENT_USER_OPEN_ID S->>B: --as bot 读取 schema / records B-->>S: 当前快照 S-->>U: 展示稳定 record IDs 与精确 patch U->>S: 明确确认外部写入 S->>B: 写前按 record ID 重读 B-->>S: 无漂移 S->>B: --as bot 最小 patch
最后更新人=user.openId B-->>S: ok:true S->>B: record-get / 完整分页回读 B-->>S: 字段与关系符合预期 S-->>U: 完成回执 ``` 统一规则: 1. 每条 Base read/write 都使用 `--as bot --format json`,失败后不降级为 user。 2. `负责人`是 assignee;`最后更新人`是最近一次通过 CLI 推进记录的真实人类;bot 只是 API caller。 3. 标题是展示文本,record ID 才是更新键。 4. 写前展示目标记录和精确 patch;写后必须回读。 5. `ok:true`不证明 record 存在或字段已生效;`ignored_fields`和 read-back mismatch 都算失败。 6. schema、视图和只读操作没有行级归因目标,不写`最后更新人`。 ## 10. Base 主状态机与 Trellis 映射 ### 10.1 Base 主状态机 ```mermaid stateDiagram-v2 [*] --> 草拟中 草拟中 --> 待确认 待确认 --> ReadyAgent: 可由 Agent 执行 待确认 --> ReadyHuman: 需要人类执行 state "ready-for-agent" as ReadyAgent state "ready-for-human" as ReadyHuman ReadyAgent --> 进行中: start-work 确认并回写 ReadyHuman --> 进行中: 人类认领 进行中 --> 阻塞: 发生 blocker 阻塞 --> 进行中: blocker 解除 进行中 --> 待评审: 进入 review 待评审 --> 进行中: review 要求修改 待评审 --> 已完成: 人类 review + 直接证据 草拟中 --> 已取代 待确认 --> 已取代 ReadyAgent --> 已取代 进行中 --> 已取代 ReadyAgent --> OutOfScope: 范围外 state "out-of-scope" as OutOfScope 已完成 --> [*] 已取代 --> [*] OutOfScope --> [*] ``` ### 10.2 Trellis 事件到 Base 的业务含义 | Trellis / 本地事件 | Base 动作 | 原因 | |---|---|---| | 只读列队、用户未选择 | 不写 | 浏览队列不是认领 | | mapping 保存成功,用户确认“选择并开始” | Spec 或 Ticket/父 Spec → `进行中`、`实现` | 表示进入规划/执行,不猜完成度 | | `planning` / `after_start` | 通常不重复写 | Start skill 已完成开始回写 | | 发生外部 blocker | `阻塞`,并填写`阻塞原因`和`下一步` | 状态与解释必须一致 | | Agent 建议可收口但无人类 review | 保持原状态 | Agent 只能推荐 | | 部分收口 | 仅用户选中的 Tickets → `已完成` | 父 Spec 保持`进行中` | | `task.py finish` / `after_finish` | 不写完成态 | 只代表 session detach | | `archive` / `after_archive` | 不自动写完成态 | 只满足 Spec 生命周期门禁之一 | | 全 Ticket 完成 + Spec 验收 + 人类最终 review + archive | Spec → `已完成`、`交付`、`完成度=1` | 最终聚合收口 | | Base 同步失败 | 保留真实现状并输出待对账 | 代码/Trellis 完成与外部同步分开报告 | ## 11. Base 完整字段字典(29 个) 以下字段来自“经销商政策”项目配置的真实 Base `field-list`回读。`文本`是当前表的主字段。 | # | 字段 | Field ID | 类型 / 当前取值 | 含义与规则 | |---:|---|---|---|---| | 1 | 文本 | `fldzLHLTca` | text,主字段 | 记录的业务标题。标题可用于创建前精确查重,但更新必须使用 record ID。 | | 2 | 产物类型 | `fldlwQ46Ks` | single select:需求/Issue、PRD/Spec、实现 Ticket、Wayfinder Map、决策 Ticket、原型、研究、Handoff、ADR、领域词汇、Bug 诊断、代码评审、架构候选、教学资产 | 说明该记录代表哪类工作流产物。 | | 3 | 来源技能 | `fldUMyVtoS` | multi-select:setup-matt-pocock-skills、setup-workflow-skills-feishu、grill-with-docs、grill-me、triage、diagnosing-bugs、wayfinder、to-spec、to-tickets、implement、tdd、code-review、improve-codebase-architecture、domain-modeling、prototype、research、handoff、teach | 记录创建或推进产物的逻辑流程。业务记录通常写逻辑 skill 名;当前表也保留 setup 的 Feishu 适配器选项。 | | 4 | 工作流阶段 | `fldjAkNPqD` | single select:探索/澄清、决策、规格、拆票/规划、实现、验证/评审、交付、维护/学习、分诊 | 表示产物处于端到端流程哪一阶段;与`状态`是不同维度。 | | 5 | 状态 | `fldHjFrlwJ` | single select:草拟中、待确认、待分诊、needs-triage、needs-info、ready-for-agent、ready-for-human、进行中、阻塞、待评审、已完成、wontfix、out-of-scope、已取代 | 统一业务状态机。英文值保留 Matt triage canonical role。 | | 6 | 类别 | `fldQWuceDX` | single select:bug、enhancement | Triage 分类;每个已分诊项必须恰好一个。 | | 7 | 决策票类型 | `fldFpkeDkj` | single select:research、prototype、grilling、task | Wayfinder 子决策票的解决方式;非决策票通常留空。 | | 8 | 协作模式 | `fldVwgWNRU` | single select:HITL、AFK | HITL 需要人类实时参与;AFK 表示 Agent 可以独立推进到下一个门禁。 | | 9 | 优先级 | `fldJv2ts2l` | single select:P0、P1、P2、P3 | 业务/执行优先级;不替代 blocker frontier。 | | 10 | 负责人 | `fldGlPa1B1` | user,单值 | 当前直接执行责任人。必须是真实飞书用户;Start/Close skills 不覆盖它。 | | 11 | 最后更新人 | `fld6nvDu18` | user,单值 | 最近一次通过 lark-cli 创建或更新记录的真实人类用户;不是 bot,也不是飞书系统更新时间。 | | 12 | 完成度 | `fldqNJjr0g` | number/progress,0–1 | 业务进度。Base 显示为百分比;最终完成写 JSON number `1`。 | | 13 | 产物文档 | `fldorpkrbf` | text/plain | canonical Wiki 或长文档链接。Spec/Triage 使用;V1 Ticket 留空并沿父 Spec 取文档。 | | 14 | 验收标准 | `fldwbSY2pq` | text | 可验证的完成条件。实现 Ticket 必填;Spec 保存聚合验收边界。 | | 15 | 验证证据 | `fldIQ0NQPO` | text | 真实命令、测试结果、浏览器验收、review、read-back 事实和未运行原因。没有具体证据不能完成。 | | 16 | 结论/摘要 | `fldmdDUJHh` | text | Problem/Solution 摘要、决策结论、诊断根因、研究摘要或交付结果;长分析放 Wiki。 | | 17 | 阻塞原因 | `fldUsFDsTS` | text | 仅`阻塞`或`needs-info`等无法继续的状态使用;说明为什么不能推进。 | | 18 | 下一步 | `fldMY6XATm` | text | 解除阻塞或进入下一阶段的最小动作;完成态清空。 | | 19 | 代码引用 | `fldNylIQ5y` | text | 分支、commit、PR、文件:行号、Trellis checkpoint、review fixed point 或`.out-of-scope/`路径。只写真实存在的引用。 | | 20 | 截止时间 | `fldZP6Cm30` | datetime,`yyyy-MM-dd HH:mm` | 人工承诺的截止时间,不由 Agent 自动推算。 | | 21 | 创建时间 | `fldnPF52hP` | created_at,只读 | Base 自动记录的创建时间;Triage 关注队列按它从旧到新排序。 | | 22 | 更新时间 | `fldd3rTk0I` | updated_at,只读 | Base 自动记录的行更新时间;用于竞态检测,不等于`最后更新人`。 | | 23 | 所属父项 | `flduS0iiPy` | self-link,双向 | Spec 指向来源 Issue;Ticket 指向父 Spec;决策 Ticket 指向 Wayfinder Map。写真实 record ID。 | | 24 | 前置依赖 | `fldYkwZVu1` | self-link,双向 | 指向阻塞当前项的记录。只有所有 blocker 都恰好为`已完成`时 Ticket 才进入 frontier。 | | 25 | 来源链接 | `fldp4F1S6m` | text/url | 原始 Issue、PR、表单或外部系统 URL。 | | 26 | 外部编号 | `fldpv8O5OW` | text | 外部系统编号。它不是 Base record ID,不能用作更新键或 link CellValue。 | | 27 | 报告人 | `fld37AxlZf` | text | 报告人的显示名或外部标识;兼容报告人不是飞书用户。 | | 28 | 最后反馈时间 | `fldTwNj5Uz` | datetime,`yyyy-MM-dd HH:mm` | 报告人或外部来源最近一次新增反馈时间。只更新时间,不自动改状态。 | | 29 | 最后分诊时间 | `fldNy9eAgM` | datetime,`yyyy-MM-dd HH:mm` | 维护者最近一次确认并写回分诊结果的时间。`最后反馈时间 > 最后分诊时间`时,needs-info 重新进入关注队列。 | ### 11.1 Link 字段边界 `所属父项`和`前置依赖`的正向字段是自动化 source of truth。`lark-cli 1.0.76`可能返回同表双向关系的 reverse field ID,但 reverse 字段不一定独立出现在`field-list`,因此当前实现不依赖反向字段寻址。 ## 12. 真实 POC:经销商政策项目 ### 12.1 POC 对象 | 产物 | Record ID / 路径 | 最终状态 | |---|---|---| | Spec:历史报告多条件检索 | `recvquNpDDOafu` | `已完成 / 交付 / 1` | | Ticket A:按事业部和分析类型筛选历史报告 | `recvquNtBMcQ2V` | `已完成 / 交付 / 1` | | Ticket B:按关键词搜索并完善筛选反馈 | `recvquNxjmWwth` | `已完成 / 交付 / 1` | | Trellis task | `.trellis/tasks/archive/2026-07/07-27-feishu-recvqunpddoafu` | archive 中,`task.json.status=completed` | | 工作 commit | `e7270cf` | `feat: 支持历史报告多条件检索` | | 规范 commit | `1f00f51` | `docs: 沉淀报告检索状态同步契约` | | 归档 commit | `30b9c8f` | `chore(task): archive 07-27-feishu-recvqunpddoafu` | | journal commit | `3e91199` | `chore: record journal` | ### 12.2 真实执行时序 ```mermaid sequenceDiagram participant U as 于选辉 participant S as start-work-feishu participant B as Feishu Base participant T as Trellis participant C as Coding / Check participant X as close-work-feishu U->>S: 选择历史报告多条件检索工作 S->>B: 查询 Spec、Tickets、父项和 blocker S->>T: 创建并保存 1 Spec = 1 task mapping S->>B: 写 Spec/Ticket 进行中并回读 S->>T: task.py start T->>C: 实现 Ticket A C-->>U: 代码、测试和构建证据 U->>X: 已完成 Ticket A 人类 review X->>B: 关闭 recvquNtBMcQ2V 并回读 B-->>X: Ticket A 已完成,Ticket B blocker 解锁 T->>C: 实现 Ticket B C-->>U: API、前端、构建、真实浏览器与竞态证据 U->>X: 已完成 Ticket B 人类 review X->>B: 关闭 recvquNxjmWwth 并回读 B-->>X: 两张 Ticket 全部已完成 C->>T: update-spec + commits U->>T: 授权 finish-work T->>T: archive task + journal U->>X: 已完成 Spec 最终 review X->>B: 重新查询全部 Tickets 与 Spec X-->>U: 展示 Spec-only 精确 patch U->>X: 第二次确认最终 patch X->>B: 更新 Spec 已完成并 record-get B-->>X: 已完成 / 交付 / 1,负责人和验收保持不变 ``` ### 12.3 实现与验证结果 本次 Spec 实现了:事业部、分析类型、标题/文件名关键词组合检索;条件使用 AND 语义;关键词去除首尾空白并对拉丁字符大小写不敏感;无筛选兼容旧行为;筛选后详情与列表保持一致;区分空仓库和筛选无结果;支持一键清空;旧请求不能覆盖新筛选结果。 真实验证证据: - `pytest -q tests/test_api.py -k reports`:`9 passed, 11 deselected`。 - 可执行后端回归:`37 passed, 1 deselected`。 - 前端 `npm test`:`4 passed`。 - 前端 `npm run build`:通过,仅保留既有 large-chunk advisory。 - Trellis task validation:通过。 - `git diff --check`:通过。 - 真实浏览器验收:文件名关键词、大小写不敏感三条件组合、当前条件/结果数反馈、筛选无结果、详情清空与恢复、一键清空全部通过。 - 受控请求竞态:较早的延迟响应未覆盖较新的筛选结果。 - 浏览器 console 仅出现与本功能无关的 `favicon.ico` 404。 - 完整 pytest 未运行成功的原因被明确保留:既有缺失模块/import collection errors,以及本次 diff 外的`policy_data` 404;没有把未运行项伪报为通过。 ### 12.4 POC 证明了什么 ```mermaid flowchart LR A["当前用户负责人过滤"] --> Proof["真实 POC 通过"] B["Ticket blocker frontier"] --> Proof C["稳定 record ID 与 Trellis mapping"] --> Proof D["1 Spec = 1 task"] --> Proof E["Ticket 逐张人类 review"] --> Proof F["部分收口不关闭 Spec"] --> Proof G["archive 不是自动完成授权"] --> Proof H["Spec 第二次确认"] --> Proof I["写前重读 + 写后回读"] --> Proof J["最后更新人归因"] --> Proof K["未运行项显式记录"] --> Proof ``` 本次 demo POC 仅对“planning artifacts 必须经人类 review 后才能 `task.py start`”放宽了一次;Ticket review、Spec 最终 review、外部写入确认和回读门禁均未放宽。最终 Spec 的 Base 回读时间为 `2026-07-27 10:04:12`,`负责人`和`最后更新人`均为于选辉,验收标准和关联字段保持不变。未执行 push。 ## 13. 失败处理与幂等对账 ```mermaid flowchart TD A["任一步失败"] --> B{"失败位置"} B -->|"身份 / schema / 查询"| C["不做任何本地或外部写入"] B -->|"选择后发生漂移"| D["旧确认失效,重新展示队列或 patch"] B -->|"task / artifact / mapping"| E["不写 Base,保留明确失败原因"] B -->|"Base 开始写失败"| F["task 保持 planning
报告 mapping 已保存但未同步"] B -->|"Base 成功,Trellis start 失败"| G["Base 已认领
Trellis 待启动,不自动回滚"] B -->|"Ticket batch 部分失败"| H["保留逐条成功/失败
禁止关闭 Spec"] B -->|"write ok 但回读 mismatch"| I["标记 mismatched,不宣称完成"] B -->|"记录已完整同步"| J["already synchronized
不重复追加证据"] H --> K["下次对账只补仍有差异的记录"] I --> K ``` 核心原则:外部系统的部分成功不能被一个总体`ok:true`遮蔽;也不能因为 Base 同步失败就篡改已经完成的代码/Trellis事实。回执必须把“代码完成”“Trellis lifecycle 完成”“Base 同步完成”分开报告。 ## 14. 当前边界与后续演进 V1 当前边界: - 两个 Coding 闭环 skills 已实现并通过真实 POC。 - 直接编排项目内`.trellis/scripts/task.py`和`lark-cli`,没有新增 helper CLI。 - 没有修改 Trellis workflow、hooks 或 Base schema。 - 不自动 commit、push 或创建 PR;Git 写操作继续遵守用户授权边界。 - 不把 coding sub-agent 设为 Base 状态决策者;外部选择、review、确认和回读都留在主会话。 建议的后续顺序: 1. 在第二个真实项目重放一次 Inline 路径,验证跨会话时按 record ID 重选的体验。 2. 演练一次故意的 Base batch 部分失败,验证对账 routing 的逐条补偿。 3. 演练一次 mapping 冲突和记录竞态,验证确认失效路径。 4. 稳定后再考虑抽取共享分页/frontier/helper;在两个 skills 尚未形成稳定重复前不提前封装。 5. 如果接入 lifecycle hook,`after_archive`只提醒或生成不含凭证的 pending 标记,不直接写 Base 完成态。 ## 15. 一句话心智模型 ```mermaid flowchart LR Issue["Issue
要解决什么"] --> Spec["Spec
为什么做、验收边界"] Spec --> Tickets["Tickets
可独立交付的垂直切片与依赖"] Tickets --> Start["Start
选择、路由、认领"] Start --> Coding["Coding
Inline 或 1 Spec = 1 Trellis task"] Coding --> Evidence["Evidence
代码、测试、浏览器、review"] Evidence --> Close["Close
Ticket 部分收口 → Spec 最终收口"] Close --> Done["Done
写后回读的 Base 闭环"] ``` **Base 回答“现在是什么状态、由谁负责、依赖谁”;Wiki 回答“为什么做、具体要求是什么”;Trellis 回答“复杂工作如何规划、执行和归档”;repo 回答“到底实现了什么、验证过什么”;六个 Feishu skills 负责把这些事实源连接成一条可确认、可回读、可重放的工作流。** ## 16. 相关资料 - [Matt 工作流 × 飞书 CLI 全流程总结](<./Matt 工作流 × 飞书 CLI 全流程总结.md>) - [Trellis × 飞书实现闭环初步方案](<./Trellis × 飞书实现闭环初步方案.md>) - 经销商政策项目 tracker 合约:`/Users/yuxuanhui/Documents/ai-workflow/project/经销商政策/docs/agents/issue-tracker.md` - 真实 POC 归档任务:`/Users/yuxuanhui/Documents/ai-workflow/project/经销商政策/.trellis/tasks/archive/2026-07/07-27-feishu-recvqunpddoafu` - Trellis 官方文档:[架构](https://docs.trytrellis.app/zh/advanced/architecture)、[自定义 Workflow](https://docs.trytrellis.app/zh/advanced/custom-workflow)、[自定义 Skills](https://docs.trytrellis.app/zh/advanced/custom-skills)、[自定义 Agents](https://docs.trytrellis.app/zh/advanced/custom-agents)、[配置](https://docs.trytrellis.app/zh/advanced/configuration)