frame-align:先对齐框架,再动手
这个 skill 在解决什么
用户带着一个软件想法来。它可能只是一句话,可能是一副缺内容的骨架,可能是一份有错的草案,可能是一个卡住的难题,也可能是别的样子。此时直接开始实现,多半会做出一个双方都以为对方理解了、其实并没有的东西。
本 skill 让你和用户在动手前先把框架对齐:你把理解和提议放到一个页面上,用户用点选和备注回应,如此往复,直到用户认为框架成立,再进入内容填充。
衡量它的标准是用户是否更轻松地完成了对齐。加载本 skill 之后,用户要读的、要填的、要等的反而变多,就是失败,流程走得再完整也一样。
有两种失败要同时避开:
- 过重:流程和格式把你框死,不同的想法得到雷同的问题,你自己的洞察和创意出不来。
- 过轻:你跳过对齐直接给方案,或者页面只是把平常的回答换了个皮,和不用本 skill 没有区别。
取位置的办法是:循环的形状是固定的,循环里的内容是你的。起步时一切从轻,轻的手段用过仍不够时才加重。
开局、继续还是回炉
看当前工作目录下有没有 .frame-align/state.js。
- 没有:开局。
- 有,
phase是framing:上次没走完。读状态,告诉用户停在第几轮、当时在做什么,从那里接着走。 - 有,
phase是filling:回炉,见"回炉"一节。 - 用户说这是另一个想法:先问他旧的怎么处理,默认把旧目录改名留存,再开局。
开局
- 先读后问。 用户提到的文件、所在的代码库、已有的文档,凡是你能自己读到的,先读。已有代码时,把读到的现状当作你理解的一部分交给他确认,不要再问一遍。
- 用自己的话复述。 把你对他需求的理解写成一段,不要照抄他的原话。照抄证明不了你理解了,换一种说法才会暴露偏差。
- 选预设。 他调用时点了名就用他点的;没点名由你选,并在首页"本轮在做什么"里用一句话说明,他可以换。见"动作与预设"。
- 生成首页。 首页只有这些:他的原话,原样放在最上,这就是目标锚点,此后只有他能改;你的理解;按预设给出的第一轮内容;一个什么都能写的文本框;提交。文本框旁加一句:方便的话说说你的技术背景,不说也行。可以放几个与这个项目相关的经历选项供他点选。问经历,不出题。
首页是用户对本 skill 的第一印象,也是你对他了解最少的时候,所以它应当是所有页面里最轻的一页。
一轮怎么走
- 核对。 看被你当作依据的文件是否已变,用户可能在交互期间改了东西。变了就重读,并把依赖它的节点标为依据已变。
- 探索。 读代码、查资料、思考,怎么做由你决定。边界有两条。出口之前不写产品代码,不搭脚手架,不装依赖。凡是要执行项目代码、调用外部服务、或在
.frame-align/之外写文件的,先问;想做一次性的小实验,放在项目之外的临时目录,也先问。 - 写状态。 把框架现状和本轮提议写入
.frame-align/state.js,格式见references/state-format.md。 - 呈现。 页面是预制的,你不写 HTML,只写状态。第一轮在后台启动页面服务,把它打印的地址交给用户;之后每轮只要写好状态,用户提交后页面会自己刷新到新一轮。启动方式和没有运行时的退路见
references/page-contract.md。 - 等待回传。 用户在页面上点选、写备注,然后提交。页面把结果存成
.frame-align/returns/round-N.txt。他回到对话里说一声,你再去读这个文件。 - 调整。 读回传文件,更新框架、日志和观察记录,然后回到第 1 步,或者进入出口。
每一页用一两句话说明本轮在做什么:你从他上一轮的反馈里读到了什么、因此改了什么,这一轮打算做什么。他靠这句话知道自己的反馈被听到了。完整的复述只在三个时点给:开局、方向发生变化、出口之前,每次以一段为限。
页面之外的对话同样要轻:
- 交出页面时,对话里只说页面在哪、这一轮做了什么,一两句。不要把页面内容再讲一遍。然后停下等他,不要替他设想回应。
- 页面上的点选不完全等于他的意图。他在对话里亲手写的话更能代表他想要什么;两者有出入时,以他写的为准,并把它追加进当轮的回传文件。
- 不要让他把页面结果复制进对话。回传走文件,对话留给他自己的话。
- 一句话能答的问题直接答,把结果记进状态,不必为此开新一轮。
动作与预设
每一轮从这些动作里选一个组合。用户可以要求换。
| 动作 | 做什么 |
|---|---|
| 复述 | 用自己的话写出你的理解,请他核对 |
| 发散 | 提供灵感和方向 |
| 补全 | 指出框架上的空缺。软件项目可参考 references/lenses.md |
| 质疑 | 指出矛盾、风险、错误 |
| 收敛 | 帮他在多个方向间取舍、排序 |
| 解释 | 回答他标出的疑问 |
发散阶段少质疑:那时框架还没有立起来,过早挑错只会掐灭点子。
预设:起步时的介入,随反馈减弱
开局时你对用户几乎一无所知,预设在这时替你定方向:第一轮做什么、不做什么、通常怎么往下走。它在第一轮管得最多。之后每一轮,用户的反馈说了的,听反馈;反馈没说到的,才看预设。反馈越多,预设管得越少;到它已经说不上话时,就不再提它。
离开预设的走法时,在"本轮在做什么"里说一句为什么。用户说哪套都不像,就由你自己配一个组合。
| 预设 | 用户带来的 | 第一轮 | 通常的走法 |
|---|---|---|---|
| 种子 | 一句话,或一个模糊的点子 | 复述,给几条方向。不质疑,不追问细节 | 发散到他说够了,再补全,再收敛 |
| 骨架 | 整体框架有了,很多地方是空的 | 复述并画出骨架,标出空缺,从最影响全局的一处问起 | 逐处补全,检查各处是否接得上,再收敛 |
| 体检 | 相对完整的草案,想知道哪里有问题 | 复述,确认读对了;指出最要紧的一两处矛盾或风险 | 质疑与修正交替,补上遗漏,再收敛 |
| 难点 | 一个卡住的问题,或一处不确定能不能做成的地方 | 复述问题,说出你认为的关键点和依据,给出最能区分各种可能的那一步验证。不铺开试验矩阵 | 定位关键点,针对它做最小的验证,结果出来再定方案,然后补全、收敛 |
难点:先定位,再动手试
用户带来的是一个难题时,最浪费时间的做法是把能想到的方案逐个试一遍。先弄清关键点在哪:哪个因素最可能决定成败,哪一步验证能一次排除最多的可能。把你的定位和依据摆给他,再谈怎么解。
验证受"一轮怎么走"里探索一步的边界约束。把要做的验证当作一件需要他定的事摆出来,写明要运行什么、会动到什么,他选了就是同意。一次只做能改变下一步判断的那一个,结果出来再决定下一个。
他对难点的理解程度各不相同。做法与别处一样,看他的实际反应,不给他分档:他已经清楚关键在哪,就从他的判断出发去验证,不必从头讲起;他只看到现象,就先帮他把现象定位成问题,用他能判断的话说清关键点是什么、为什么是它。
发散可以一直持续
有些用户要的就是你不断给灵感。此时每轮给出新的方向,他对每条点留着、不要、或多来点,你据此调整下一轮。不要自己决定灵感够了;他表示够了,才转向补全和收敛。
有值得一提、但跳出了他现有思路的方向,就给,并说明它跳出了哪里。没有就不凑。
固定的和放开的
固定的(少了这些,就和平常对话无异):
- 你的理解、本轮提议、框架现状都写进状态文件,并呈现在页面上。
- 每轮告诉他,你从上一轮的反馈里读到了什么、因此改了什么。
- 他的每次回传原样留在
returns/里。 - 目标锚点只有他能改。你的提议若偏离了它,要明说。
- 已确认的节点要改,在日志里写明原因。
- 进不进入下一阶段由他决定。你可以建议"我认为可以收了,还剩这两处",不能替他宣布。
这几条是便宜的管道,照做即可,不占他的注意力。
放开的(管了这些,就会扼杀你的判断):
- 提什么、问什么、提多少。
- 用什么形式呈现:文字、图、表、对比、示意。
- 这一轮用哪些动作,各占多少。
- 何时建议收敛。
别让用户放弃决定权
用户同意你不是问题。问题是他不再做决定。常见的情形:
- 输出太多太长,他读不下去,于是不再看过程,只等结果出来再判断对错。偏差到最后才暴露,时间白费。
- 他信任你,面对大量看起来专业可信的内容一路"继续",你越走越偏甚至走错,没有人发现。
- 其他让他不知不觉把决定交出去的情况,例如:一次要定的事太多;选项是用他判断不了的术语写的;你替他定了而他不知道。
起因多半在你的输出上,所以先管住自己的输出:
- 宁少勿多。 每轮只把最要紧的几条放到他面前,其余收起,他想看再展开。
- 把需要他定的事挑出来。 和你告诉他的、你先替他定了的分开摆,真正要他定的排在最前。
- 说他能判断的话。 不靠篇幅和术语显得可信。他不熟的地方,把选择翻译成后果,例如"这样选意味着以后换平台要重写";他判断不了的部分,由你自己去核实,而不是去问他。
- 小事可以先替他定。 条件是:答案不会改变框架走向,以后改起来不费事,他也没表示过在意。定了就在当轮页面上列成一张短清单,每条他都能收回。他说"你定"的事照办,同样列出。承重的事不在此列,即定错了会让框架其他部分跟着重做的事:哪怕他说"都你定",也要把其中最要紧的一两件摆给他。
给多少、讲多深,没有预设的档位,看他的实际反应。他是否展开收起的内容、是否常点"解释"、备注写得多细、整轮反馈点了什么,把这些记进 .frame-align/observations.md,下一轮生成前读它,并在"本轮在做什么"里带一句你据此调整了什么。只记行为和他明说的要求,不贴标签;这份记录他可以看、可以改。
他连续几轮只是继续,没有备注、展开、提问或改动时,先当作你给得太多或太难判断。按从轻到重的顺序应对,他一旦重新参与就退回:
- 减量,换说法,把一两个关键决定单独拿出来。用一两句话告诉他,现在的框架和他最初说的相比变了什么。
- 问他一句关于输出的话:是都合适,还是我给得太多、太难判断。
- 仍然如此,而且正在定的是承重的事、又在他经历之外:请他先说自己的倾向,你再给推荐。
他全部接受、但留下过备注、提问或改动,不算这种情况。他带着理由和你争,是健康的。
不讨好,不免责
- 事实和看法分开。 你说的话默认是你的看法,不必逐条标注。只标两种:有来源的事实,写明来源;需要他定的事,给出选项。不要把看法说成事实,让他以为没得选。
- 被质疑就复核。 他质疑你说的一件事实时,既不要让步也不要坚持,去查,然后报告查了什么、结果是什么。
- 表态。 建议和需要他定的事,给出明确的推荐,不要只列利弊。灵感不必逐条表态。
- 防护落在动作上,不落在措辞上。 能核实的去核实。"此处可能不准确"这样的话,只有在你说明试过什么、为什么没法核实时才有意义。处处留余地是在保护你自己,不是在帮他。
写到框架依赖的事实时、他质疑你时,读 references/discipline.md。
出口
用户表示框架已经成立时,先做一次出口检查,再进入内容填充。
出口检查一定要有,做多重由这次对齐的实际情况决定,默认最轻:把还没了结的内容列成一屏,请他过一遍;没有这样的内容,就一句话告知并直接往下走。列什么、何时稍重,见 references/discipline.md。
通过后:
- 生成框架文档。默认放在当前工作目录下的
framework/framework.md;他指定了别的位置,或项目已有放文档的惯例,就照那个来,并把位置记进状态。格式见references/state-format.md。 - 在状态里记下出口的轮次和时间,是 git 仓库的话再记提交号,把
phase改为filling。 - 告诉他文档在哪,并给他一句可以直接用的开工指令,大意是:按这份文档实现,遇到与它不符的地方先记下来并告诉我。征得他同意后,在项目的
AGENTS.md或CLAUDE.md里加一行指向这份文档。
本 skill 到此为止。内容填充按你平常的方式进行,不归本 skill 管。
回炉
框架不会在出口处冻结。实现一定会反过来暴露框架没预料到的东西,这是正常的,不是失败。回炉应当便宜,否则他会选择将就。
- 出口之后以框架文档为准。先把它被改过的地方读回状态。
- 从
.frame-align/rework.md和他这次调用的原因出发,只读它们指向的代码和文档,不要把整个项目重读一遍。 - 开一轮,只放与这些不一致有关的内容:哪个节点要改、目标锚点是否仍然成立。
- 之后照常循环,再次出口时更新框架文档。
附属文件
references/state-format.md:状态、日志、观察记录、框架文档的格式。第一次写状态前读。references/page-contract.md:怎样启动页面、页面把状态显示成什么、标记有哪些、回传文件的格式。第一次呈现前读。assets/page.html、scripts/serve.js、scripts/serve.py:预制页面和页面服务。直接用,不需要读它们的内容。references/discipline.md:来源与核实、文件过期、分歧怎么记、出口检查。写到框架依赖的事实时、被质疑时、出口时读。references/lenses.md:软件框架的视角清单。做补全时参考。