SuperAPI AI Directory

workspace-topology

Deployment & operations

View repository

Updated 10-15 12:45

About this skill

Workspace Topology

The shape of the repo: where the code is, where the docs are. Not the code content, not the doc content.

Three shapes:

Shape Code Docs Code owned by this repo
inline(default) Repo root <仓库根>/docs/ Yes
shell Under each git submodule <仓库根>/docs/ Yes
linked Local directory reference, inside or outside the repo <仓库根>/docs/ No

The docs root is docs/under all three shapes, which is a fixed invariant with no configuration field. Only where the code is and who owns it vary.

shell Serves two zero-pollution scenarios: maintaining open source projects (your own docs must not go into the upstream repo) and proprietary projects pending release. linked Serves the scenario where "the code root does not belong to this repo": multi-repo joint debugging, temporary workspaces assembled on demand, wanting only the docs repo to remember where the related code is. What means to use to organize it (worktree / clone / symlink / just a directory) is up to the user; this skill only recognizes, does not create。

Three entry points

Entry point What it does Does it write files?
inspect Returns the topology context for other flows to consume Only when the declaration is missing and cannot be determined: ask once and write the declaration; do not write when a declaration already exists
reconcile Interactively maintain the declaration (idempotent, reentrant) Yes
migrate --to shell --mode manual|auto Repo transformation from single repo → shell layout Yes

Calling with no arguments is equivalent to reconcile。

When there is no declaration: ask, do not guess

If there is a workspace: declaration, use it directly.If there is no declaration, ask the user, write the answer into AGENTS.md, and never ask again after that.

1. 问 mode:inline / shell / linked 三选一
2. 按 mode 问代码根:
     inline → 不用问,代码根就是仓库根
     shell  → 列出 .gitmodules 的条目,多选哪些是代码根
     linked → 问路径,可多个(相对 / ~/ / 绝对都行)
3. 写进 AGENTS.md 的 workspace: 块

⛔ No automatic determination. The repo shape is a product decision, not a fact that can be inferred from the file layout — especially the linked code root can be outside the repo, and no in-repo scan can discover it.

Scanning is only used to order the presets of the options; guessing wrong does not matter:

If scanned Preset
.gitmodules If there is an entry in shell , order it first, and list submodules as code root candidates
The repo root has its own files inline , order it first
Neither Lay out the three options flat

To determine "the repo root has its own files", use git ls-files -s and exclude entries whose mode is 160000 — those are submodule records, not files of this repo. If they are not excluded, every shell repo will be preset as inline。

⚠️ Submodules may also be vendor / assets, so under shell ,whichsubmodules count as code roots must be asked; you cannot take all entries of .gitmodules at face value.

inspect

Read the declaration and return yaml-summary-v1, with the topology context written to summary.details。When a declaration already exists, it is purely read-only; when there is no declaration, ask once as in the previous section and write the declaration, and after that it is always read-only.

schema: workspace-context.v2
mode: inline | shell | linked
workspace_root: /abs/path
docs_dir: docs
code_roots:
  - name: api
    path: /abs/resolved/path      # 解析后的绝对路径,消费方唯一要用的
    declared_path: ../pool/api    # 声明原文,供报告与诊断
missing_code_roots:               # 声明了但解析不到实体
  - name: web
    declared_path: ../pool/web

Under all three modes, the code_roots element shapeis completely identical——Caller does not branch,inline is also a code root (single item,path = repository root absolute path).

inline when , the single item takes {name: ".", path: <仓根绝对路径>, declared_path: "."} —— name and declared_path preserve . as a sentinel, so consumers can distinguish based on it without reading mode。

  • path gives the resolved absolute path, consumers do zero parsing;declared_path is only for human viewing
  • Code roots that cannot be resolved do not go into code_roots, they go into missing_code_roots. Every item the consumer gets can directly cd, no need to probe liveness first
  • missing_code_roots non-empty does not block the flow, ⚠️ just report it. The real failure is left until the consumer actually needs to use it
  • detected kind / branch / upstream does not go into the contract, only in reconcile and the report for human viewing—consumers only need cd to get in and work

How consumers get it

⛔ Consumers are not required to call this skill themselves. Most atomic skills' allowed-tools do not have Task, so they cannot dispatch sub-Agents; letting them read the declaration file themselves would leak implementation details.

Use constraints.md §3 's minimal handshake protocol:

  • Orchestration layer(pipeline / feature / bugfix / dev-workflow) calls inspectonce at the start of the flow, passing workspace_context down with the handshake's inputs pass down
  • Atomic skill reads from inputs.workspace_context , no need for Task
  • Standalone run(user directly calls an atomic skill, no orchestration layer): no workspace_context → follow inline defaults + ℹ️ prompt "shell / linked projects should be called via the orchestration layer"

What consumers depend on is theinput parameter shape, not the declaration file, not .gitmodules。

reconcile

interactive maintenance and declaration.Idempotent: if the state is consistent, it is a no-op and produces no empty commit.

