用途
workspace-topology 管的是仓库的形状:代码在哪、文档在哪。它不关心代码内容,也不关心文档内容,只负责声明与维护工作区的布局拓扑。文档根固定为 <仓库根>/docs/,这是三种形状共有的不变量,不设配置字段;会变的只有代码在哪、以及代码归属关系。
三种形状
| 形状 | 代码 | 文档 | 代码归本仓所有 |
|---|---|---|---|
| inline(缺省) | 仓库根 | <仓库根>/docs/ | 是 |
| shell | 各 git 子模块下 | <仓库根>/docs/ | 是 |
| linked | 本机目录引用,仓内仓外皆可 | <仓库根>/docs/ | 否 |
shell 服务于两类零污染场景:维护开源项目(自己的文档不能进上游仓)、自有项目待公开。linked 服务于「代码根不属于本仓」的场景:多仓联调、按需组合的临时工作区、只想让文档仓记住相关代码在哪。组织形式(worktree / clone / symlink / 普通目录)由用户决定,该技能只认不造。
三个入口
| 入口 | 做什么 | 写文件吗 |
|---|---|---|
| inspect | 返回拓扑上下文,供其他流程消费 | 仅在声明缺失且判不出时:问一次并落声明;已有声明时不写 |
| reconcile | 交互式维护声明(幂等,可重入) | 是 |
| migrate --to shell --mode manual|auto | 单仓 → 外壳布局的仓库改造 | 是 |
无参数调用等同 reconcile。
无声明时的处理:问,不猜
有 workspace: 声明就直接用;没有就问用户,答案写进 AGENTS.md,此后不再询问。流程为:问 mode(inline / shell / linked),再按 mode 问代码根——inline 不需问,代码根即仓库根;shell 列出 .gitmodules 条目供多选;linked 问路径,可多个(相对、~ 或绝对均可),最后写入 AGENTS.md 的 workspace: 块。该技能不做自动判定,因为仓库形状是产品决策,无法从文件布局推断,尤其 linked 的代码根可在仓外,任何仓内扫描都发现不了。扫描仅用于给选项排预设:有 .gitmodules 条目则 shell 排前;仓根有自己的文件则 inline 排前;都没有则平铺。判断「仓根有自己的文件」需用 git ls-files -s 并排除 mode 为 160000 的条目(子模块记录),否则每个外壳仓都会被预设成 inline。子模块也可能是 vendor 或素材,所以 shell 下哪些子模块算代码根必须问,不能照单全收。
inspect
读声明并返回 yaml-summary-v1,拓扑上下文落在 summary.details。已有声明时纯只读;无声明时问一次并落声明,之后一直只读。schema 为 workspace-context.v2,包含 mode、workspace_root、docs_dir、code_roots、missing_code_roots 等字段。三种 mode 下 code_roots 元素形状完全一致,调用方不需分支;inline 也是单项代码根,取 {name: ".", path: <仓根绝对路径>, declared_path: "."}。path 给解析后的绝对路径,消费方零解析;declared_path 仅供人看。解析不到的代码根进入 missing_code_roots,不阻塞流程,仅报告。探测到的 kind / branch / upstream 不进入契约,只在 reconcile 与报告中展示。
消费方获取方式:不要求消费方自己调该技能,多数原子技能的 allowed-tools 没有 Task,无法派发子 Agent。编排层(pipeline / feature / bugfix / dev-workflow)在流程开头调一次 inspect,把 workspace_context 随握手 inputs 下传;原子技能从 inputs.workspace_context 读取。单跑时若无 workspace_context,按 inline 缺省处理并提示 shell / linked 项目请经编排层调用。
reconcile
交互式维护声明,幂等:状态一致则 no-op。动作包括:重新确认(覆盖已有声明,不自动判定)、调整 code_roots、已存在子模块加入管理范围、新增子模块(用户给 URL 与路径 → git submodule add → 追加 code_roots)、退出 code_roots(仅退声明,不动 .gitmodules 与本地目录)、legacy 字段迁移、中断续做、linked 路径维护。需区分两点:改声明 ≠ 改真实拓扑(inline → shell 的真实变更走 migrate);退出 code_roots ≠ 删子模块(后者是破坏性操作,该技能不做)。写入后立即重跑校验全表,任一错误则回滚并报告。shell → inline 只给手动规划,不承诺自动化。
migrate
仅在 inline ↔ shell 之间发生,双向皆可;linked 任何方向都不参与,判据是所有权。仅由用户显式意图触发,该技能永不主动提议。手动模式与自动模式的分界在谁把代码仓挪进根目录。顺序是硬约束:建壳 → 整仓 mv 进去 → submodule add 登记 → 复制文档 → 校验复制到位 → 才删原件 → 写声明 → 推送收尾。不做 rebase / filter / squash / force-push,不静默 push,不自动删除原 checkout,不把代码仓自有文档一并迁走。
元数据与所有权
声明在项目根 AGENTS.md frontmatter 的独立 workspace: 块中。code_roots 同时接受字符串项与对象项;shell 下 name 即 submodule name(路径真源是 .gitmodules),linked 下必须给 path。workspace-topology 拥有 workspace: 块;agent-memory 拥有正文与 devdocs: 块,对 workspace: 块原样保留。legacy 兼容方面,旧版本把字段放在 devdocs.workspace_mode / devdocs.code_roots,只在 legacy 字段时只读兼容并在 reconcile 时迁移;两处并存冲突或跨两处拆分时阻塞,由用户裁决。
校验(fail-closed)
三条最易踩的规则:声明的代码根解析不到 → 归入 missing_code_roots 不阻塞;裸 gitlink(跟踪 mode 160000 但 .gitmodules 无条目)→ 全 mode 阻塞;解析后路径逃逸出已授权工作范围 → 全 mode 阻塞。shell 下仓根残留的、不属任何 code_root 的已跟踪文件会被列出交用户判断,不猜测哪个算源码。提交遵循「谁干活谁提交」,跨仓提交协议由干活的技能按条文执行。
限制
本技能不自动判定仓库形状,不管理代码或文档内容,linked 形态不参与 migrate,不做破坏性的子模块删除或 rebase / force-push 等操作。更详细的校验全表、零污染写入范围、N+1 仓提交协议与迁移双模式说明见其 references 目录。