重构扫描
检测,但不写入。 提议下一步可做的工作,并注意到自上次扫描以来哪些问题已自行解决——从不创建 Issue,也从不替用户做决定。refactor-design 负责为选中的提议创建 Issue;refactor-learn 则根据此技能检测到的结果采取行动。
流程
1. 检查前置条件
- 没有Git仓库 → 停止本轮扫描,报告情况,不提出任何提议。
- 存在五个或更多打开的
refactor:candidate标签的 Issue → 停止,不提出新提议;让现有工作先清理完成。
2. 优先恢复待办工作
读取重构笔记的 bookkeeping.md 中的 Pending candidates 字段(路径:skills/continuous-refactoring/references/refactoring-bookkeeping.md)。如果该字段指定了一个 Issue,表示前一轮的 refactor-design 已为其创建了 Issue,但在遵循合并请求之前被中断。此时直接提议该 Issue 并停止——完成待办工作优先于提出新工作。
3. 检测已关闭/合并的已记录状态
获取已记录的集合——docs/agents/issue-tracker.md 指定了一个原生标签跟踪器(GitHub, GitLab)→ 获取所有打开的 refactor:candidate Issue,并通过跟踪器自身的原生 Issue↔关闭PR 交叉引用(Closes #<n>,即 refactor-implement 步骤5已放置在交付MR上的信息)将每个Issue解析到其关联的拉取请求;如果Issue还没有关联的PR,则此步骤无需处理。否则 → 获取重构笔记的 merge-requests.md 中的每一项。针对每一项,检查外部跟踪器/git:该MR是否仍然打开?该Issue是否仍然打开?
没有 gh/glab(或其他Forge API/令牌)可用 → 回退到仅使用Git进行协调,而不是跳过此步骤(也不要尝试在此处安装 gh/glab——将其缺失视为此回退的精确触发条件):skills/refactor-scan/references/git-only-reconciliation.md。没有远程仓库(git remote -v 为空)→ 使用同一文件的纯本地变体——一个已记录的候选者可能已被人类在纯本地环境中合并或拒绝,遵循 opening-a-merge-request.md 中的“无Forge/远程可用”。
-
已合并 → 发现:已交付。
-
未合并即关闭 → 发现:已拒绝——记录关闭评论是否给出了维护者的结构性原因(超出范围的材料)或没有。
-
仍打开,且有审阅活动(审阅或评论)晚于分支的最后一次提交 → 一个恢复候选者,不是发现——没有人做出任何决定,只是需要再看一次。如果API可用 → 同时根据时间戳读取实际的审阅状态(
gh pr view <n> --json reviews,reviewDecision或等效命令)——不要仅仅因为“有变化”就停止:CHANGES_REQUESTED(请求变更),且未被解决(没有比审阅更新的提交)→ 仍然是恢复候选者,但需要将审阅自身的评论/正文连同分支一起传递——refactor-implement的步骤1将读取并处理该特定反馈,而不仅仅是重新从头检查分支。- 一条评论,或一个
APPROVED(批准)审阅且没有阻塞性反馈 → 执行现有的通用恢复候选者处理;不需要附加其他内容。
按照步骤2将
Pending candidates条目传递出去的相同方式传递它:直接交给refactor-implement,跳过refactor-prioritize/refactor-design(它已经有了设计和打开的MR;只应用修复循环——refactor-implement的步骤1已经支持恢复现有分支)。没有API访问(仅Git回退)→ 审阅状态不可用,正如git-only-reconciliation.md已记录的那样;此细化不适用于此处。 -
仍打开,但无任何变化 → 没有发现。
同时检查重构笔记的 out-of-scope/<node>.md 中的每一项,这些项标明了 **Blocked by:** PHP >= X.Y 条件(tooling_tree.py 的 detect_nodes() 在运行时直接报告此信息;手动操作时,对照目标当前的 composer.json require.php/config.platform.php 进行比较)——如果目标的当前PHP版本现在满足此条件,则有一个发现:此拒绝被撤销。没有 Blocked by: 字段,或条件仍未满足 → 永远不是发现。
将所有发现交给 refactor-learn——此技能仅负责检测;它永远不会将任何内容标记为 done/wontfix,也永远不会自行写入重构笔记的 out-of-scope/ 目录。
3b. 检测外部标记的候选者
API可用(非仅Git回退)→ 同时查询那些没有被步骤2的 Pending candidates 条目或步骤3的已记录集合覆盖的打开的 refactor:candidate Issue(一个有关联拉取请求的Issue已经在进行中)。剩下的,就是由人类(或其他进程)直接标记的候选者,此循环从未为其设计或实施过——在正常操作中,套件自身的候选者总是被上述两者之一覆盖,因此无需额外操作来区分“我们的”和“别人的”。将每个候选者作为提议传递出去,与步骤4的工具树提议加入的列表相同——refactor-prioritize 将根据其常规因素对所有提议进行排序;它凭自身能力竞争,不会仅仅因为存在而插队。没有API访问(仅Git回退)→ 完全跳过此步骤,纯Git无法按标签枚举Issue。
4. 提议工具树节点
如果步骤2已经提出了一个待办候选者,则跳过此步骤。
运行 python3 skills/refactor-scan/references/tooling_tree.py <target-repo> 并读取JSON的 next 字段——真实的、当前未被阻塞的集合,已排除被拒绝的节点(重构笔记 out-of-scope/<node>.md 中已有的条目),照单全收,无论有多少个条目。注意是 next,不是 roadmap(roadmap是前瞻模拟,不是今天真实可选的)。同时读取 withheld:那些本来应该在 next 中,但等待一个未决定的推荐父节点的节点——每个条目指明了等待哪个/哪些父节点。没有 python3,或不允许执行 → 使用 skills/refactor-scan/references/tree-walk-prompt.md 中的提示({N}=all)分派一个子智能体——它将手动遍历相同的树文档(首先读取重构笔记的 bookkeeping.md 中的 Fulfilled nodes 以跳过重新推导缓存状态,跳过任何有超出范围条目的节点);没有子智能体机制 → 自己内联执行其步骤。
- 普通工具节点(
loop-config和语言特化节点,例如skills/refactor-scan/references/php-tooling-tree.md)——通过它们的名称(绝不是原始slug)提议;每个节点已在其树文档中完全指定。 structural-scan——仅在每一个具有指向它的resolved边的节点都已解决(已满足,或在重构笔记的out-of-scope/下被明确拒绝)时才提议:通用根目录下的editorconfig,加上活动语言特化自身的聚合节点(PHP:php-structural-scan),而聚合节点本身又在其每一个已解决的父节点都解决后才解决(skills/refactor-scan/references/tooling-tree.md)。只有structural-scan会以这种方式被提议——php-structural-scan纯粹是管道,永远不会成为候选者。提议它仅仅是命名它;代码库遍历发生在refactor-design中,仅针对实际被选中的节点。- 未识别到语言树:
structural-scan仍然等待editorconfig,即通用根节点的叶子节点——不能仅仅因为没有特定语言的树适用就立即提议。
输出
由编排器清晰地传递下去:
- 如果某前置条件阻止了本次扫描,则报告哪个前置条件——本次扫描下面的内容均不适用。
- 发现(可能为空)→ 交给
refactor-learn。 - 一个恢复候选者(如果有检测到)→ 直接交给
refactor-implement,绕过refactor-prioritize/refactor-design。 - 提议——仅限待办候选者,或者当前所有未被阻塞节点的名称(绝不是slug,不设上限)加上步骤3b中的任何外部标记的候选者,或者为空 → 交给
refactor-prioritize。包含所有当前未被阻塞的节点(所需父节点已满足、未被拒绝,且所有推荐父节点已决定)——绝不是按优先级截断的子集。同时,列出每个withheld节点及其正在等待的父节点(例如:“Rector: Type Coverage Set — 等待:PHP CS Fixer”)。
完成标准
发现(如果有)已交给 refactor-learn,恢复候选者(如果有)已直接交给 refactor-implement,提议(如果有)已交给 refactor-prioritize——或者有前置条件阻止了本次扫描并且报告指明了是哪个。绝不将一个节点与 structural-scan 之后的条目放在同一个列表中。