Strapi 仓库的 AI Agent Issue 追踪机制:Obsidian 笔记优先 + Linear 晋升的双轨制工作流
【免费下载链接】strapi🚀 Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.项目地址: https://gitcode.com/GitHub_Trending/st/strapi
Strapi 仓库在docs/agents/目录下维护了一套面向 AI 技能(skills)的 Issue 追踪规范,其核心思想是:问题单与 PRD 先以 Markdown 笔记的形式个人化地沉淀在 Obsidian 中,只有当用户明确要求时才晋升(promote)到公司级 Linear 工作区。读完本篇,你可以掌握这套「Markdown + frontmatter + MCP 工具」的轻量 Issue 系统的设计原则、笔记结构约定、逐条操作的 MCP 工具映射,以及如何把它与 Strapi monorepo 的 Agent 工具链(.ai/skills/、yarn ai:sync)衔接起来,为在自己的仓库中复刻类似的 AI 可操作追踪流程提供完整参照。
双轨制追踪:为什么是 Obsidian 优先
整套机制的定义见 docs/agents/issue-tracker.md,其第一句话就点明了核心策略:
Issues and PRDs for this repo are trackedpersonally in Obsidian first, and only promoted to the companyLinearworkspace when explicitly asked. This keeps solo/exploratory work out of the shared tracker until it's ready.
这是一条典型的「个人探索与共享追踪隔离」策略:
- 个人层(Obsidian):所有问题单、PRD 先落在本地 Obsidian 笔记库,粒度随意、状态随意,适合独奏式、探索性工作;
- 公司层(Linear):只有当用户显式要求晋升时,才把成熟的 issue 写入共享的 Linear 工作区。
这样做的好处从源码角度看很直接:未收敛的想法、半成品草稿不会污染团队共享的追踪器;而一旦晋升,两侧的关联通过 frontmatter 中的linear:字段持久化,保证双向可追溯。
Issue 的物理位置:一个 issue 一个 Markdown 文件
Issue 并不存放在任何数据库或远端服务里,而是以文件形式存在于 Obsidian 库的固定目录:
- 路径:Obsidian 库下的
notes/work/strapi/issues/,每个 issue 对应一个独立的<slug>.md文件(例如cm-403-redirect-loop.md); - 全部通过Obsidian MCP 工具进行创建、读取、更新,涉及的工具共六个:
write_note、read_note、search_notes、patch_note、update_frontmatter、list_directory。
这种「文件系统即数据库」的选择意味着 issue 的完整历史天然可被 git/版本管理工具审计,AI agent 也能用最朴素的文件读写完成全部操作,无需对接 issue 系统的 REST API。
笔记结构:frontmatter 承载追踪器状态
每个 issue 笔记由 YAML frontmatter + 自由正文两部分组成。frontmatter 是追踪器的状态机载体,字段约定如下:
--- title: <short description> type: issue status: <open | in-progress | closed> labels: [needs-triage] # one or more of the triage roles — see docs/agents/triage-labels.md created: YYYY-MM-DD linear: <Linear issue URL, once promoted> ---各字段的语义:
| 字段 | 取值/约束 | 作用 |
|---|---|---|
title | 简短描述 | issue 的显示标题 |
type | issue | 区分笔记类型(与 PRD 等其他类型笔记区分) |
status | open/in-progress/closed | 三态状态机,追踪器的主状态 |
labels | 一个或多个 triage 角色字符串 | 分诊标签数组,取值见下文标签映射表 |
created | YYYY-MM-DD | 创建日期 |
linear | Linear issue URL | 晋升到 Linear 后回填,保持两侧链接 |
正文(Body)部分为自由格式,文档明确要求包含:问题陈述(problem statement)、复现步骤(repro)、上下文(context)、验收标准(acceptance criteria)与备注(notes)。
注意labels字段的注释直接指向 docs/agents/triage-labels.md,说明标签词汇表是独立维护的——这是本文档体系中职责分离的体现:issue-tracker.md定义「怎么存、怎么操作」,triage-labels.md定义「标签叫什么」。
操作约定:每个动作对应一个 MCP 工具
docs/agents/issue-tracker.md 用一节 Conventions 把 issue 生命周期中的每个动作一一映射到具体的 Obsidian MCP 调用,完整继承如下:
| 动作 | 工具调用方式 |
|---|---|
| 创建 issue | write_note在notes/work/strapi/issues/下新建带上述 frontmatter 的文件 |
| 读取 issue | read_note(按标题/permalink),或search_notes检索 |
| 列表/查询 issue | list_directory作用于notes/work/strapi/issues/,或search_notes/search_by_metadata按status与labels过滤 |
| 评论/更新 | patch_note向正文追加内容;update_frontmatter修改status/labels |
| 打/摘 triage 标签 | 通过update_frontmatter编辑labels数组(标签映射见 docs/agents/triage-labels.md) |
| 关闭 issue | 通过update_frontmatter设置status: closed |
这个约定值得借鉴的设计点是:正文修改与状态修改走两条不同的工具路径(patch_notevsupdate_frontmatter)。评论、补充复现信息属于正文演化,而状态流转属于元数据变更,二者分离后,search_by_metadata这类基于 frontmatter 的查询永远面对结构稳定的数据,不会被正文噪音干扰。
Triage 标签体系:五个规范角色与本地词汇表的映射
配套的 docs/agents/triage-labels.md 把上游技能集(mattpocock/skills)使用的五个规范分诊角色映射到本仓库追踪器实际使用的标签字符串:
| mattpocock/skills 中的角色 | 本追踪器中的标签 | 含义 |
|---|---|---|
needs-triage | needs-triage | 需要维护者评估该 issue |
needs-info | needs-info | 等待报告者补充更多信息 |
ready-for-agent | ready-for-agent | 规格完整,可交给 AFK(无人值守)agent 处理 |
ready-for-human | ready-for-human | 需要人类来实现 |
wontfix | wontfix | 不做处理 |
标签存储位置与issue-tracker.md一致:notes/work/strapi/issues/下每个 issue 笔记的labels:frontmatter 数组。该文档还给出两条落地规则:
- 当技能提及某个角色(例如“打上 AFK-ready 的分诊标签”)时,必须使用映射表右列的实际字符串;
- 右列是可编辑的——如果团队实际使用的标签词汇不同,应修改右列以匹配真实词汇,而左列(规范角色)保持不变。
这种「规范角色 → 本地字符串」的间接层让分诊逻辑与具体标签命名解耦,是典型的适配器模式在文档层面的应用。
其中ready-for-agent标签尤其值得注意:它标志着 issue 已经从「待办」升级为「可被 AI 全自动执行的任务」——规格完整到可以交给无人值守 agent,这恰好呼应了 Strapi 仓库整体「AI 优先」的 Agent 工具链定位。
晋升 Linear:显式触发的单向同步协议
文档对「何时允许触碰公司级追踪器」设置了硬门槛:仅当用户明确要求晋升某个 issue 时。晋升步骤为三步:
- 使用Linear MCP的
save_issue工具创建 Linear issue,标题与正文直接取自 Obsidian 笔记; - 将返回的 Linear issue URL回填到 Obsidian 笔记的
linear:frontmatter 字段,使两侧记录保持链接; - 除非用户要求把追踪完全迁到 Linear,否则Obsidian 笔记继续作为工作副本(working copy)存在。
这个协议有三个工程要点:
- 同步方向是单向回填(Linear URL → Obsidian frontmatter),而非双向实时同步,避免了状态冲突;
- 触发条件是显式的,防止 agent 在自主执行过程中把半成品草稿泄入共享追踪器;
- 工作副本不迁移,Obsidian 侧继续承载日常演进,Linear 侧作为公司层面的正式记录。
技能指令到具体操作的翻译规则
issue-tracker.md的最后两节规定了当仓库内其他技能(skills)发出模糊指令时,agent 应如何翻译成具体的工具调用:
当技能说 "publish to the issue tracker": 通过 Obsidian MCP 的write_note工具在notes/work/strapi/issues/下写一个新的 Markdown 笔记。
当技能说 "fetch the relevant ticket": 用read_note(或search_notes)读取notes/work/strapi/issues/下对应的笔记;如果该笔记带有linear:URL 且用户想要公司层面的正式记录,则改用 Linear MCP 的get_issue工具去取。
这两条规则实际上定义了一个「指令 → 工具」的翻译表,把自然语言动词(publish/fetch)绑定到确定性的 MCP 调用上,消除了 agent 在自主执行时的行为歧义。
与 Strapi 仓库 Agent 工具链的衔接
放在 Strapi 仓库的整体上下文里看,docs/agents/目录是三份面向 AI 技能的约定文档:
- docs/agents/issue-tracker.md —— 本文主线,定义 issue 存储、结构与操作流程;
- docs/agents/triage-labels.md —— 定义分诊标签映射表;
- docs/agents/domain.md —— 定义技能在探索代码库前如何消费领域文档(
CONTEXT-MAP.md、各 package 的CONTEXT.md、docs/adr/),并要求在输出中使用领域词汇表中定义的术语。
从 docs/agents/domain.md 可以看到,issue 标题、重构提案、假设与测试命名都必须使用对应CONTEXT.md中定义的术语——也就是说,晋升进 Obsidian/Linear 的 issue 标题在措辞上受领域词汇表约束,保证追踪器中的语言与代码库语言一致。
在工具链层面,仓库根目录的 AGENTS.md 声明了技能(skills)的存放与分发机制:.ai/skills/是提交进仓库的技能唯一权威来源,每个包含SKILL.md的子目录即为一个技能;.agents/skills/、.claude/skills/、.cursor/skills/三个目录是yarn ai:sync维护的软链接目标,由 scripts/ai-tooling/ 中的 CLI(sync/unlink/status三个子命令)负责幂等地创建、修剪与检查链接。issue-tracker.md中提到的各种技能(如 "publish to the issue tracker" 的调用方)正是运行在这套.ai/skills/体系之上的消费者:技能定义「做什么」,docs/agents/下的三份文档定义「按什么约定做」。
小结:一套可复刻的轻量 Issue 系统范式
回到 docs/agents/issue-tracker.md 本身,它提供的远不止 Strapi 一个仓库的笔记位置说明,而是一套完整的、AI agent 可操作的轻量 Issue 系统设计:
- 文件即数据库:一个 issue 一个
<slug>.md,状态全部收敛到 frontmatter,正文自由演化; - 状态与标签正交:
status三态管生命周期,labels数组管分诊,二者通过独立的update_frontmatter操作维护; - 词汇表外置:标签映射独立成文档且允许改写右列,规范角色与本地词汇解耦;
- 共享追踪器设晋升门槛:显式触发 + 单向 URL 回填 + 工作副本不迁移,防止草稿污染共享状态;
- 模糊指令有确定性翻译:publish/fetch 等自然语言动词被显式绑定到具体 MCP 工具调用。
如果你的仓库也在为 AI agent 构建任务追踪能力,这套「Markdown frontmatter 状态机 + MCP 工具 + 双轨晋升」的组合可以直接参照:把notes/work/strapi/issues/换成你自己的目录,把五个 triage 标签替换成团队真实词汇,其余结构原样保留即可运行。
【免费下载链接】strapi🚀 Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first.项目地址: https://gitcode.com/GitHub_Trending/st/strapi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考