Files
llm-wiki/docs/superpowers/plans/2026-06-04-three-platform-sync-phase0-engine-agents.md
T

810 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 3-플랫폼 동기화 Phase 0 — 생성 엔진 + agents Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Claude의 9개 agent를 Codex CLI(`.codex/agents/*.toml`)와 Antigravity CLI(`.agents/agents/<name>/agent.json`)의 native 포맷으로 생성하는 멱등 스크립트 `scripts/sync-automation.py`를 만들고, 누락된 2개 agent(`branch-depth-auditor`·`coverage-auditor`)를 양 플랫폼에 추가한다.
**Architecture:** Claude `.claude/agents/<name>.md` frontmatter의 `tools:`를 **권한 SSOT**로 삼아 codex `sandbox_mode`와 antigravity `toolNames`를 기계적으로 파생한다. 프롬프트 본문은 플랫폼별 SSOT(`.codex/agents/<name>.md` = codex 일반화 본문, `.agents/plugins/wiki-superpowers/agents/<name>.md` = antigravity gated 본문)에서 가져와 native 머신 포맷으로 포장한다. 스크립트는 순수 함수(권한 파생·포맷 직렬화)와 I/O를 분리하고, `--check` 모드로 SSOT↔생성물 drift를 0으로 검증한다.
**Tech Stack:** Python 3.12 (stdlib only — `tomllib` for TOML 검증, `json`, `pathlib`, `argparse`), pytest.
---
## 배경 / 현재 상태 (실측)
- Claude agents (SSOT identity + 권한): `.claude/agents/*.md` — frontmatter `name`/`description`/`tools`(콤마 목록)/`model: sonnet`. 9개 존재.
- Codex agents: `.codex/agents/*.md` — frontmatter `name`/`description`(tools/model 없음) + 일반화 본문. **7개** (branch-depth-auditor·coverage-auditor 누락). codex가 실제 읽는 native 포맷은 `*.toml`인데 **하나도 없음**.
- Antigravity agents: `.agents/plugins/wiki-superpowers/agents/*.md` — frontmatter `name`/`description` + G1~G4 gate 본문. **7개** (동일 2개 누락). antigravity가 실제 읽는 `.agents/agents/<name>/agent.json` **하나도 없음** (디스크의 `~/.gemini/.../agent.json`은 stale·오류 생성물 — read-only agent에 write 도구가 들어있고 content가 요약됨. 신뢰 금지).
**확정된 agent.json 스키마** (실 디스크 `~/.gemini/antigravity-cli/brain/.../wiki-research-lane/agent.json`에서 키 구조만 채택):
```json
{
"name": "<name>",
"description": "<desc>",
"hidden": true,
"config": {
"customAgent": {
"systemPromptSections": [ { "title": "Agent System Instructions", "content": "<body>" } ],
"toolNames": [ ... ],
"systemPromptConfig": {
"includeSections": ["user_information","mcp_servers","skills","subagent_reminder","messaging","artifacts","user_rules"]
}
}
}
}
```
**권한 파생 규칙** (Claude `tools:` → 플랫폼):
| Claude tool | Antigravity `toolNames` | 비고 |
|---|---|---|
| (모든 agent 공통 baseline) | `send_message`, `view_file`, `find_by_name`, `grep_search`, `list_dir` | 9개 모두 Read+Grep+Glob 보유 |
| `Bash` | `run_command` | |
| `Edit` | `replace_file_content`, `multi_replace_file_content` | |
| `Write` | `write_to_file` | |
| `WebFetch` | `read_url_content` | |
| `WebSearch` | `search_web` | |
- Codex `sandbox_mode` = `workspace-write` (Claude tools에 `Edit` 또는 `Write` 포함 시) / else `read-only`.
- 9개 agent 권한 (Claude frontmatter 실측):
- read-only: `branch-depth-auditor`(Read,Grep,Glob), `coverage-auditor`·`wiki-adversarial-reviewer`·`wiki-diagram-reviewer`·`wiki-link-verifier`·`wiki-research-lane`(Read,Grep,Glob,Bash)
- workspace-write: `wiki-doc-author`(+Edit,Write), `wiki-source-summarizer`(+Edit,Write,WebFetch), `wiki-decision-researcher`(+Write,WebSearch,WebFetch)
---
## File Structure
- Create: `scripts/sync-automation.py` — 단일 진입점 CLI. 순수 함수 구역(파싱·권한 파생·직렬화) + I/O 구역(파일 read/write) + `argparse` CLI.
- Create: `scripts/test_sync_automation.py` — pytest 단위 테스트 (순수 함수 + 생성물 유효성).
- Create: `.codex/agents/branch-depth-auditor.md`, `.codex/agents/coverage-auditor.md` — codex 일반화 본문 SSOT (Claude에서 적응).
- Create: `.agents/plugins/wiki-superpowers/agents/branch-depth-auditor.md`, `.agents/plugins/wiki-superpowers/agents/coverage-auditor.md` — antigravity gated 본문 SSOT.
- Generate (스크립트 출력): `.codex/agents/<name>.toml` ×9, `.agents/agents/<name>/agent.json` ×9.
- Modify (Phase 0 말미, 최소): `.codex/agents/README.md`, `.agents/plugins/wiki-superpowers/README.md` — 생성 스크립트 사용법 1단락. (전체 문서 정리는 Phase 2.)
> 책임 경계: `sync-automation.py`는 *권한 파생 + 포맷 포장*만 한다. 본문 프로즈의 플랫폼 적응(일반화·gate 작성)은 사람이 SSOT `.md`에 직접 한다. codex는 `.toml`만 로드하므로 `.codex/agents/*.md`는 우리 SSOT로 남고 codex는 무시한다.
---
## Task 1: 스크립트 골격 + frontmatter 파싱 + 권한 파생 (순수 함수)
**Files:**
- Create: `scripts/sync-automation.py`
- Test: `scripts/test_sync_automation.py`
- [ ] **Step 1: Write the failing test**
```python
# scripts/test_sync_automation.py
import json
import tomllib
import sync_automation as s
def test_parse_frontmatter_extracts_fields():
md = (
"---\n"
"name: wiki-link-verifier\n"
"description: Audit the wiki for orphans.\n"
"tools: Read, Grep, Glob, Bash\n"
"model: sonnet\n"
"---\n\n"
"You are the Wiki Link Verifier.\n"
)
fm, body = s.parse_frontmatter(md)
assert fm["name"] == "wiki-link-verifier"
assert fm["description"] == "Audit the wiki for orphans."
assert fm["tools"] == "Read, Grep, Glob, Bash"
assert body == "You are the Wiki Link Verifier.\n"
def test_parse_tools_list():
assert s.parse_tools("Read, Grep, Glob, Bash") == ["Read", "Grep", "Glob", "Bash"]
assert s.parse_tools("Read,Edit,Write") == ["Read", "Edit", "Write"]
def test_sandbox_mode_read_only_when_no_write_tools():
assert s.codex_sandbox_mode(["Read", "Grep", "Glob", "Bash"]) == "read-only"
def test_sandbox_mode_workspace_write_when_edit_or_write():
assert s.codex_sandbox_mode(["Read", "Edit", "Write", "Bash"]) == "workspace-write"
assert s.codex_sandbox_mode(["Read", "Write", "WebSearch"]) == "workspace-write"
def test_antigravity_toolnames_read_only_agent():
# Read,Grep,Glob,Bash -> baseline + run_command, no write tools
assert s.antigravity_tool_names(["Read", "Grep", "Glob", "Bash"]) == [
"send_message", "view_file", "find_by_name", "grep_search", "list_dir",
"run_command",
]
def test_antigravity_toolnames_write_agent_with_web():
# source-summarizer: Read,Edit,Write,Bash,Grep,Glob,WebFetch
assert s.antigravity_tool_names(
["Read", "Edit", "Write", "Bash", "Grep", "Glob", "WebFetch"]
) == [
"send_message", "view_file", "find_by_name", "grep_search", "list_dir",
"write_to_file", "replace_file_content", "multi_replace_file_content",
"run_command", "read_url_content",
]
```
- [ ] **Step 2: Run test to verify it fails**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -v`
Expected: FAIL with `ModuleNotFoundError: No module named 'sync_automation'`.
> 참고: 파일명이 `sync-automation.py`(하이픈)라 `import sync_automation`이 안 된다. 테스트 상단에서 모듈을 로드하도록 `conftest.py`로 별칭을 만든다(아래 Step 3에 포함). 또는 파일명을 `sync_automation.py`로 하고 CLI는 `python3 scripts/sync_automation.py`로 부른다. **결정: 파일명을 `scripts/sync_automation.py`(언더스코어)로 한다** — import 가능 + CLI 호출에 지장 없음. 본 계획의 이후 모든 경로에서 `sync_automation.py`로 읽는다.
- [ ] **Step 3: Write minimal implementation**
```python
# scripts/sync_automation.py
"""Generate Codex/Antigravity native agent files from Claude SSOT.
Permission SSOT : .claude/agents/<name>.md frontmatter `tools:`
Codex body SSOT : .codex/agents/<name>.md
Antigravity SSOT : .agents/plugins/wiki-superpowers/agents/<name>.md
Outputs : .codex/agents/<name>.toml , .agents/agents/<name>/agent.json
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
CLAUDE_AGENTS = REPO / ".claude" / "agents"
CODEX_AGENTS = REPO / ".codex" / "agents"
ANTIGRAV_SSOT = REPO / ".agents" / "plugins" / "wiki-superpowers" / "agents"
ANTIGRAV_OUT = REPO / ".agents" / "agents"
AGENT_NAMES = [
"branch-depth-auditor",
"coverage-auditor",
"wiki-adversarial-reviewer",
"wiki-decision-researcher",
"wiki-diagram-reviewer",
"wiki-doc-author",
"wiki-link-verifier",
"wiki-research-lane",
"wiki-source-summarizer",
]
ANTIGRAV_INCLUDE_SECTIONS = [
"user_information", "mcp_servers", "skills",
"subagent_reminder", "messaging", "artifacts", "user_rules",
]
_ANTIGRAV_BASELINE = ["send_message", "view_file", "find_by_name", "grep_search", "list_dir"]
_ANTIGRAV_EXTRA_ORDER = [
("Write", ["write_to_file"]),
("Edit", ["replace_file_content", "multi_replace_file_content"]),
("Bash", ["run_command"]),
("WebFetch", ["read_url_content"]),
("WebSearch", ["search_web"]),
]
def parse_frontmatter(md: str) -> tuple[dict[str, str], str]:
lines = md.splitlines(keepends=True)
if not lines or lines[0].strip() != "---":
raise ValueError("missing frontmatter open")
fm: dict[str, str] = {}
i = 1
while i < len(lines) and lines[i].strip() != "---":
line = lines[i].rstrip("\n")
if ":" in line:
key, _, val = line.partition(":")
fm[key.strip()] = val.strip()
i += 1
if i >= len(lines):
raise ValueError("missing frontmatter close")
body = "".join(lines[i + 1:]).lstrip("\n")
return fm, body
def parse_tools(tools_csv: str) -> list[str]:
return [t.strip() for t in tools_csv.split(",") if t.strip()]
def codex_sandbox_mode(tools: list[str]) -> str:
return "workspace-write" if ({"Edit", "Write"} & set(tools)) else "read-only"
def antigravity_tool_names(tools: list[str]) -> list[str]:
names = list(_ANTIGRAV_BASELINE)
tset = set(tools)
for claude_tool, mapped in _ANTIGRAV_EXTRA_ORDER:
if claude_tool in tset:
names.extend(mapped)
return names
```
- [ ] **Step 4: Run test to verify it passes**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -v`
Expected: PASS (6 tests).
- [ ] **Step 5: Commit**
```bash
git add scripts/sync_automation.py scripts/test_sync_automation.py
git commit -m "feat(sync): frontmatter parse + permission derivation pure functions"
```
---
## Task 2: Codex TOML 직렬화
**Files:**
- Modify: `scripts/sync_automation.py`
- Test: `scripts/test_sync_automation.py`
- [ ] **Step 1: Write the failing test**
```python
def test_codex_toml_is_valid_and_roundtrips():
body = "You are the Wiki Link Verifier.\nLine two with `backticks` and 'quotes'.\n"
out = s.render_codex_toml(
name="wiki-link-verifier",
description='Audit "the wiki" for orphans.',
sandbox_mode="read-only",
body=body,
)
parsed = tomllib.loads(out)
assert parsed["name"] == "wiki-link-verifier"
assert parsed["description"] == 'Audit "the wiki" for orphans.'
assert parsed["sandbox_mode"] == "read-only"
assert parsed["developer_instructions"].strip() == body.strip()
def test_codex_toml_rejects_triple_single_quote_body():
import pytest
with pytest.raises(ValueError):
s.render_codex_toml("n", "d", "read-only", "bad ''' body")
```
- [ ] **Step 2: Run test to verify it fails**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -k codex_toml -v`
Expected: FAIL with `AttributeError: module 'sync_automation' has no attribute 'render_codex_toml'`.
- [ ] **Step 3: Write minimal implementation**
```python
def _toml_basic_string(value: str) -> str:
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
return f'"{escaped}"'
def render_codex_toml(name: str, description: str, sandbox_mode: str, body: str) -> str:
if "'''" in body:
raise ValueError("body contains ''' which breaks TOML literal multiline string")
return (
f"name = {_toml_basic_string(name)}\n"
f"description = {_toml_basic_string(description)}\n"
f"sandbox_mode = {_toml_basic_string(sandbox_mode)}\n"
f"developer_instructions = '''\n"
f"{body.rstrip()}\n"
f"'''\n"
)
```
- [ ] **Step 4: Run test to verify it passes**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -k codex_toml -v`
Expected: PASS (2 tests).
- [ ] **Step 5: Commit**
```bash
git add scripts/sync_automation.py scripts/test_sync_automation.py
git commit -m "feat(sync): codex TOML serialization with literal multiline body"
```
---
## Task 3: Antigravity agent.json 직렬화
**Files:**
- Modify: `scripts/sync_automation.py`
- Test: `scripts/test_sync_automation.py`
- [ ] **Step 1: Write the failing test**
```python
def test_antigravity_agent_json_schema():
obj = s.build_antigravity_agent(
name="wiki-link-verifier",
description="Audit the wiki.",
body="You are the Wiki Link Verifier.\n",
tools=["Read", "Grep", "Glob", "Bash"],
)
assert obj["name"] == "wiki-link-verifier"
assert obj["description"] == "Audit the wiki."
assert obj["hidden"] is True
ca = obj["config"]["customAgent"]
assert ca["systemPromptSections"][0]["title"] == "Agent System Instructions"
assert ca["systemPromptSections"][0]["content"] == "You are the Wiki Link Verifier.\n"
assert "write_to_file" not in ca["toolNames"] # read-only agent
assert ca["toolNames"][0] == "send_message"
assert ca["systemPromptConfig"]["includeSections"] == s.ANTIGRAV_INCLUDE_SECTIONS
def test_antigravity_json_render_is_valid_json():
obj = s.build_antigravity_agent("n", "d", "body\n", ["Read", "Grep", "Glob"])
text = s.render_json(obj)
assert json.loads(text) == obj
assert text.endswith("\n")
```
- [ ] **Step 2: Run test to verify it fails**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -k antigravity_agent_json -v`
Expected: FAIL with `AttributeError: ... 'build_antigravity_agent'`.
- [ ] **Step 3: Write minimal implementation**
```python
def build_antigravity_agent(name: str, description: str, body: str, tools: list[str]) -> dict:
return {
"name": name,
"description": description,
"hidden": True,
"config": {
"customAgent": {
"systemPromptSections": [
{"title": "Agent System Instructions", "content": body}
],
"toolNames": antigravity_tool_names(tools),
"systemPromptConfig": {"includeSections": list(ANTIGRAV_INCLUDE_SECTIONS)},
}
},
}
def render_json(obj: dict) -> str:
return json.dumps(obj, indent=2, ensure_ascii=False) + "\n"
```
- [ ] **Step 4: Run test to verify it passes**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -k antigravity_agent_json -v`
Expected: PASS (2 tests).
- [ ] **Step 5: Commit**
```bash
git add scripts/sync_automation.py scripts/test_sync_automation.py
git commit -m "feat(sync): antigravity agent.json builder + stable JSON render"
```
---
## Task 4: CLI 배선 (generate / check) + 기존 7개 검증
**Files:**
- Modify: `scripts/sync_automation.py`
- Test: `scripts/test_sync_automation.py`
- [ ] **Step 1: Write the failing test**
```python
def test_load_agent_inputs_for_existing_agent():
inp = s.load_agent_inputs("wiki-link-verifier")
assert inp.tools == ["Read", "Grep", "Glob", "Bash"]
assert inp.codex_description
assert inp.antigrav_description
assert "Wiki Link Verifier" in inp.codex_body
assert inp.antigrav_body
def test_generate_one_writes_both_outputs(tmp_path, monkeypatch):
monkeypatch.setattr(s, "CODEX_AGENTS", tmp_path / "codex")
monkeypatch.setattr(s, "ANTIGRAV_OUT", tmp_path / "antigrav")
(tmp_path / "codex").mkdir()
s.generate_one("wiki-link-verifier", check=False)
toml_path = tmp_path / "codex" / "wiki-link-verifier.toml"
json_path = tmp_path / "antigrav" / "wiki-link-verifier" / "agent.json"
assert toml_path.exists()
assert json_path.exists()
assert tomllib.loads(toml_path.read_text())["sandbox_mode"] == "read-only"
assert json.loads(json_path.read_text())["hidden"] is True
```
- [ ] **Step 2: Run test to verify it fails**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -k "load_agent_inputs or generate_one" -v`
Expected: FAIL with `AttributeError: ... 'load_agent_inputs'`.
- [ ] **Step 3: Write minimal implementation**
```python
from dataclasses import dataclass
@dataclass
class AgentInputs:
name: str
tools: list[str]
codex_description: str
codex_body: str
antigrav_description: str
antigrav_body: str
def load_agent_inputs(name: str) -> AgentInputs:
claude_fm, _ = parse_frontmatter((CLAUDE_AGENTS / f"{name}.md").read_text())
codex_fm, codex_body = parse_frontmatter((CODEX_AGENTS / f"{name}.md").read_text())
ag_fm, ag_body = parse_frontmatter((ANTIGRAV_SSOT / f"{name}.md").read_text())
return AgentInputs(
name=name,
tools=parse_tools(claude_fm["tools"]),
codex_description=codex_fm["description"],
codex_body=codex_body,
antigrav_description=ag_fm["description"],
antigrav_body=ag_body,
)
def _write_or_check(path: Path, content: str, check: bool, drift: list[str]) -> None:
if check:
current = path.read_text() if path.exists() else None
if current != content:
drift.append(str(path.relative_to(REPO)))
else:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content)
def generate_one(name: str, check: bool, drift: list[str] | None = None) -> None:
drift = drift if drift is not None else []
inp = load_agent_inputs(name)
toml_text = render_codex_toml(
inp.name, inp.codex_description, codex_sandbox_mode(inp.tools), inp.codex_body
)
json_text = render_json(
build_antigravity_agent(inp.name, inp.antigrav_description, inp.antigrav_body, inp.tools)
)
_write_or_check(CODEX_AGENTS / f"{name}.toml", toml_text, check, drift)
_write_or_check(ANTIGRAV_OUT / name / "agent.json", json_text, check, drift)
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description="Sync Claude agents -> Codex/Antigravity native files")
ap.add_argument("target", choices=["agents"], help="what to sync")
ap.add_argument("--check", action="store_true", help="fail (exit 2) if outputs drift from SSOT")
ap.add_argument("--only", help="single agent name (default: all)")
args = ap.parse_args(argv)
names = [args.only] if args.only else AGENT_NAMES
drift: list[str] = []
for name in names:
generate_one(name, check=args.check, drift=drift)
if args.check and drift:
print("DRIFT detected in:\n " + "\n ".join(drift), file=sys.stderr)
return 2
action = "checked" if args.check else "generated"
print(f"{action} {len(names)} agents (codex .toml + antigravity agent.json)")
return 0
if __name__ == "__main__":
raise SystemExit(main())
```
- [ ] **Step 4: Run unit tests (still only 7 agents exist, so use --only)**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -v`
Expected: PASS (all tests). `load_agent_inputs`/`generate_one` use `wiki-link-verifier` which exists.
- [ ] **Step 5: Smoke-run on one existing agent**
Run:
```bash
cd /home/donghyeon/dev/llm-wiki-private
python3 scripts/sync_automation.py agents --only wiki-link-verifier
python3 -c "import tomllib,pathlib; print(tomllib.loads(pathlib.Path('.codex/agents/wiki-link-verifier.toml').read_text())['sandbox_mode'])"
python3 -c "import json,pathlib; o=json.loads(pathlib.Path('.agents/agents/wiki-link-verifier/agent.json').read_text()); print(o['hidden'], 'write_to_file' in o['config']['customAgent']['toolNames'])"
```
Expected output:
```
generated 1 agents (codex .toml + antigravity agent.json)
read-only
True False
```
(`True False` = hidden true + write_to_file absent for read-only agent — confirms the stale-disk bug is fixed.)
- [ ] **Step 6: Commit**
```bash
git add scripts/sync_automation.py scripts/test_sync_automation.py .codex/agents/wiki-link-verifier.toml .agents/agents/wiki-link-verifier/agent.json
git commit -m "feat(sync): CLI generate/check + AgentInputs loader; verify on link-verifier"
```
---
## Task 5: 누락 agent 2개의 codex SSOT 본문 작성 (`branch-depth-auditor`, `coverage-auditor`)
> codex 본문 = Claude 본문의 일반화: frontmatter에서 `tools:`/`model:` 제거(name+description만 유지), 본문의 `Read tool`/`Edit tool`/`Write tool`/`Bash tool` → `Read`/`Edit`/`Write`/`shell`, `CLAUDE.md` 단독 언급 → `CLAUDE.md (또는 AGENTS.md)`.
**Files:**
- Create: `.codex/agents/branch-depth-auditor.md`
- Create: `.codex/agents/coverage-auditor.md`
- [ ] **Step 1: Learn the generalization convention**
Read these two pairs side by side to learn the exact convention:
- `.claude/agents/wiki-link-verifier.md` vs `.codex/agents/wiki-link-verifier.md`
- Then Read `.claude/agents/branch-depth-auditor.md` and `.claude/agents/coverage-auditor.md` in full.
- [ ] **Step 2: Create `.codex/agents/branch-depth-auditor.md`**
Structure:
```markdown
---
name: branch-depth-auditor
description: <verbatim copy of the description line from .claude/agents/branch-depth-auditor.md>
---
<Claude body copied verbatim, then these substitutions applied:
"Read tool"->"Read", "Grep tool"->"Grep", "Glob tool"->"Glob", "Bash tool"->"shell",
"Edit tool"->"Edit", "Write tool"->"Write";
standalone "CLAUDE.md" reference -> "CLAUDE.md (또는 AGENTS.md)".
Leave all rules/ and templates/ references, axes, and depth-ladder logic byte-identical.>
```
- [ ] **Step 3: Create `.codex/agents/coverage-auditor.md`** identically (verbatim description + generalized body).
- [ ] **Step 4: Verify frontmatter parses for both**
Run:
```bash
cd /home/donghyeon/dev/llm-wiki-private/scripts && python3 -c "
import sync_automation as s
for n in ['branch-depth-auditor','coverage-auditor']:
fm,body=s.parse_frontmatter(open(f'../.codex/agents/{n}.md').read())
assert fm['name']==n and fm['description'] and body.strip(), n
assert 'tool' not in fm and 'model' not in fm, 'frontmatter must be name+description only'
print('ok', n)
"
```
Expected:
```
ok branch-depth-auditor
ok coverage-auditor
```
- [ ] **Step 5: Commit**
```bash
git add .codex/agents/branch-depth-auditor.md .codex/agents/coverage-auditor.md
git commit -m "feat(codex): add branch-depth-auditor + coverage-auditor SSOT bodies"
```
---
## Task 6: 누락 agent 2개의 antigravity gated SSOT 본문 작성
> antigravity 본문 = codex 본문 + G1~G4 Gemini hard gate. 기존 read-only gated agent를 템플릿으로 사용한다. tool 참조는 antigravity 어휘(`view_file`/`grep_search`/`run_command`)로 쓴다.
**Files:**
- Create: `.agents/plugins/wiki-superpowers/agents/branch-depth-auditor.md`
- Create: `.agents/plugins/wiki-superpowers/agents/coverage-auditor.md`
- [ ] **Step 1: Study the gate template**
Read `.agents/plugins/wiki-superpowers/agents/wiki-link-verifier.md` in full (read-only agent — closest analog). Extract the exact section structure of: G1 Pre-Read Proof, G2 Post-Write Validator, G3 Output Schema + V Counter, G4 Enumerated STOP Conditions. Also read `.agents/plugins/wiki-superpowers/agents/wiki-research-lane.md` if a longer analytical example helps.
- [ ] **Step 2: Create `.agents/plugins/wiki-superpowers/agents/branch-depth-auditor.md`**
```markdown
---
name: branch-depth-auditor
description: <verbatim copy from .claude/agents/branch-depth-auditor.md frontmatter>
---
<body = the codex branch-depth-auditor body content (audit axes + L0~L3 depth ladder
+ Ready/Not-ready verdict logic), PLUS the four gates:
- G1 Pre-Read Proof: table quoting the first line of each Mandatory First Read
(CLAUDE.md/AGENTS.md, rules/branch-depth-gate.md, the target branch note).
- G2 Post-Write Validator: read-only -> state "read-only agent, no file writes;
verification is the grep/sed verbatim-quote proofs inside the gap report (G3 V counter)".
- G3 Output Schema + V Counter: {{ }} placeholder schema for the Ready/Not-ready
verdict + per-axis gap rows; V = number of quote-grep commands actually run.
- G4 Enumerated STOP Conditions: numbered list -> return NEEDS_CONTEXT
(1: branch-note path missing/unreadable; 2: linked raw sources unreadable;
3: wiki_structure_lint not yet passed; 4: target is not a feature-*.md branch note).
Use Antigravity tool names in any tool reference: view_file, grep_search, run_command.>
```
Mirror the axes/ladder language from `.claude/agents/branch-depth-auditor.md`; do NOT invent new audit criteria.
- [ ] **Step 3: Create `.agents/plugins/wiki-superpowers/agents/coverage-auditor.md`**
Same structure. Coverage = completeness (covered-here / delegated / missing per required concern) with a 3-tier verdict and optional project mode. G4 STOP conditions: governing_docs missing, `## Coverage` section absent, links unresolved, target not a branch note. tool refs use `run_command` for grep-based concern classification + `view_file` for governing docs.
- [ ] **Step 4: Verify frontmatter parses + all four gates present**
Run:
```bash
cd /home/donghyeon/dev/llm-wiki-private
for n in branch-depth-auditor coverage-auditor; do
f=.agents/plugins/wiki-superpowers/agents/$n.md
echo "== $n =="
grep -c "G1 Pre-Read Proof" "$f"
grep -c "G4" "$f"
done
```
Expected: each `grep -c` prints `1` or more (gates present). If `0`, the gate section is missing — add it.
- [ ] **Step 5: Commit**
```bash
git add .agents/plugins/wiki-superpowers/agents/branch-depth-auditor.md .agents/plugins/wiki-superpowers/agents/coverage-auditor.md
git commit -m "feat(antigravity): add branch-depth-auditor + coverage-auditor gated SSOT bodies"
```
---
## Task 7: 전체 9개 생성 + 스키마 검증 + drift 0 확인
**Files:**
- Generate: `.codex/agents/*.toml` ×9, `.agents/agents/<name>/agent.json` ×9
- Test: `scripts/test_sync_automation.py` (add a full-suite generation guard)
- [ ] **Step 1: Add a test asserting all 9 generate and pass schema checks**
```python
def test_all_nine_agents_generate_valid_artifacts(tmp_path, monkeypatch):
monkeypatch.setattr(s, "CODEX_AGENTS", tmp_path / "codex")
monkeypatch.setattr(s, "ANTIGRAV_OUT", tmp_path / "antigrav")
# copy SSOT bodies the generator reads from real repo (CODEX_AGENTS is also the source dir),
# so point the source dir explicitly: regenerate using real source via a fresh load.
(tmp_path / "codex").mkdir()
# NOTE: generate_one reads codex body from s.CODEX_AGENTS; for this test we only verify
# the antigravity side (json) which reads from ANTIGRAV_SSOT (unchanged real dir).
for name in s.AGENT_NAMES:
obj = s.build_antigravity_agent(
name, "d",
s.parse_frontmatter((s.ANTIGRAV_SSOT / f"{name}.md").read_text())[1],
s.parse_tools(s.parse_frontmatter((s.CLAUDE_AGENTS / f"{name}.md").read_text())[0]["tools"]),
)
assert json.loads(s.render_json(obj))["name"] == name
# read-only agents must NOT carry write tools
ro = {"branch-depth-auditor", "coverage-auditor", "wiki-adversarial-reviewer",
"wiki-diagram-reviewer", "wiki-link-verifier", "wiki-research-lane"}
if name in ro:
assert "write_to_file" not in obj["config"]["customAgent"]["toolNames"], name
```
> 위 테스트는 `CODEX_AGENTS`/`ANTIGRAV_SSOT`의 9개 `.md`가 모두 존재해야 통과한다(Task 5·6 완료 후). codex toml 본문은 실 디렉토리에서 직접 읽으므로 통합 스모크(Step 3)로 검증한다.
- [ ] **Step 2: Run the unit suite**
Run: `cd scripts && python3 -m pytest test_sync_automation.py -v`
Expected: PASS (all, including the new 9-agent guard).
- [ ] **Step 3: Generate all 9 and validate every artifact**
Run:
```bash
cd /home/donghyeon/dev/llm-wiki-private
python3 scripts/sync_automation.py agents
echo "--- validate codex toml (9) ---"
python3 -c "
import tomllib, pathlib
ns=['branch-depth-auditor','coverage-auditor','wiki-adversarial-reviewer','wiki-decision-researcher','wiki-diagram-reviewer','wiki-doc-author','wiki-link-verifier','wiki-research-lane','wiki-source-summarizer']
for n in ns:
o=tomllib.loads(pathlib.Path(f'.codex/agents/{n}.toml').read_text())
assert o['name']==n and o['sandbox_mode'] in ('read-only','workspace-write') and o['developer_instructions'].strip()
print('codex toml ok:', len(ns))
"
echo "--- validate antigravity json (9) ---"
python3 -c "
import json, pathlib
ns=['branch-depth-auditor','coverage-auditor','wiki-adversarial-reviewer','wiki-decision-researcher','wiki-diagram-reviewer','wiki-doc-author','wiki-link-verifier','wiki-research-lane','wiki-source-summarizer']
for n in ns:
o=json.loads(pathlib.Path(f'.agents/agents/{n}/agent.json').read_text())
ca=o['config']['customAgent']
assert o['name']==n and o['hidden'] is True
assert ca['systemPromptSections'][0]['content'].strip()
assert ca['toolNames'][0]=='send_message'
print('antigravity json ok:', len(ns))
"
```
Expected:
```
generated 9 agents (codex .toml + antigravity agent.json)
--- validate codex toml (9) ---
codex toml ok: 9
--- validate antigravity json (9) ---
antigravity json ok: 9
```
- [ ] **Step 4: Confirm idempotency / drift-0**
Run:
```bash
cd /home/donghyeon/dev/llm-wiki-private
python3 scripts/sync_automation.py agents --check && echo "DRIFT-FREE"
```
Expected:
```
checked 9 agents (codex .toml + antigravity agent.json)
DRIFT-FREE
```
(exit 0). If it prints `DRIFT detected`, re-run without `--check` and re-commit the generated files.
- [ ] **Step 5: Commit generated artifacts**
```bash
cd /home/donghyeon/dev/llm-wiki-private
git add scripts/test_sync_automation.py .codex/agents/*.toml .agents/agents/
git commit -m "feat(sync): generate all 9 agents for codex (.toml) + antigravity (agent.json)"
```
---
## Task 8: 생성 스크립트 사용법 문서 1단락 (codex + antigravity README)
> 전체 문서 정리(CLAUDE.md의 구식 '수동 cat' 서술 제거 등)는 Phase 2. 여기서는 Phase 0가 만든 스크립트/산출물이 고아가 되지 않도록 최소 포인터만 추가한다.
**Files:**
- Modify: `.codex/agents/README.md`
- Modify: `.agents/plugins/wiki-superpowers/README.md`
- [ ] **Step 1: Add a "Native generation" note to `.codex/agents/README.md`**
기존 README 상단(또는 "Pattern" 섹션 위)에 다음 취지의 1단락 추가 (정확한 문구는 기존 톤에 맞춰 작성):
- codex는 이제 native subagent를 `.codex/agents/*.toml`로 자동 등록한다(`developer_instructions`+`sandbox_mode`). `.md`는 사람이 편집하는 SSOT이고 `.toml``python3 scripts/sync_automation.py agents`로 생성된다.
- `.md` 편집 후 반드시 `sync_automation.py agents`를 다시 돌려야 `.toml`에 반영된다. CI/hook에서는 `--check`로 drift를 검사한다.
- [ ] **Step 2: Add the same note to `.agents/plugins/wiki-superpowers/README.md`**
기존 "⚠️ Loading model" 박스의 "Sync command" 줄을 실제 스크립트로 교체:
- 구: `python3 .agents/scripts/convert-wiki-agents.py` (존재하지 않음)
- 신: `python3 scripts/sync_automation.py agents``.agents/plugins/.../agents/*.md`(gated SSOT) + `.claude/agents/*.md`(권한 SSOT) → `.agents/agents/<name>/agent.json` 생성. `--check`로 drift 검사.
- [ ] **Step 3: Verify the dead reference is gone**
Run:
```bash
cd /home/donghyeon/dev/llm-wiki-private
grep -rn "convert-wiki-agents.py" .agents/ .codex/ CLAUDE.md || echo "no dead reference remaining in README scope"
```
Expected: `no dead reference remaining in README scope` (CLAUDE.md 본문의 언급은 Phase 2에서 처리하므로, 여기서 grep이 CLAUDE.md만 남기면 그 줄은 Phase 2 TODO로 남겨도 됨 — 단 README 2개에는 남지 않아야 함).
- [ ] **Step 4: Commit**
```bash
git add .codex/agents/README.md .agents/plugins/wiki-superpowers/README.md
git commit -m "docs(sync): point READMEs at scripts/sync_automation.py (replaces missing convert script)"
```
---
## Phase 0 완료 기준 (Definition of Done)
- `scripts/sync_automation.py` + 테스트 통과(pytest green).
- `.codex/agents/*.toml` 9개 + `.agents/agents/<name>/agent.json` 9개 생성·검증.
- `sync_automation.py agents --check` 가 exit 0 (drift 0).
- 누락 2개 agent(branch-depth-auditor·coverage-auditor)가 codex·antigravity 양쪽 SSOT + 생성물에 존재.
- README 2개가 실제 생성 스크립트를 가리킴.
- 검증된 부채 수정: read-only agent의 `agent.json`에 write 도구 없음(stale 디스크 버그 해소).
이후: **Phase 1**(commands → codex `.agents/skills/` + antigravity `.agents/workflows/`)는 별도 plan으로 작성한다. Phase 0의 `sync_automation.py``commands` 타깃을 확장한다.
---
## Self-Review (작성자 체크)
- **Spec coverage**: 설계 §4 Phase 0(엔진+agents)의 모든 항목 — 생성 엔진, 9개 agent 생성, 누락 2개 추가, antigravity 로딩 경로 복구(agent.json+스크립트), codex toml 현대화 — 각각 Task 1~8에 매핑됨. Phase 1·2는 범위 밖(별도 plan).
- **Placeholder scan**: 코드 스텝은 실제 코드 포함. Task 5·6의 본문 작성은 "기존 파일을 템플릿으로 verbatim 적응"이라는 결정론적 절차 + 검증 grep을 제공(프로즈 자체는 SSOT 적응이라 코드처럼 박제 불가하나, 입력 파일·치환 규칙·검증 명령을 명시).
- **Type consistency**: 함수 시그니처 일관 — `parse_frontmatter``(fm,body)`, `antigravity_tool_names(tools)`, `render_codex_toml(name,description,sandbox_mode,body)`, `build_antigravity_agent(name,description,body,tools)`, `generate_one(name,check,drift)`. 모든 Task에서 동일 이름 사용. 모듈명 `sync_automation`(언더스코어)로 통일.
- **알려진 한계**: Task 7 Step 1 테스트는 codex toml 본문을 실디렉토리에서 읽어 통합 스모크(Step 3)로 보완. Task 6 Step 4의 grep 카운트는 게이트 "존재"만 보장하고 의미적 정확성은 보장하지 않음 → 실제 antigravity 실행 스모크는 Phase 1 검증과 함께 수행 권장.