Action Semantics
Reconfirm Ask once about mode and code roots per "## When there is no declaration: ask, don't guess", overwriting the existing declaration. ⛔ Do not auto-determine
Adjust code_roots Declaration-only change
Existing submodule added to management scope Declaration-only change
Add submodule User provides URL and path → git submodule add → append code_roots
Exit code_roots Declaration-only exit, meaning "no longer a working code root". ⛔ Do not touch .gitmodules, do not touch the local directory
legacy field migration See "Metadata" below, one-time
Resume after interruption Fromactual git statecontinue, without relying on a record of "which step was executed"
linked path maintenance Ask the user for paths (may be multiple). Adding/removing is adeclaration-only change, ⛔ do not create a worktree, do not clone, do not touch the referenced directory

Two boundaries that must be distinguished:

  1. Changing the declaration ≠ changing the real topology。mode fields can be changed freely, but inline → shell real topology changes are repository restructuring, go through migrate。
  2. Exit code_roots ≠ delete submodules. The latter would modify .gitmodules, gitlink, and local directories; it is a destructive operation, and this skill does not do it, leaving it to the user to perform.

Self-verify after writing: immediately rerun the full verification table; any ⛔ → roll back this write and report. ⛔ "command exit code 0" must not be taken to mean completion.

shell → inline: provides manual planning only, does not promise automation—with multiple code roots, there is no safe default answer for "which repo to merge into / whether to preserve shell history / how to handle multiple remotes."

migrate

migrate only occurs between inline ↔ shell , in both directions.linked does not participate in any direction— the criterion is ownership; for the full rationale see references/migration.md. When conversion between linked and other forms is needed, the user performs the physical transformation themselves, then runs reconcile to redeclare.

Key points:

  • triggered only by explicit user intent, this skill never proactively proposes it—that is a product decision, not a spec gap
  • The boundary between the two modes iswho moves the code repo into the root directory: in manual mode the user handles this action that crosses the workspace boundary (parent directory write permission, cwd migration), while the rest is still completed by this skill
  • The order is a hard constraint: create shell → whole repo mv in → submodule add register → copy docs → verify the copy is in place → only then delete the original → write declaration → push and wrap up
  • ⛔ Do not do rebase / filter / squash / force-push; ⛔ do not push silently; ⛔ do not automatically delete the original checkout; ⛔ do not migrate the code repo's own docs (API docs, README material) along with it

Metadata

The declaration is in the project root AGENTS.md frontmatter'sindependent workspace: block:

---
workspace:
  mode: shell
  code_roots: [web, api]      # mode=shell / mode=linked 时必填。shell 下元素为 .gitmodules 的 submodule name;linked 下须给 path,见下方 linked 示例
devdocs:                      # keel 项目才有;非 keel 项目只有 workspace: 块
---

linked Form and object item syntax:

---
workspace:
  mode: linked
  code_roots:
    - name: api
      path: ../pool/api          # linked 下必填
      upstream: ssh://...        # 可选,重建线索
      branch: feature-x          # 可选,重建线索
---

code_roots accepts both string items and object items; the string foo is equivalent to {name: foo}。shell name is sufficient (the path source of truth is .gitmodules),Existing declarations require zero changes;linked must provide path。

Why it is a separate block: this is a repository-level fact, not a keel fact. Hanging it under devdocs: would make "declaring a repository setting" have to go through the keel process.

The path source of truth is .gitmodules(only shell),code_roots records only the submodule name, and at runtime it is resolved via git config -f .gitmodules submodule.<name>.path . Changing a submodule's path only changes .gitmodules, and the declaration does not need to follow.linked Under , the declaration itself is the source of truth.

Ownership

Who owns what
workspace-topology workspace: block
agent-memory body + devdocs: block; ⛔ for workspace: blockpreserve as-is, do not overwrite or delete

legacy compatibility

Older versions put fields under devdocs.workspace_mode / devdocs.code_roots。

Situation Behavior
Only workspace: block Read normally
Only legacy fields Read-only compatibility;reconcile When , migrate to workspace: and delete the old field
Both places coexist andconsistent Migrate and remove old fields
Both places coexist andconflict ⛔ Blocked; the user decides which place is authoritative
The field is split across two places (mode in the new,code_roots in the old) ⛔ Blocked—concatenating the two declarations leaves "which place is authoritative" without a single answer
neither has it inline, zero impact

code_roots comparison is bysetsemantics; order is meaningless.

Validation (fail-closed)

The whole table is split by mode; authority lies in references/protocol.md §3. The three most easily hit:

Case Behavior
The declared code root cannot be resolved ⚠️ Classified under missing_code_roots, does not block the process
Bare gitlink (tracks mode 160000 but .gitmodules has no entry) ⛔ All modes—objectively corrupted; after clone it is an empty directory and cannot be recovered
The resolved path escapes the authorized working scope ⛔ All modes—a path declaration is not write-permission authorization

Leftover files at the repo root

shell Under, the repository root is not in docs/, does not belong to any code_root of thetracked files → ℹ️ List them and hand them to the user for judgment.

⛔ Do not guess which counts as source code. Upstream repo directory conventions vary; a static allowlist would cause false positives. List them and let people look.

Commit

Whoever does the work commits: this skill commits what it produces itself (.gitmodules、workspace: blocks, file moves during migration); it does not commit on behalf of other skills, nor is it used by other skills to commit on their behalf.

Cross-repo commit protocol (shell under, Commit 1 expands to N+1 repos) see references/protocol.md, executed by the working skill per the provisions.

Detailed protocol