关于 frame-a-proposal
frame-a-proposal 是一项将问题整理为可评审 RFC 提案的技能。它引导作者检索既有方案、界定问题、撰写设计与替代方案并进行校验;在撰写解决方案前设有确认环节。
主要功能
- 01检索相关提案和决策
- 02界定受益者、可观察变化与待决事项
- 03撰写动机、设计、缺点和替代方案
- 04记录未决问题、证据与负责人
- 05校验链接、章节和提案状态
适用场景
- 01从已识别的问题着手编写 RFC
- 02新建提案前检查既有方案
- 03梳理真实替代方案与设计权衡
- 04准备可评审草案但不推进其状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ inkeep/ open-knowledge-skills --skill frame-a-proposal安装后在 Claude Code 中输入/frame-a-proposal确认已加载。
详细介绍
制定提案——将问题转化为可审阅的 RFC
/open-knowledge 平台技能仍然管辖此处的所有 markdown 操作(通过 exec/search 读取,通过 write/edit 写入,链接使用普通相对 markdown,绝不对适用范围内的文件使用原生 Read/Edit/Grep/cat)。此技能在此基础上叠加提案撰写技巧:它决定一份好的提案应包含什么,以及如何按顺序逐步完成各个部分。
proposals/ 中的提案是一种设计论证,不是决策,也不是计划。它旨在促使大家在多个选项中做出选择,并为评审者提供充分依据以便提出异议。文件名为 0001-feature-name.md——由零填充的 4 位序号和 kebab 风格标题组成。状态流转为 draft → fcp → accepted/rejected(fcp = 最终评论期)。提案获批后,会晋升为 decisions/ 中的一条记录——这是一个独立的人工操作,也需要使用独立的技能。
此技能旨在防止以下失败情形:Agent 在大家尚未就问题达成一致前就跳到 ## Design,在 ## Alternatives 中塞满稻草人选项,并将 ## Drawbacks 留空。下面的每个步骤都有一道关卡来避免这种情况。
必须遵循的执行顺序
硬性关卡——切勿跳步。 如果你正准备起草 ## Design,但尚未通过步骤 1 的问题界定关卡,请停止——你跳过了一道关卡。提案的核心意义,就是先就问题达成一致,再撰写解决方案。
- 梳理先例——了解
proposals/和decisions/中关于此子系统已有的内容。 - 界定问题——停止关卡。 明确受益者、可观察到的变化和必须做出的决策。在撰写任何解决方案内容前,先征求确认。
- 分配序号并基于
proposal模板创建文件。 - 动机——问题、证据、当前受影响的人、什么都不做的代价,以及明确的非目标。
- 设计——将提案描述到足以让读者提出异议的程度。
- 备选方案——至少提出两个切实可行的方案,并分别说明不选它们的原因。
- 缺点——坦诚说明代价。
- 未决问题——一份持续更新的待办清单,每项都列明解决责任人和解决所需的证据。
- 添加链接并验证。
- 回顾总结,并说明推进到
fcp需要满足什么条件。
如果宿主的任务系统支持,请为步骤 0–9 创建工作流任务——这样在会话中途就能发现被跳过的关卡。
步骤 0:梳理先例
在界定问题前,先查明知识库对这个子系统已经做过哪些决策或提案。悄悄重新讨论已获批决策的提案注定会被否决;引用该决策并解释为何应重新审视它的提案则是合理的。
search({ query: "<subsystem or problem keywords>" })——语义搜索,可匹配同义词。exec("ls -A proposals/")和exec("ls -A decisions/")——查看序号分配情况和已落地的内容。exec("grep -rln <keyword> proposals/ decisions/")——找出提及同一子系统的文件。- 对最相关的 1–3 条结果,使用
exec("cat proposals/0003-x.md")阅读完整文档及其反向链接。
对发现的内容进行分类,并在草稿中体现:
| 发现 | 应采取的行动 |
|---|---|
| 某项已获批的决策涵盖此领域 | 新提案 MUST 引用该决策(使用指向 decisions/ 的 markdown 链接),并在“动机”中说明发生了什么变化,使该决策需要重新审视。如果没有任何变化,请告诉用户这可能根本不需要提案。 |
| 某项草稿/fcp 提案存在重叠 | 提议扩展或取代该提案,而不是另开一份近似重复的提案。两份重叠提案会分散评审意见。 |
| 没有相关内容 | 直接继续。 |
步骤 1:界定问题 — STOP 门槛
这是区分 RFC 与一堆解决方案文本的关键门槛。在用户确认问题界定之前,不要起草 ## Design,也不要创建文件。
生成并原样返回以下内容,然后停止并等待:
## Framing (confirm before I draft)
**Beneficiary:** who is worse off today and will be better off if this ships. A named role or user, not "the system" or "us".
**Observable change:** the concrete, checkable difference they will see. "X drops from N to M", "Y becomes possible", "Z stops happening". Not "improve", not "streamline".
**Forced decision:** the one question this proposal makes reviewers answer. If accepting it doesn't commit anyone to anything, it is a report, not a proposal.
**Rough shape:** one sentence on the direction — enough to know we're framing the right problem, not the design itself.
纪律:
- 如果你无法说出一个不同于“团队”的受益者,就说明问题尚未界定清楚。起草前应提出异议。
- “可观察的变化”必须可证伪,否则就不算完成。如果你说不清如何检查,那你描述的是一项活动,而不是一个结果。
- 触发条件含糊(“我们应该为缓存准备一份提案”)?把范围缩小:为了谁、由什么决策推动、改变他们能观察到的什么。
- 在明确的无头/非交互式运行中,说明问题界定并继续,但要将这三个字段逐字写入 Motivation,以便审阅者可以直接否决问题界定本身。
步骤 2:分配编号并从模板创建
列出顺序,不要猜。 exec("ls -A proposals/"),找出已存在的最大 NNNN,加一,并补零至四位。猜编号迟早会撞车,尤其是同一周起草两个提案时。
文件名:NNNN-kebab-title.md(0007-async-export-pipeline.md)。从模板创建文件——只有这样,## Motivation → ## Design → ## Drawbacks → ## Alternatives → ## Unresolved questions 骨架和 frontmatter 才能正确生成:
write({ document: { path: "proposals/0007-async-export-pipeline.md", template: "proposal" } })
模板会生成以下 frontmatter——填写它,不要手动重新输入:
type: proposal
description: "..." # one line: the forced decision, not the feature name
status: draft # stays draft until a human advances it — see Non-goals
authors: [<user>]
created: YYYY-MM-DD
tags: [proposal]
将 description 设为提案要促成的决策,用一行写明——读者在列表中看到的就是它。
步骤 3:Motivation
通过 edit 编辑已创建的文档,填写 ## Motivation。本节必须能够独立成立:在这里有异议的读者永远不会读你的 Design,而这正是应该的。
- 问题,从受益者的体验出发来陈述,而不是描述缺少了什么解决方案。“超过 100MB 的导出会超时,用户会丢失任务”胜过“我们没有异步导出流水线”。
- 证明问题真实存在的证据:指标、反复出现的报告或具体事件。仅以“这样会更好”为动机的提案,动机不足;应向用户说明这一点,并找到一个信号。
- 当前受到影响的人,明确指出(即步骤 1 中的受益者),并说明影响发生的频率。
- 不采取行动的代价——这是衡量所有替代方案(包括“拒绝此提案”)的现状基线。如果不采取行动确实没有问题,那么这份提案可能就没有存在的必要。
- 明确列出非目标。 列出本提案有意不解决的问题。非目标能让提案便于审阅;范围无边界的提案无法被接受,因为没人能判断自己究竟同意了什么。在 Motivation 内用清晰的
### Non-goals行列出它们。
步骤 4:Design
在 ## Design 中填写实际提案,以读者能够提出异议的粒度来阐述。过于细致(每个函数签名)会让审阅者对未经评估的设计照单全收;过于笼统(“我们会让它更快”)则没有任何可接受的内容。目标是让有能力的读者读完本节后能说:“不,我会用另一种方式做,因为……”
- 描述机制、变更的形态,以及引入或调整的关键接口或契约。
- 明确说明起决定作用的选择。如果设计依赖某一项决策(数据模型、边界或顺序),就把它点明——这通常才是整份提案真正讨论的内容。
- 说明如何实现步骤 1 中的可观察变化。将设计与受益者联系起来。
- 以普通的相对 Markdown 格式链接相关规范、指南,以及你所依据或重新审视的决策:
[the export boundary decision](./decisions/0004-export-boundary.md)。 - 不要写实现层面的细枝末节——这种粒度属于规范的工作(一个配套的 skill),而非提案。
第 5 步:备选方案
在 ## Alternatives 中填写至少两个真实选项,并说明为什么没有选择。加入那些在另一天你可能会选择的诚恳选项;如果该决策已经生效,也要加入“不采取行动”。
每项的结构如下:
### Alternative: <name>
What it is — one honest paragraph, argued at its best.
Why not — the specific tradeoff that lost, versus the proposed design.
要求:
- 稻草人方案是危险信号——要明确指出。 如果 Alternatives 部分中的每个选项都明显更差,说明作者没有考虑真正的方案,评审者会(正确地)不信任整个提案。如果你无法说清某个备选方案真正吸引人的地方,就说明你还没有充分理解它,不足以将其否决。
- “不采取行动”是一个正式的备选方案。它的“为什么不选”就是第 3 步中不采取行动的代价。
- 合适的数量通常是 2 到 4 个。只有一个,说明你不是在提出方案,而是在宣布决定。
第 6 步:缺点
在 ## Drawbacks 中填写所提设计本身的真实代价——而不是其他方案的代价。
- 没有缺点的提案,说明审视得还不够充分,而不是完美无缺。 每个真正的设计都会有所取舍:复杂性、迁移工作、新的故障模式、维护负担,或一道关闭的门。请明确列出。
- 具体说明由谁、在何时承担代价——付出代价的一方可能与受益方不同,评审者需要了解这种取舍。
- 如果你确实认为某项缺点可以接受,请在这里说明原因,而不是将它隐藏起来。明确说明并接受的缺点,比评审者后来才发现的隐患更有说服力。
第 7 步:未解决的问题
将 ## Unresolved questions 填写为待处理事项清单,而不是免责声明。每项都应让一个真实的待解问题清晰可见,而不是将它埋在自信满满的文字里。
每个问题的结构如下:
- **<question>** — What would resolve it: <the evidence, experiment, or measurement>. Who decides: <role or person>.
- 每个问题都要说明什么证据可以解决它以及由谁决定。两者皆无的问题只是焦虑;请将它具体化,或删除。
- 阻碍提案获批的问题必须明确列在这里——它们将成为
fcp阶段的议程。 - 处于
draft状态时,本部分不为空是合理的。为了显得已经完成而隐藏不确定性,才是问题所在;诚实列出未解决的问题,才能让提案便于安全评审。
第 8 步:添加链接并验证
在告诉用户提案已准备就绪之前,先完成以下工作:
- 所有引用的决策、规范或指南都必须使用普通的 Markdown 相对链接(
[text](./decisions/0004-x.md)),绝不能用反引号包裹,也不能使用 HTML 锚点。 - 从 1 到 2 篇密切相关的文档中添加反向链接,让读者能够找到该提案——例如它重新审视的已采纳决策、相关规范或适用指南。使用
links({ kind: "backlinks", ... })查看现有的反向链接。 audit({ path: "proposals/0007-async-export-pipeline.md" })返回无问题(即不存在 lint 违规或内部链接失效)——修复所有发现的问题。- Frontmatter 完整:
type: proposal、一行描述description、status: draft、authors、created、tags: [proposal]。 - 五个 H2 部分均须存在且内容非空:
## Motivation、## Design、## Drawbacks、## Alternatives、## Unresolved questions。Drawbacks 为空或 Alternatives 中包含稻草人方案,即使相应部分在形式上存在,也无法通过此项检查。 status仍为draft。不要推进状态——请参阅“非目标”。
第 9 步:总结并说明进入 fcp 的路径
在对话中与用户一起完成收尾:
## Recap
- Framed: <beneficiary> gets <observable change>; forces the decision <…>.
- Proposal: proposals/NNNN-title.md (status: draft)
- Alternatives weighed: <n>, chosen over them because <one line>.
- Honest drawbacks: <the main one>.
- Open questions still live: <count> — the fcp agenda.
**To advance to `fcp`:** a human moves status draft → fcp and opens the final comment
period. Acceptance (→ accepted, then a record in decisions/) is a human decision, not
mine. If it's rejected, mark status: rejected and keep the doc — the reasoning is the value.
明确说明,推进状态和采纳提案是由人来决定的。你的工作到此为止:产出一份论证诚恳、框架清晰的 draft。
非目标
- 不要记录决策。 提案获批后,会成为
decisions/中的一条记录——这是同级的/record-a-decision技能,应在人类接受提案 之后 运行。此技能止步于draft。 - 不要编写实现规范。 关注设计层面,而非构建细节。规范由
/write-a-spec编写。 - 不要擅自将提案标记为
accepted(或fcp)。 推进状态是人类的行为。保留status: draft;说明推进状态需要什么。 - 不要跳过步骤 1 的问题界定关卡。 在问题达成一致前撰写解决方案,正是此技能旨在防止的问题。如果你已经撰写了 Design,却没有确认受益者和必须做出的决策,就说明你跳过了关卡——请退回上一步。
- 不要用稻草人充斥 Alternatives,也不要让 Drawbacks 留空。 这两点都表明提案并未经过真正审视。评审者也会如此看待。