Files
obsidian-vault/docs/Matt 工作流 × 飞书 Skills × Trellis Coding 闭环总结.md
T
yuxuanhui dcd6d44960 feat: add Feishu user authorization flow documentation and update frontend guidelines
- Added new documentation for the Feishu user authorization flow, detailing the backend protocol, authorization initiation, callback handling, and token management.
- Updated frontend index to link to the new authorization flow documentation for better accessibility and guidance on UI design consistency.
2026-08-31 09:18:03 +08:00

40 KiB
Raw Blame History

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 或操作归因覆盖。
flowchart LR
    U["用户<br/>选择、review、确认外部写入"] --> Skills["六个 Feishu workflow skills<br/>流程编排与门禁"]
    Skills --> Auth["lark-cli 身份门禁<br/>bot + user verified"]

    Auth -->|"--as bot"| Base["Feishu Base<br/>状态、关系、队列、责任"]
    Auth -->|"--as bot"| Wiki["Feishu Wiki / Docs<br/>Spec 与长叙述"]
    Skills --> Trellis["Trellis<br/>复杂任务生命周期与本地上下文"]
    Skills --> Repo["Repository<br/>代码、测试、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/<skill-name>/。它们都配置了 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 之间的调用关系

flowchart TD
    Setup["setup-workflow-skills-feishu<br/>建立项目级 tracker 合约"] --> Intake{"工作从哪里进入?"}

    Intake -->|"已澄清想法 / 对话"| Spec["to-spec-feishu<br/>Wiki Spec + Base Spec"]
    Intake -->|"Issue / PR / 表单"| Triage["triage-feishu<br/>分类、验证、澄清、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<br/>Base Ticket 依赖图"]
    Spec --> Start["start-work-feishu<br/>选择并开始"]
    Tickets --> Start

    Start --> Coding["Inline 或 Trellis Coding"]
    Coding --> Close["close-work-feishu<br/>部分 / 最终 / 对账"]
    Close -->|"仍有未完成 Ticket"| Coding
    Close -->|"全部门禁满足"| Done["Base Spec 已完成<br/>工作流闭环"]

3. 端到端工作流总图

下面这张图把需求侧、规划侧、实现侧和收口侧放在同一条链路中。

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<br/>等待反馈"]
    D2 --> D
    D1 -->|"ready-for-human"| D3["Human Brief / 人类处理"]
    D1 -->|"wontfix"| D4["关闭说明<br/>必要时 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<br/>持久化 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 产物关系总图

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<br/>Spec 来源与验收边界"]
    Task --> Design["design.md<br/>复杂任务技术设计"]
    Task --> Impl["implement.md<br/>Ticket frontier + checkpoint"]
    Task --> JSONL["implement.jsonl / check.jsonl<br/>必要 spec / research 清单"]
    Task --> Archive["archive task.json<br/>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 合约

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:一份长规格 + 一条可查询记录

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:两遍发布依赖图

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<br/>暂不写 blocker"]
    Pass1 --> IDs["冻结返回的 record IDs"]
    IDs --> Pass2["Pass 2:写所属父项 + 前置依赖"]
    Pass2 --> Verify["逐条回读父项、blocker 集、验收和最后更新人"]
    Verify --> Frontier["frontier = 状态可开始<br/>且每个 blocker 恰好为已完成"]

Ticket V1 不创建 Wiki 文档,产物文档留空。实现者通过 所属父项找到父 Spec,再从父 Spec 的 产物文档取得完整规格。

5.4 triage:请求进入工程流程的 on-ramp

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 内部逻辑

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

{
  "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

{
  "状态": "进行中",
  "工作流阶段": "实现",
  "最后更新人": [{"id": "<CURRENT_USER_OPEN_ID>"}]
}
  • 选择 Spec:只写 Spec。
  • 选择 Ticket:写选中的 Ticket 和它的父 Spec。
  • 不修改负责人、完成度、sibling Tickets、证据、blocker 或下一步。
  • Trellis 新任务只有在 mapping 持久化成功、Base 写入成功且回读一致后才执行 task.py start。

7. Trellis Coding 生命周期

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 完整逻辑

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<br/>并选择允许关闭的 record IDs"]
    J --> K["写前逐条重读,检查 owner、状态、父项、依赖、验收、证据、更新时间"]
    K --> L{"是否漂移?"}
    L -->|"是"| I
    L -->|"否"| M["批量最小 patch,最多 200 条且串行"]
    M --> N["逐条 record-get 回读"]
    N --> O{"失败 / ignored / mismatch?"}
    O -->|"是"| Reconcile["输出成功、失败、待对账清单<br/>禁止关闭 Spec"]
    O -->|"否"| P["重新完整查询所有子 Tickets"]
    P --> Q{"是否全部严格为已完成?"}
    Q -->|"否"| Partial["部分收口完成<br/>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

{
  "状态": "已完成",
  "工作流阶段": "交付",
  "完成度": 1,
  "验证证据": "<保留旧内容并追加一次 Ticket 专属 closure section>",
  "代码引用": "<保留并去重的真实引用>",
  "阻塞原因": null,
  "下一步": null,
  "最后更新人": [{"id": "<CURRENT_USER_OPEN_ID>"}]
}

每个 closure section 至少记录:人类 review、验收到证据的映射、真实验证命令和结果、明确未运行项及原因。不能用 Agent review、测试通过、Trellis finish或 archive 单独替代人类 review。

8.4 Spec 最终门禁

flowchart LR
    A["全部子 Tickets<br/>状态恰好为已完成"] --> Gate{"Spec 最终门禁"}
    B["Spec 每条验收标准<br/>都有直接证据"] --> Gate
    C["用户完成 Spec<br/>最终代码/功能 review"] --> Gate
    D["Trellis 路径:task 位于 archive<br/>且 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. 身份、确认与写后回读协议

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<br/>最后更新人=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 主状态机

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 重新进入关注队列。

所属父项和前置依赖的正向字段是自动化 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 真实执行时序

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 证明了什么

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. 失败处理与幂等对账

flowchart TD
    A["任一步失败"] --> B{"失败位置"}
    B -->|"身份 / schema / 查询"| C["不做任何本地或外部写入"]
    B -->|"选择后发生漂移"| D["旧确认失效,重新展示队列或 patch"]
    B -->|"task / artifact / mapping"| E["不写 Base,保留明确失败原因"]
    B -->|"Base 开始写失败"| F["task 保持 planning<br/>报告 mapping 已保存但未同步"]
    B -->|"Base 成功,Trellis start 失败"| G["Base 已认领<br/>Trellis 待启动,不自动回滚"]
    B -->|"Ticket batch 部分失败"| H["保留逐条成功/失败<br/>禁止关闭 Spec"]
    B -->|"write ok 但回读 mismatch"| I["标记 mismatched,不宣称完成"]
    B -->|"记录已完整同步"| J["already synchronized<br/>不重复追加证据"]
    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. 一句话心智模型

flowchart LR
    Issue["Issue<br/>要解决什么"] --> Spec["Spec<br/>为什么做、验收边界"]
    Spec --> Tickets["Tickets<br/>可独立交付的垂直切片与依赖"]
    Tickets --> Start["Start<br/>选择、路由、认领"]
    Start --> Coding["Coding<br/>Inline 或 1 Spec = 1 Trellis task"]
    Coding --> Evidence["Evidence<br/>代码、测试、浏览器、review"]
    Evidence --> Close["Close<br/>Ticket 部分收口 → Spec 最终收口"]
    Close --> Done["Done<br/>写后回读的 Base 闭环"]

Base 回答“现在是什么状态、由谁负责、依赖谁”;Wiki 回答“为什么做、具体要求是什么”;Trellis 回答“复杂工作如何规划、执行和归档”;repo 回答“到底实现了什么、验证过什么”;六个 Feishu skills 负责把这些事实源连接成一条可确认、可回读、可重放的工作流。

16. 相关资料