From 146fcafc5e948a8e86d44a84f4c62b859ea61ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=20=E9=A7=BF=E7=94=AB=20=28Shunsuke=20Hayashi=29?= Date: Fri, 10 Apr 2026 01:07:25 +0900 Subject: [PATCH] =?UTF-8?q?[=E8=BF=BD=E5=8A=A0]=20DTP=20(Deterministic=20T?= =?UTF-8?q?ask=20Protocol)=20=E8=A8=AD=E8=A8=88=E6=96=87=E6=9B=B8=E3=83=BB?= =?UTF-8?q?=E6=8C=87=E7=A4=BA=E6=9B=B8=E3=82=92=E7=A7=BB=E6=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deterministic-task-protocol リポから miyabi-cli-standalone に統合: - docs/dtp/: PLAYBOOK, PLAN, UML, GIT-RULES, Codex レビュー 3件 - autorun/: Phase 0-8 の TASKS/ASSIGNMENT/GATE + INDEX/HANDOFF/ROLLBACK - project_memory/tasks.json: 全9 Phase の DAG SSOT - skills/: polaris-ops, rust-llm-pitfalls - .codex/instructions.md: Codex 設定 実装は miyabi-core に gate.rs, lock.rs, protocol.rs, store.rs を追加する方針。 既存の dag.rs, github.rs, approval.rs 等は変更不要。 Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/skills/gitnexus/gitnexus-cli/SKILL.md | 82 ++ .../gitnexus/gitnexus-debugging/SKILL.md | 89 ++ .../gitnexus/gitnexus-exploring/SKILL.md | 78 ++ .../skills/gitnexus/gitnexus-guide/SKILL.md | 64 ++ .../gitnexus-impact-analysis/SKILL.md | 97 ++ .../gitnexus/gitnexus-refactoring/SKILL.md | 121 +++ .codex/instructions.md | 20 + .gitignore | 1 + AGENTS.md | 102 ++ CLAUDE.md | 102 ++ autorun/HANDOFF.md | 299 ++++++ autorun/INDEX.md | 111 +++ autorun/ROLLBACK.md | 148 +++ autorun/phase-0-preconditions/ASSIGNMENT.md | 7 + autorun/phase-0-preconditions/GATE.md | 12 + autorun/phase-0-preconditions/TASKS.md | 23 + autorun/phase-1-types/ASSIGNMENT.md | 7 + autorun/phase-1-types/GATE.md | 12 + autorun/phase-1-types/TASKS.md | 34 + autorun/phase-2-state-machine/ASSIGNMENT.md | 7 + autorun/phase-2-state-machine/GATE.md | 11 + autorun/phase-2-state-machine/TASKS.md | 37 + autorun/phase-3-event-store/ASSIGNMENT.md | 7 + autorun/phase-3-event-store/GATE.md | 13 + autorun/phase-3-event-store/TASKS.md | 39 + autorun/phase-4-file-lock/ASSIGNMENT.md | 7 + autorun/phase-4-file-lock/GATE.md | 12 + autorun/phase-4-file-lock/TASKS.md | 41 + autorun/phase-5-github-sync/ASSIGNMENT.md | 7 + autorun/phase-5-github-sync/GATE.md | 13 + autorun/phase-5-github-sync/TASKS.md | 44 + autorun/phase-6-protocol/ASSIGNMENT.md | 7 + autorun/phase-6-protocol/GATE.md | 14 + autorun/phase-6-protocol/TASKS.md | 60 ++ autorun/phase-7-cli/ASSIGNMENT.md | 7 + autorun/phase-7-cli/GATE.md | 12 + autorun/phase-7-cli/TASKS.md | 41 + autorun/phase-8-e2e/ASSIGNMENT.md | 7 + autorun/phase-8-e2e/GATE.md | 12 + autorun/phase-8-e2e/TASKS.md | 40 + docs/dtp/GIT-RULES.md | 170 ++++ docs/dtp/PLAN-v1.md | 414 ++++++++ docs/dtp/PLAYBOOK.md | 921 ++++++++++++++++++ docs/dtp/UML.md | 427 ++++++++ .../reviews/codex-review-dag-coordination.md | 194 ++++ docs/dtp/reviews/codex-review-github-sync.md | 139 +++ .../dtp/reviews/codex-review-state-machine.md | 310 ++++++ project_memory/tasks.json | 160 +++ skills/polaris-ops/SKILL.md | 103 ++ skills/rust-llm-pitfalls/SKILL.md | 210 ++++ 50 files changed, 4895 insertions(+) create mode 100644 .claude/skills/gitnexus/gitnexus-cli/SKILL.md create mode 100644 .claude/skills/gitnexus/gitnexus-debugging/SKILL.md create mode 100644 .claude/skills/gitnexus/gitnexus-exploring/SKILL.md create mode 100644 .claude/skills/gitnexus/gitnexus-guide/SKILL.md create mode 100644 .claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md create mode 100644 .claude/skills/gitnexus/gitnexus-refactoring/SKILL.md create mode 100644 .codex/instructions.md create mode 100644 autorun/HANDOFF.md create mode 100644 autorun/INDEX.md create mode 100644 autorun/ROLLBACK.md create mode 100644 autorun/phase-0-preconditions/ASSIGNMENT.md create mode 100644 autorun/phase-0-preconditions/GATE.md create mode 100644 autorun/phase-0-preconditions/TASKS.md create mode 100644 autorun/phase-1-types/ASSIGNMENT.md create mode 100644 autorun/phase-1-types/GATE.md create mode 100644 autorun/phase-1-types/TASKS.md create mode 100644 autorun/phase-2-state-machine/ASSIGNMENT.md create mode 100644 autorun/phase-2-state-machine/GATE.md create mode 100644 autorun/phase-2-state-machine/TASKS.md create mode 100644 autorun/phase-3-event-store/ASSIGNMENT.md create mode 100644 autorun/phase-3-event-store/GATE.md create mode 100644 autorun/phase-3-event-store/TASKS.md create mode 100644 autorun/phase-4-file-lock/ASSIGNMENT.md create mode 100644 autorun/phase-4-file-lock/GATE.md create mode 100644 autorun/phase-4-file-lock/TASKS.md create mode 100644 autorun/phase-5-github-sync/ASSIGNMENT.md create mode 100644 autorun/phase-5-github-sync/GATE.md create mode 100644 autorun/phase-5-github-sync/TASKS.md create mode 100644 autorun/phase-6-protocol/ASSIGNMENT.md create mode 100644 autorun/phase-6-protocol/GATE.md create mode 100644 autorun/phase-6-protocol/TASKS.md create mode 100644 autorun/phase-7-cli/ASSIGNMENT.md create mode 100644 autorun/phase-7-cli/GATE.md create mode 100644 autorun/phase-7-cli/TASKS.md create mode 100644 autorun/phase-8-e2e/ASSIGNMENT.md create mode 100644 autorun/phase-8-e2e/GATE.md create mode 100644 autorun/phase-8-e2e/TASKS.md create mode 100644 docs/dtp/GIT-RULES.md create mode 100644 docs/dtp/PLAN-v1.md create mode 100644 docs/dtp/PLAYBOOK.md create mode 100644 docs/dtp/UML.md create mode 100644 docs/dtp/reviews/codex-review-dag-coordination.md create mode 100644 docs/dtp/reviews/codex-review-github-sync.md create mode 100644 docs/dtp/reviews/codex-review-state-machine.md create mode 100644 project_memory/tasks.json create mode 100644 skills/polaris-ops/SKILL.md create mode 100644 skills/rust-llm-pitfalls/SKILL.md diff --git a/.claude/skills/gitnexus/gitnexus-cli/SKILL.md b/.claude/skills/gitnexus/gitnexus-cli/SKILL.md new file mode 100644 index 0000000..c9e0af3 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-cli/SKILL.md @@ -0,0 +1,82 @@ +--- +name: gitnexus-cli +description: "Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: \"Index this repo\", \"Reanalyze the codebase\", \"Generate a wiki\"" +--- + +# GitNexus CLI Commands + +All commands work via `npx` — no global install required. + +## Commands + +### analyze — Build or refresh the index + +```bash +npx gitnexus analyze +``` + +Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates CLAUDE.md / AGENTS.md context files. + +| Flag | Effect | +| -------------- | ---------------------------------------------------------------- | +| `--force` | Force full re-index even if up to date | +| `--embeddings` | Enable embedding generation for semantic search (off by default) | + +**When to run:** First time in a project, after major code changes, or when `gitnexus://repo/{name}/context` reports the index is stale. In Claude Code, a PostToolUse hook runs `analyze` automatically after `git commit` and `git merge`, preserving embeddings if previously generated. + +### status — Check index freshness + +```bash +npx gitnexus status +``` + +Shows whether the current repo has a GitNexus index, when it was last updated, and symbol/relationship counts. Use this to check if re-indexing is needed. + +### clean — Delete the index + +```bash +npx gitnexus clean +``` + +Deletes the `.gitnexus/` directory and unregisters the repo from the global registry. Use before re-indexing if the index is corrupt or after removing GitNexus from a project. + +| Flag | Effect | +| --------- | ------------------------------------------------- | +| `--force` | Skip confirmation prompt | +| `--all` | Clean all indexed repos, not just the current one | + +### wiki — Generate documentation from the graph + +```bash +npx gitnexus wiki +``` + +Generates repository documentation from the knowledge graph using an LLM. Requires an API key (saved to `~/.gitnexus/config.json` on first use). + +| Flag | Effect | +| ------------------- | ----------------------------------------- | +| `--force` | Force full regeneration | +| `--model ` | LLM model (default: minimax/minimax-m2.5) | +| `--base-url ` | LLM API base URL | +| `--api-key ` | LLM API key | +| `--concurrency ` | Parallel LLM calls (default: 3) | +| `--gist` | Publish wiki as a public GitHub Gist | + +### list — Show all indexed repos + +```bash +npx gitnexus list +``` + +Lists all repositories registered in `~/.gitnexus/registry.json`. The MCP `list_repos` tool provides the same information. + +## After Indexing + +1. **Read `gitnexus://repo/{name}/context`** to verify the index loaded +2. Use the other GitNexus skills (`exploring`, `debugging`, `impact-analysis`, `refactoring`) for your task + +## Troubleshooting + +- **"Not inside a git repository"**: Run from a directory inside a git repo +- **Index is stale after re-analyzing**: Restart Claude Code to reload the MCP server +- **Embeddings slow**: Omit `--embeddings` (it's off by default) or set `OPENAI_API_KEY` for faster API-based embedding diff --git a/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md b/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md new file mode 100644 index 0000000..9510b97 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md @@ -0,0 +1,89 @@ +--- +name: gitnexus-debugging +description: "Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: \"Why is X failing?\", \"Where does this error come from?\", \"Trace this bug\"" +--- + +# Debugging with GitNexus + +## When to Use + +- "Why is this function failing?" +- "Trace where this error comes from" +- "Who calls this method?" +- "This endpoint returns 500" +- Investigating bugs, errors, or unexpected behavior + +## Workflow + +``` +1. gitnexus_query({query: ""}) → Find related execution flows +2. gitnexus_context({name: ""}) → See callers/callees/processes +3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow +4. gitnexus_cypher({query: "MATCH path..."}) → Custom traces if needed +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] Understand the symptom (error message, unexpected behavior) +- [ ] gitnexus_query for error text or related code +- [ ] Identify the suspect function from returned processes +- [ ] gitnexus_context to see callers and callees +- [ ] Trace execution flow via process resource if applicable +- [ ] gitnexus_cypher for custom call chain traces if needed +- [ ] Read source files to confirm root cause +``` + +## Debugging Patterns + +| Symptom | GitNexus Approach | +| -------------------- | ---------------------------------------------------------- | +| Error message | `gitnexus_query` for error text → `context` on throw sites | +| Wrong return value | `context` on the function → trace callees for data flow | +| Intermittent failure | `context` → look for external calls, async deps | +| Performance issue | `context` → find symbols with many callers (hot paths) | +| Recent regression | `detect_changes` to see what your changes affect | + +## Tools + +**gitnexus_query** — find code related to error: + +``` +gitnexus_query({query: "payment validation error"}) +→ Processes: CheckoutFlow, ErrorHandling +→ Symbols: validatePayment, handlePaymentError, PaymentException +``` + +**gitnexus_context** — full context for a suspect: + +``` +gitnexus_context({name: "validatePayment"}) +→ Incoming calls: processCheckout, webhookHandler +→ Outgoing calls: verifyCard, fetchRates (external API!) +→ Processes: CheckoutFlow (step 3/7) +``` + +**gitnexus_cypher** — custom call chain traces: + +```cypher +MATCH path = (a)-[:CodeRelation {type: 'CALLS'}*1..2]->(b:Function {name: "validatePayment"}) +RETURN [n IN nodes(path) | n.name] AS chain +``` + +## Example: "Payment endpoint returns 500 intermittently" + +``` +1. gitnexus_query({query: "payment error handling"}) + → Processes: CheckoutFlow, ErrorHandling + → Symbols: validatePayment, handlePaymentError + +2. gitnexus_context({name: "validatePayment"}) + → Outgoing calls: verifyCard, fetchRates (external API!) + +3. READ gitnexus://repo/my-app/process/CheckoutFlow + → Step 3: validatePayment → calls fetchRates (external) + +4. Root cause: fetchRates calls external API without proper timeout +``` diff --git a/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md b/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md new file mode 100644 index 0000000..927a4e4 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md @@ -0,0 +1,78 @@ +--- +name: gitnexus-exploring +description: "Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: \"How does X work?\", \"What calls this function?\", \"Show me the auth flow\"" +--- + +# Exploring Codebases with GitNexus + +## When to Use + +- "How does authentication work?" +- "What's the project structure?" +- "Show me the main components" +- "Where is the database logic?" +- Understanding code you haven't seen before + +## Workflow + +``` +1. READ gitnexus://repos → Discover indexed repos +2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness +3. gitnexus_query({query: ""}) → Find related execution flows +4. gitnexus_context({name: ""}) → Deep dive on specific symbol +5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow +``` + +> If step 2 says "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] READ gitnexus://repo/{name}/context +- [ ] gitnexus_query for the concept you want to understand +- [ ] Review returned processes (execution flows) +- [ ] gitnexus_context on key symbols for callers/callees +- [ ] READ process resource for full execution traces +- [ ] Read source files for implementation details +``` + +## Resources + +| Resource | What you get | +| --------------------------------------- | ------------------------------------------------------- | +| `gitnexus://repo/{name}/context` | Stats, staleness warning (~150 tokens) | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores (~300 tokens) | +| `gitnexus://repo/{name}/cluster/{name}` | Area members with file paths (~500 tokens) | +| `gitnexus://repo/{name}/process/{name}` | Step-by-step execution trace (~200 tokens) | + +## Tools + +**gitnexus_query** — find execution flows related to a concept: + +``` +gitnexus_query({query: "payment processing"}) +→ Processes: CheckoutFlow, RefundFlow, WebhookHandler +→ Symbols grouped by flow with file locations +``` + +**gitnexus_context** — 360-degree view of a symbol: + +``` +gitnexus_context({name: "validateUser"}) +→ Incoming calls: loginHandler, apiMiddleware +→ Outgoing calls: checkToken, getUserById +→ Processes: LoginFlow (step 2/5), TokenRefresh (step 1/3) +``` + +## Example: "How does payment processing work?" + +``` +1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes +2. gitnexus_query({query: "payment processing"}) + → CheckoutFlow: processPayment → validateCard → chargeStripe + → RefundFlow: initiateRefund → calculateRefund → processRefund +3. gitnexus_context({name: "processPayment"}) + → Incoming: checkoutHandler, webhookHandler + → Outgoing: validateCard, chargeStripe, saveTransaction +4. Read src/payments/processor.ts for implementation details +``` diff --git a/.claude/skills/gitnexus/gitnexus-guide/SKILL.md b/.claude/skills/gitnexus/gitnexus-guide/SKILL.md new file mode 100644 index 0000000..937ac73 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-guide/SKILL.md @@ -0,0 +1,64 @@ +--- +name: gitnexus-guide +description: "Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: \"What GitNexus tools are available?\", \"How do I use GitNexus?\"" +--- + +# GitNexus Guide + +Quick reference for all GitNexus MCP tools, resources, and the knowledge graph schema. + +## Always Start Here + +For any task involving code understanding, debugging, impact analysis, or refactoring: + +1. **Read `gitnexus://repo/{name}/context`** — codebase overview + check index freshness +2. **Match your task to a skill below** and **read that skill file** +3. **Follow the skill's workflow and checklist** + +> If step 1 warns the index is stale, run `npx gitnexus analyze` in the terminal first. + +## Skills + +| Task | Skill to read | +| -------------------------------------------- | ------------------- | +| Understand architecture / "How does X work?" | `gitnexus-exploring` | +| Blast radius / "What breaks if I change X?" | `gitnexus-impact-analysis` | +| Trace bugs / "Why is X failing?" | `gitnexus-debugging` | +| Rename / extract / split / refactor | `gitnexus-refactoring` | +| Tools, resources, schema reference | `gitnexus-guide` (this file) | +| Index, status, clean, wiki CLI commands | `gitnexus-cli` | + +## Tools Reference + +| Tool | What it gives you | +| ---------------- | ------------------------------------------------------------------------ | +| `query` | Process-grouped code intelligence — execution flows related to a concept | +| `context` | 360-degree symbol view — categorized refs, processes it participates in | +| `impact` | Symbol blast radius — what breaks at depth 1/2/3 with confidence | +| `detect_changes` | Git-diff impact — what do your current changes affect | +| `rename` | Multi-file coordinated rename with confidence-tagged edits | +| `cypher` | Raw graph queries (read `gitnexus://repo/{name}/schema` first) | +| `list_repos` | Discover indexed repos | + +## Resources Reference + +Lightweight reads (~100-500 tokens) for navigation: + +| Resource | Content | +| ---------------------------------------------- | ----------------------------------------- | +| `gitnexus://repo/{name}/context` | Stats, staleness check | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores | +| `gitnexus://repo/{name}/cluster/{clusterName}` | Area members | +| `gitnexus://repo/{name}/processes` | All execution flows | +| `gitnexus://repo/{name}/process/{processName}` | Step-by-step trace | +| `gitnexus://repo/{name}/schema` | Graph schema for Cypher | + +## Graph Schema + +**Nodes:** File, Function, Class, Interface, Method, Community, Process +**Edges (via CodeRelation.type):** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS + +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"}) +RETURN caller.name, caller.filePath +``` diff --git a/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md b/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md new file mode 100644 index 0000000..e19af28 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md @@ -0,0 +1,97 @@ +--- +name: gitnexus-impact-analysis +description: "Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: \"Is it safe to change X?\", \"What depends on this?\", \"What will break?\"" +--- + +# Impact Analysis with GitNexus + +## When to Use + +- "Is it safe to change this function?" +- "What will break if I modify X?" +- "Show me the blast radius" +- "Who uses this code?" +- Before making non-trivial code changes +- Before committing — to understand what your changes affect + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → What depends on this +2. READ gitnexus://repo/{name}/processes → Check affected execution flows +3. gitnexus_detect_changes() → Map current git changes to affected flows +4. Assess risk and report to user +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklist + +``` +- [ ] gitnexus_impact({target, direction: "upstream"}) to find dependents +- [ ] Review d=1 items first (these WILL BREAK) +- [ ] Check high-confidence (>0.8) dependencies +- [ ] READ processes to check affected execution flows +- [ ] gitnexus_detect_changes() for pre-commit check +- [ ] Assess risk level and report to user +``` + +## Understanding Output + +| Depth | Risk Level | Meaning | +| ----- | ---------------- | ------------------------ | +| d=1 | **WILL BREAK** | Direct callers/importers | +| d=2 | LIKELY AFFECTED | Indirect dependencies | +| d=3 | MAY NEED TESTING | Transitive effects | + +## Risk Assessment + +| Affected | Risk | +| ------------------------------ | -------- | +| <5 symbols, few processes | LOW | +| 5-15 symbols, 2-5 processes | MEDIUM | +| >15 symbols or many processes | HIGH | +| Critical path (auth, payments) | CRITICAL | + +## Tools + +**gitnexus_impact** — the primary tool for symbol blast radius: + +``` +gitnexus_impact({ + target: "validateUser", + direction: "upstream", + minConfidence: 0.8, + maxDepth: 3 +}) + +→ d=1 (WILL BREAK): + - loginHandler (src/auth/login.ts:42) [CALLS, 100%] + - apiMiddleware (src/api/middleware.ts:15) [CALLS, 100%] + +→ d=2 (LIKELY AFFECTED): + - authRouter (src/routes/auth.ts:22) [CALLS, 95%] +``` + +**gitnexus_detect_changes** — git-diff based impact analysis: + +``` +gitnexus_detect_changes({scope: "staged"}) + +→ Changed: 5 symbols in 3 files +→ Affected: LoginFlow, TokenRefresh, APIMiddlewarePipeline +→ Risk: MEDIUM +``` + +## Example: "What breaks if I change validateUser?" + +``` +1. gitnexus_impact({target: "validateUser", direction: "upstream"}) + → d=1: loginHandler, apiMiddleware (WILL BREAK) + → d=2: authRouter, sessionManager (LIKELY AFFECTED) + +2. READ gitnexus://repo/my-app/processes + → LoginFlow and TokenRefresh touch validateUser + +3. Risk: 2 direct callers, 2 processes = MEDIUM +``` diff --git a/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md b/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md new file mode 100644 index 0000000..f48cc01 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md @@ -0,0 +1,121 @@ +--- +name: gitnexus-refactoring +description: "Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: \"Rename this function\", \"Extract this into a module\", \"Refactor this class\", \"Move this to a separate file\"" +--- + +# Refactoring with GitNexus + +## When to Use + +- "Rename this function safely" +- "Extract this into a module" +- "Split this service" +- "Move this to a new file" +- Any task involving renaming, extracting, splitting, or restructuring code + +## Workflow + +``` +1. gitnexus_impact({target: "X", direction: "upstream"}) → Map all dependents +2. gitnexus_query({query: "X"}) → Find execution flows involving X +3. gitnexus_context({name: "X"}) → See all incoming/outgoing refs +4. Plan update order: interfaces → implementations → callers → tests +``` + +> If "Index is stale" → run `npx gitnexus analyze` in terminal. + +## Checklists + +### Rename Symbol + +``` +- [ ] gitnexus_rename({symbol_name: "oldName", new_name: "newName", dry_run: true}) — preview all edits +- [ ] Review graph edits (high confidence) and ast_search edits (review carefully) +- [ ] If satisfied: gitnexus_rename({..., dry_run: false}) — apply edits +- [ ] gitnexus_detect_changes() — verify only expected files changed +- [ ] Run tests for affected processes +``` + +### Extract Module + +``` +- [ ] gitnexus_context({name: target}) — see all incoming/outgoing refs +- [ ] gitnexus_impact({target, direction: "upstream"}) — find all external callers +- [ ] Define new module interface +- [ ] Extract code, update imports +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +### Split Function/Service + +``` +- [ ] gitnexus_context({name: target}) — understand all callees +- [ ] Group callees by responsibility +- [ ] gitnexus_impact({target, direction: "upstream"}) — map callers to update +- [ ] Create new functions/services +- [ ] Update callers +- [ ] gitnexus_detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +## Tools + +**gitnexus_rename** — automated multi-file rename: + +``` +gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) +→ 12 edits across 8 files +→ 10 graph edits (high confidence), 2 ast_search edits (review) +→ Changes: [{file_path, edits: [{line, old_text, new_text, confidence}]}] +``` + +**gitnexus_impact** — map all dependents first: + +``` +gitnexus_impact({target: "validateUser", direction: "upstream"}) +→ d=1: loginHandler, apiMiddleware, testUtils +→ Affected Processes: LoginFlow, TokenRefresh +``` + +**gitnexus_detect_changes** — verify your changes after refactoring: + +``` +gitnexus_detect_changes({scope: "all"}) +→ Changed: 8 files, 12 symbols +→ Affected processes: LoginFlow, TokenRefresh +→ Risk: MEDIUM +``` + +**gitnexus_cypher** — custom reference queries: + +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "validateUser"}) +RETURN caller.name, caller.filePath ORDER BY caller.filePath +``` + +## Risk Rules + +| Risk Factor | Mitigation | +| ------------------- | ----------------------------------------- | +| Many callers (>5) | Use gitnexus_rename for automated updates | +| Cross-area refs | Use detect_changes after to verify scope | +| String/dynamic refs | gitnexus_query to find them | +| External/public API | Version and deprecate properly | + +## Example: Rename `validateUser` to `authenticateUser` + +``` +1. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) + → 12 edits: 10 graph (safe), 2 ast_search (review) + → Files: validator.ts, login.ts, middleware.ts, config.json... + +2. Review ast_search edits (config.json: dynamic reference!) + +3. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: false}) + → Applied 12 edits across 8 files + +4. gitnexus_detect_changes({scope: "all"}) + → Affected: LoginFlow, TokenRefresh + → Risk: MEDIUM — run tests for these flows +``` diff --git a/.codex/instructions.md b/.codex/instructions.md new file mode 100644 index 0000000..be99851 --- /dev/null +++ b/.codex/instructions.md @@ -0,0 +1,20 @@ +# Codex Instructions for Polaris + +## 必読ファイル +1. CLAUDE.md(ルール) +2. autorun/INDEX.md(Phase DAG) +3. 該当 Phase の TASKS.md + +## 品質チェック(タスク完了前に必ず実行) +```bash +cargo test && cargo clippy --all-targets --all-features -- -D warnings +``` + +## コミットルール +日本語タグ形式: `[追加]`, `[修正]`, `[改善]`, `[整備]`, `[文書]`, `[検証]`, `[完了]` + +## 禁止事項 +- unsafe コード +- GATE バイパス +- テスト RED のまま完了宣言 +- git push --force diff --git a/.gitignore b/.gitignore index 45f7e09..5cfa934 100644 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,4 @@ yarn-error.log* .worktrees/ .ai/ .env +.gitnexus diff --git a/AGENTS.md b/AGENTS.md index 1bf44f3..c7da178 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,3 +25,105 @@ ## Environment & Configuration - Environment defaults from `.env.example`; typical variables: `GITHUB_TOKEN`, `ANTHROPIC_API_KEY`, `REPOSITORY`, optional `RUST_LOG`/`RUST_BACKTRACE` for debugging. - Local config/state lives in `~/.miyabi/` and `.miyabi/` (do not commit secrets). Review `.miyabi.yml` for runtime behavior before modifying agent workflows. + + +# GitNexus — Code Intelligence + +This project is indexed by GitNexus as **miyabi-cli-standalone** (4734 symbols, 10920 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. + +> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. + +## Always Do + +- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user. +- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. +- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits. +- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. +- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`. + +## When Debugging + +1. `gitnexus_query({query: ""})` — find execution flows related to the issue +2. `gitnexus_context({name: ""})` — see all callers, callees, and process participation +3. `READ gitnexus://repo/miyabi-cli-standalone/process/{processName}` — trace the full execution flow step by step +4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed + +## When Refactoring + +- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`. +- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code. +- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed. + +## Never Do + +- NEVER edit a function, class, or method without first running `gitnexus_impact` on it. +- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis. +- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph. +- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope. + +## Tools Quick Reference + +| Tool | When to use | Command | +|------|-------------|---------| +| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` | +| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` | +| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` | +| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` | +| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` | +| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` | + +## Impact Risk Levels + +| Depth | Meaning | Action | +|-------|---------|--------| +| d=1 | WILL BREAK — direct callers/importers | MUST update these | +| d=2 | LIKELY AFFECTED — indirect deps | Should test | +| d=3 | MAY NEED TESTING — transitive | Test if critical path | + +## Resources + +| Resource | Use for | +|----------|---------| +| `gitnexus://repo/miyabi-cli-standalone/context` | Codebase overview, check index freshness | +| `gitnexus://repo/miyabi-cli-standalone/clusters` | All functional areas | +| `gitnexus://repo/miyabi-cli-standalone/processes` | All execution flows | +| `gitnexus://repo/miyabi-cli-standalone/process/{name}` | Step-by-step execution trace | + +## Self-Check Before Finishing + +Before completing any code modification task, verify: +1. `gitnexus_impact` was run for all modified symbols +2. No HIGH/CRITICAL risk warnings were ignored +3. `gitnexus_detect_changes()` confirms changes match expected scope +4. All d=1 (WILL BREAK) dependents were updated + +## Keeping the Index Fresh + +After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it: + +```bash +npx gitnexus analyze +``` + +If the index previously included embeddings, preserve them by adding `--embeddings`: + +```bash +npx gitnexus analyze --embeddings +``` + +To check whether embeddings exist, inspect `.gitnexus/meta.json` — the `stats.embeddings` field shows the count (0 means no embeddings). **Running analyze without `--embeddings` will delete any previously generated embeddings.** + +> Claude Code users: A PostToolUse hook handles this automatically after `git commit` and `git merge`. + +## CLI + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` | +| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` | +| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` | +| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` | +| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` | +| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` | + + diff --git a/CLAUDE.md b/CLAUDE.md index fe12968..404838a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -117,3 +117,105 @@ miyabi agent run coordinator --issue <番号> --- **このファイルはClaude Codeが自動参照します。** + + +# GitNexus — Code Intelligence + +This project is indexed by GitNexus as **miyabi-cli-standalone** (4734 symbols, 10920 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. + +> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. + +## Always Do + +- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user. +- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. +- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits. +- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance. +- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`. + +## When Debugging + +1. `gitnexus_query({query: ""})` — find execution flows related to the issue +2. `gitnexus_context({name: ""})` — see all callers, callees, and process participation +3. `READ gitnexus://repo/miyabi-cli-standalone/process/{processName}` — trace the full execution flow step by step +4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed + +## When Refactoring + +- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`. +- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code. +- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed. + +## Never Do + +- NEVER edit a function, class, or method without first running `gitnexus_impact` on it. +- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis. +- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph. +- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope. + +## Tools Quick Reference + +| Tool | When to use | Command | +|------|-------------|---------| +| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` | +| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` | +| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` | +| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` | +| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` | +| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` | + +## Impact Risk Levels + +| Depth | Meaning | Action | +|-------|---------|--------| +| d=1 | WILL BREAK — direct callers/importers | MUST update these | +| d=2 | LIKELY AFFECTED — indirect deps | Should test | +| d=3 | MAY NEED TESTING — transitive | Test if critical path | + +## Resources + +| Resource | Use for | +|----------|---------| +| `gitnexus://repo/miyabi-cli-standalone/context` | Codebase overview, check index freshness | +| `gitnexus://repo/miyabi-cli-standalone/clusters` | All functional areas | +| `gitnexus://repo/miyabi-cli-standalone/processes` | All execution flows | +| `gitnexus://repo/miyabi-cli-standalone/process/{name}` | Step-by-step execution trace | + +## Self-Check Before Finishing + +Before completing any code modification task, verify: +1. `gitnexus_impact` was run for all modified symbols +2. No HIGH/CRITICAL risk warnings were ignored +3. `gitnexus_detect_changes()` confirms changes match expected scope +4. All d=1 (WILL BREAK) dependents were updated + +## Keeping the Index Fresh + +After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it: + +```bash +npx gitnexus analyze +``` + +If the index previously included embeddings, preserve them by adding `--embeddings`: + +```bash +npx gitnexus analyze --embeddings +``` + +To check whether embeddings exist, inspect `.gitnexus/meta.json` — the `stats.embeddings` field shows the count (0 means no embeddings). **Running analyze without `--embeddings` will delete any previously generated embeddings.** + +> Claude Code users: A PostToolUse hook handles this automatically after `git commit` and `git merge`. + +## CLI + +| Task | Read this skill file | +|------|---------------------| +| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` | +| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` | +| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` | +| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` | +| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` | +| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` | + + diff --git a/autorun/HANDOFF.md b/autorun/HANDOFF.md new file mode 100644 index 0000000..6fa1ecb --- /dev/null +++ b/autorun/HANDOFF.md @@ -0,0 +1,299 @@ +# Handoff Protocol — エージェント間引き継ぎ手順 + +> 全 Phase のエージェント交代時に必ず従うプロシージャ。 +> 引き継ぎ条件を満たさない限り、次のエージェントは作業を開始してはならない。 + +--- + +## 1. 引き継ぎの原則 + +``` +前任エージェント(sender) + │ + ├─ 1. GATE.md の全条件を GREEN にする + ├─ 2. HANDOFF_NOTE.md を書く + ├─ 3. git commit + push する + └─ 4. 完了を通知する + │ + ▼ +後任エージェント(receiver) + │ + ├─ 1. git pull で最新を取得 + ├─ 2. HANDOFF_NOTE.md を読む + ├─ 3. cargo test + cargo clippy で GREEN を確認 + ├─ 4. TASKS.md の未完了チェックボックスを確認 + └─ 5. 作業開始 +``` + +--- + +## 2. HANDOFF_NOTE.md テンプレート + +各 Phase ディレクトリに sender が書き残すファイル。 + +```markdown +# Handoff Note: Phase N + +## Sender +- Agent: {agent name}@{node} +- Completed at: {ISO 8601 timestamp} +- Commit: {git commit hash} + +## Status +- cargo test: {PASS/FAIL} ({N}/{M} tests) +- cargo clippy: {PASS/FAIL} +- GATE.md: {ALL GREEN / N remaining} + +## What was done +- {completed task 1} +- {completed task 2} + +## What was NOT done (and why) +- {skipped task}: {reason} + +## Known issues / warnings +- {issue 1} +- {issue 2} + +## Context for receiver +- {important context that isn't obvious from code} +- {files that were tricky or need special attention} + +## How to verify +```bash +cargo test +cargo clippy --all-targets -- -D warnings +``` +``` + +--- + +## 3. エージェント別引き継ぎプロシージャ + +### 3.1 Codex → Codex(worktree 間) + +``` +Codex A (sender): + 1. cargo test → GREEN + 2. cargo clippy → GREEN + 3. Write phase-N/HANDOFF_NOTE.md + 4. git add -A && git commit -m "[完了] Phase N: {summary}" + 5. git push + +Codex B (receiver): + 1. git pull origin main + 2. Read phase-N/HANDOFF_NOTE.md + 3. Read phase-N+1/TASKS.md + 4. Read phase-N+1/ASSIGNMENT.md + 5. cargo test → must be GREEN (sender's work didn't break anything) + 6. Begin phase-N+1/TASKS.md checkboxes +``` + +### 3.2 Codex → Claude Code(worktree → local) + +``` +Codex (sender): + 1. cargo test → GREEN + 2. Write phase-N/HANDOFF_NOTE.md + 3. git commit + push + +Claude Code (receiver): + 1. git pull + 2. Read HANDOFF_NOTE.md + 3. cargo test (verify locally) + 4. Read phase-N+1/TASKS.md + 5. If Phase requires interactive debugging (5, 6, 8): proceed locally + 6. If Phase is parallelizable: dispatch sub-tasks to Codex +``` + +### 3.3 Claude Code → Codex(local → worktree) + +``` +Claude Code (sender): + 1. cargo test → GREEN + 2. Write phase-N/HANDOFF_NOTE.md + 3. git commit + push + 4. Dispatch via tmux: + tmux send-keys -t %{pane} "codex 'Execute Phase {N+1}. \ + Read docs/autorun/phase-{N+1}-*/TASKS.md and HANDOFF from phase-{N}. \ + git pull first. Run cargo test before starting. \ + Write your HANDOFF_NOTE.md when done.'" Enter + +Codex (receiver): + 1. git pull + 2. Read phase-N/HANDOFF_NOTE.md (predecessor context) + 3. cargo test → GREEN + 4. Execute TASKS.md + 5. Write phase-N+1/HANDOFF_NOTE.md + 6. git commit + push +``` + +### 3.4 Claude Code → OpenClaw main(local → gateway) + +``` +Claude Code (sender): + 1. cargo test → GREEN + 2. Write phase-N/HANDOFF_NOTE.md + 3. git commit + push + 4. Dispatch: + openclaw agent --agent main --message \ + "[TASK] Execute Phase {N+1} of DTP. \ + Repo: Miyabi-G-K/deterministic-task-protocol. \ + Read docs/autorun/phase-{N+1}-*/TASKS.md. \ + GATE: docs/autorun/phase-{N+1}-*/GATE.md. \ + Previous handoff: docs/autorun/phase-{N}-*/HANDOFF_NOTE.md." + +OpenClaw main (receiver): + 1. git clone / pull + 2. Read HANDOFF_NOTE.md + 3. cargo test → GREEN + 4. Execute or delegate to sub-agents + 5. Write HANDOFF_NOTE.md + 6. git commit + push + 7. Report completion via Telegram +``` + +--- + +## 4. 引き継ぎ条件チェックリスト(sender 用) + +sender は以下を全て満たしてから引き継ぐ: + +``` +[ ] cargo test → GREEN +[ ] cargo clippy -- -D warnings → GREEN +[ ] GATE.md の全条件が GREEN +[ ] HANDOFF_NOTE.md を該当 Phase ディレクトリに書いた +[ ] git add + commit + push 済み +[ ] 次の Phase の ASSIGNMENT.md を確認し、receiver を特定した +[ ] receiver への通知を送った(tmux / OpenClaw / Telegram) +``` + +--- + +## 5. 引き継ぎ受理チェックリスト(receiver 用) + +receiver は以下を全て満たしてから作業開始: + +``` +[ ] git pull で最新コードを取得した +[ ] HANDOFF_NOTE.md を読んだ +[ ] cargo test → GREEN(sender の作業が壊れていないことを確認) +[ ] TASKS.md の残チェックボックスを確認した +[ ] GATE.md の承認条件を理解した +[ ] ASSIGNMENT.md の制約(並列可否、依存)を理解した +``` + +--- + +## 6. 引き継ぎ失敗時のリカバリ + +### sender の GATE が GREEN にならない場合 + +``` +sender: + 1. HANDOFF_NOTE.md に "Status: PARTIAL" と書く + 2. What was NOT done に未完了項目を列挙 + 3. Known issues に失敗原因を書く + 4. git commit + push(作業途中でも push) + 5. 人間に ESCALATE: + "Phase N が GATE 未達。理由: {reason}。対応方針を判断してください。" +``` + +### receiver が cargo test FAIL を検出した場合 + +``` +receiver: + 1. HANDOFF_NOTE.md を再確認(sender が認識していた問題か?) + 2. git log で sender の最終コミットを確認 + 3. 自分のコードが原因でないことを確認 + 4. sender に差し戻し(tmux / OpenClaw で通知): + "Phase N の HANDOFF を受理できません。cargo test FAIL。 + 失敗テスト: {test name}。sender 側で修正してください。" + 5. 3回差し戻しで人間 ESCALATE +``` + +### エージェントがクラッシュ/応答なしの場合 + +``` +監視側(Claude Code or 人間): + 1. tmux capture-pane で最後の出力を確認 + 2. git log で最終コミットを確認 + 3. cargo test で現在の状態を確認 + 4. 代替エージェントを起動: + - 同じ Phase の TASKS.md を渡す + - HANDOFF_NOTE.md がなければ「前任クラッシュ」として扱う + - cargo test GREEN から再開 +``` + +--- + +## 7. 並列 Phase の同期点 + +Phase 2 と Phase 3 は並列実行可能だが、Phase 6 は両方の完了を待つ。 + +``` +Phase 2 (Codex B) ─────┐ + ├──→ 同期点: 両方の GATE GREEN を確認 +Phase 3 (Codex C) ─────┘ │ + ▼ + Phase 4 (Phase 3 依存) + │ + Phase 5 (Phase 3 依存) + │ + Phase 6 開始条件: + [ ] Phase 2 GATE GREEN + [ ] Phase 3 GATE GREEN + [ ] Phase 4 GATE GREEN + [ ] Phase 5 GATE GREEN + [ ] 全 HANDOFF_NOTE.md 存在 +``` + +同期点の確認は **Claude Code(オーケストレーター)** が行う: + +```bash +# 全 Phase の GATE 状態を一括確認 +for phase in phase-2-state-machine phase-3-event-store phase-4-file-lock phase-5-github-sync; do + echo "=== $phase ===" + cat docs/autorun/$phase/GATE.md | grep "\[x\]" | wc -l + cat docs/autorun/$phase/GATE.md | grep "\[ \]" | wc -l +done +``` + +--- + +## 8. 通知テンプレート + +### Phase 完了通知(sender → 全体) + +``` +[DTP] Phase {N} 完了 +Agent: {agent}@{node} +Commit: {hash} +Tests: {N}/{M} GREEN +GATE: ALL GREEN +Next: Phase {N+1} → {receiver agent} +HANDOFF: docs/autorun/phase-{N}-*/HANDOFF_NOTE.md +``` + +### Phase 開始通知(receiver → 全体) + +``` +[DTP] Phase {N} 開始 +Agent: {agent}@{node} +Predecessor: Phase {N-1} by {sender agent} +HANDOFF received: ✅ +cargo test: GREEN +Starting TASKS.md execution... +``` + +### エスカレーション通知 + +``` +🚨 [DTP] Phase {N} ESCALATION +Agent: {agent}@{node} +Issue: {description} +Attempts: {N}/3 +Action needed: 人間判断 +HANDOFF: docs/autorun/phase-{N}-*/HANDOFF_NOTE.md +``` diff --git a/autorun/INDEX.md b/autorun/INDEX.md new file mode 100644 index 0000000..80d2b74 --- /dev/null +++ b/autorun/INDEX.md @@ -0,0 +1,111 @@ +# Deterministic Task Protocol — Auto Run Index + +> 全 Phase の依存 DAG + 承認ゲート + リトライループ + アサインメント + +## Phase DAG + +``` +Phase 0 (前提条件) + │ + ▼ +Phase 1 (型ハードニング) + │ + ├────────────┬────────────┐ + ▼ ▼ │ +Phase 2 Phase 3 │ +(State+GATE) (Event Store) │ + │ │ │ + │ ▼ │ + │ Phase 4 │ + │ (File Lock) │ + │ │ │ + ├────────────┘ │ + ▼ │ +Phase 5 (GitHub Sync) ◄──────┘ + │ + ▼ +Phase 6 (Protocol 統合) ← 核心 + │ + ▼ +Phase 7 (CLI) + │ + ▼ +Phase 8 (E2E + OpenClaw) +``` + +## Phase 別アサインメント + +| Phase | ファイル | 推奨エージェント | 並列可否 | 承認ゲート | +|-------|---------|---------------|---------|-----------| +| 0 | `00-phase0-preconditions.md` | Claude Code (local) | — | cargo test + clippy GREEN | +| 1 | `01-phase1-harden-types.md` | Codex A (worktree) | — | cargo test GREEN + 5 新規テスト | +| 2 | `02-phase2-state-machine-gates.md` | Codex B (worktree) | 3 と並列 OK | 不正遷移テスト 5件 GREEN | +| 3 | `03-phase3-event-store.md` | Codex C (worktree) | 2 と並列 OK | CAS + rebuild テスト GREEN | +| 4 | `04-phase4-file-lock-lease.md` | Codex A (worktree) | 3 完了後 | lease lifecycle テスト GREEN | +| 5 | `05-phase5-github-sync.md` | Claude Code (local) | 3 完了後 | mock API テスト GREEN | +| 6 | `06-phase6-protocol-integration.md` | Claude Code (local) | 2+3+4+5 完了後 | E2E + GATE 拒否 20件 GREEN | +| 7 | `07-phase7-cli.md` | Codex B (worktree) | 6 完了後 | CLI 動作確認 | +| 8 | `08-phase8-e2e-openclaw.md` | Claude Code (local) | 7 完了後 | E2E GREEN + OpenClaw 設計書 | + +## 承認フロー + +``` +Phase N 実行 + │ + ▼ +cargo test + cargo clippy + │ + ├─ GREEN → 承認ゲート通過 + │ │ + │ ▼ + │ Phase N の全チェックボックス確認 + │ │ + │ ├─ 全て ✅ → Phase N+1 へ + │ └─ 未完了あり → 追加作業してリトライ + │ + └─ FAIL → リトライ + │ + ├─ コンパイルエラー → 該当コード修正 → 再ビルド + ├─ テスト失敗 → 原因特定 → 修正 → 再テスト + ├─ clippy 警告 → 該当コード修正 → 再 clippy + └─ 3回連続失敗 → エスカレーション(人間判断) +``` + +## Codex ディスパッチコマンド + +```bash +# Phase 1: Codex A に型ハードニングをアサイン +tmux send-keys -t %9 "codex 'Execute Phase 1 from docs/autorun/01-phase1-harden-types.md. Read the file, implement ALL checkboxes, run cargo test, run cargo clippy. Do NOT proceed to Phase 2.'" Enter + +# Phase 2: Codex B にステートマシンをアサイン(Phase 1 完了後) +tmux send-keys -t %10 "codex 'Execute Phase 2 from docs/autorun/02-phase2-state-machine-gates.md. Read the file, implement ALL checkboxes, run cargo test. Do NOT proceed to Phase 3.'" Enter + +# Phase 3: Codex C に Event Store をアサイン(Phase 1 完了後、Phase 2 と並列) +tmux send-keys -t %11 "codex 'Execute Phase 3 from docs/autorun/03-phase3-event-store.md. Read the file, implement ALL checkboxes, run cargo test. Do NOT proceed to Phase 4.'" Enter +``` + +## リトライループ + +各 Phase は以下のループで実行: + +``` +ATTEMPT = 1 +MAX_ATTEMPTS = 3 + +loop: + execute(Phase N) + result = cargo test + cargo clippy + + if result == GREEN and all_checkboxes_done: + APPROVE → Phase N+1 + break + + if ATTEMPT >= MAX_ATTEMPTS: + ESCALATE → 人間に報告、Phase N を手動レビュー + break + + ATTEMPT += 1 + diagnose(failure) + fix(failure) + goto loop +``` diff --git a/autorun/ROLLBACK.md b/autorun/ROLLBACK.md new file mode 100644 index 0000000..00703bb --- /dev/null +++ b/autorun/ROLLBACK.md @@ -0,0 +1,148 @@ +# ロールバック・中止ポイント定義 + +> 各 Phase の「ここまで戻れる」「ここで止める」の判断基準。 +> エージェントはこのルールに従い、人間の承認なしにロールバックを実行してよい。 + +--- + +## 1. ロールバックポイント一覧 + +各 Phase の開始コミットが安全なロールバック先。 + +| Phase | ロールバック先 | git tag | 条件 | +|-------|-------------|---------|------| +| 0 | 初期コミット | `v0.0-initial` | いつでも戻れる | +| 1 | Phase 0 完了時点 | `v0.1-phase0-done` | Phase 1 の型変更が既存テストを壊した場合 | +| 2 | Phase 1 完了時点 | `v0.2-phase1-done` | ステートマシン改造が壊れた場合 | +| 3 | Phase 1 完了時点 | `v0.2-phase1-done` | Event Store 実装が壊れた場合(Phase 2 とは独立) | +| 4 | Phase 3 完了時点 | `v0.4-phase3-done` | ロック実装が壊れた場合 | +| 5 | Phase 3 完了時点 | `v0.4-phase3-done` | GitHub 同期が壊れた場合 | +| 6 | Phase 5 完了時点 | `v0.6-phase5-done` | Protocol 統合が壊れた場合 | +| 7 | Phase 6 完了時点 | `v0.7-phase6-done` | CLI 実装が壊れた場合 | +| 8 | Phase 7 完了時点 | `v0.8-phase7-done` | E2E が壊れた場合 | + +### タグの打ち方 + +各 Phase の GATE GREEN 確認後、次の Phase に進む前に必ずタグを打つ: + +```bash +git tag v0.{N+1}-phase{N}-done +git push origin v0.{N+1}-phase{N}-done +``` + +### ロールバックの実行方法 + +```bash +# Phase N が壊れた → Phase N-1 の完了地点に戻す +git log --oneline --all # タグを確認 +git checkout v0.{N}-phase{N-1}-done # 安全地点に移動 +git checkout -b rollback/phase-{N}-retry # リトライ用ブランチ作成 +# 修正後、main にマージ +``` + +--- + +## 2. 中止ポイント(ここで止める判断基準) + +### Phase レベルの中止条件 + +| 条件 | アクション | +|------|-----------| +| 同じテストが **3回連続失敗** | Phase を中止。人間にエスカレーション | +| `cargo build` が通らない状態が **30分以上継続** | Phase を中止。ロールバック | +| 依存クレートのバグで進めない | Phase を中止。Issue 作成して待機 | +| 設計上の根本的な問題が発覚 | **全 Phase 中止**。設計レビューに戻る | + +### プロジェクトレベルの中止条件 + +| 条件 | アクション | +|------|-----------| +| Codex レビューで **Critical 指摘が未解決のまま Phase 6 に到達** | 全停止。指摘を解決してから再開 | +| GitHub API の仕様変更で Phase 5 が成立しない | Phase 5 を再設計。Phase 6 以降は凍結 | +| Rust コンパイラのバグで unsafe_code = "forbid" と競合 | バグ報告 + 回避策を検討 | +| 人間が「止めろ」と言った | **即座に全停止**。コミット + push して待機 | + +--- + +## 3. 各 Phase の安全な中断方法 + +### 作業途中で中断する場合 + +```bash +# 1. 現在の作業をコミット(RED でもよい) +git add -A +git commit -m "[中断] Phase {N}: {理由}" + +# 2. push して他エージェントと共有 +git push + +# 3. HANDOFF_NOTE.md に「中断」と書く +cat > autorun/phase-{N}-*/HANDOFF_NOTE.md << 'EOF' +# Handoff Note: Phase N — 中断 + +## Status: 中断 +- Reason: {理由} +- Tests: {状態} +- Last working commit: {hash} + +## 再開手順 +1. git pull +2. cargo test で現状確認 +3. {具体的な再開ポイント} +EOF + +# 4. push +git add -A && git commit -m "[中断] Phase {N}: HANDOFF_NOTE 追記" && git push +``` + +### ロールバックして再開する場合 + +```bash +# 1. 安全地点を確認 +git log --oneline --tags + +# 2. ロールバック(ブランチで) +git checkout -b rollback/phase-{N}-attempt-{M} v0.{N}-phase{N-1}-done + +# 3. 壊れた変更を選択的に取り込み(cherry-pick) +git cherry-pick {良いコミットだけ} + +# 4. テスト確認 +cargo test && cargo clippy --all-targets -- -D warnings + +# 5. main に戻す +git checkout main +git merge rollback/phase-{N}-attempt-{M} +git push +``` + +--- + +## 4. 復旧不能な場合の最終手段 + +全てのリトライが失敗し、ロールバックしても直らない場合: + +``` +1. git tag v0.{N}-abandoned-{date} で現状を記録 +2. 人間に以下を報告: + - 何が壊れたか + - どのコミットまでは動いていたか + - 試したリトライの内容と結果 + - 推奨アクション(設計変更 / 依存変更 / スコープ縮小) +3. 人間の判断を待つ。判断が出るまで一切のコミットをしない +``` + +--- + +## 5. 安全境界の定義 + +| 操作 | 安全? | 備考 | +|------|--------|------| +| `git tag` を打つ | 安全 | いつでも打ってよい | +| `git checkout` でタグに移動 | 安全 | detached HEAD になるが問題なし | +| `git branch` でブランチ作成 | 安全 | | +| `git cherry-pick` | 注意 | コンフリクト時は手動解決 | +| `git merge` | 注意 | テスト GREEN を確認してからマージ | +| `git revert` | 安全 | 特定コミットを取り消す場合に使用 | +| `git reset --hard` | **禁止** | GIT-RULES.md 参照 | +| `git push --force` | **禁止** | GIT-RULES.md 参照 | diff --git a/autorun/phase-0-preconditions/ASSIGNMENT.md b/autorun/phase-0-preconditions/ASSIGNMENT.md new file mode 100644 index 0000000..2dd0fa1 --- /dev/null +++ b/autorun/phase-0-preconditions/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 0 + +- **Agent**: Claude Code (local) +- **Parallel**: None (first phase) +- **Estimated**: 10 min +- **Depends on**: Nothing +- **Blocks**: Phase 1 diff --git a/autorun/phase-0-preconditions/GATE.md b/autorun/phase-0-preconditions/GATE.md new file mode 100644 index 0000000..ca18dea --- /dev/null +++ b/autorun/phase-0-preconditions/GATE.md @@ -0,0 +1,12 @@ +# Approval Gate: Phase 0 + +## Pass Criteria (ALL must be true) +- [ ] `cargo build` → exit 0 +- [ ] `cargo test` → all GREEN +- [ ] `cargo clippy --all-targets -- -D warnings` → 0 warnings +- [ ] `ls -la refs/` → no broken symlinks + +## On Failure +- Fix the failing check +- Retry (max 3 attempts) +- Escalate to human after 3 failures diff --git a/autorun/phase-0-preconditions/TASKS.md b/autorun/phase-0-preconditions/TASKS.md new file mode 100644 index 0000000..3334845 --- /dev/null +++ b/autorun/phase-0-preconditions/TASKS.md @@ -0,0 +1,23 @@ +# Phase 0: 前提条件の確定 + +> 承認ゲート: 全チェック GREEN でなければ Phase 1 に進まない + +## 検証タスク + +- [ ] `cargo build` が成功する(現在のコードベースがコンパイル通る) +- [ ] `cargo test` が全て GREEN(現在 2/2 通過済み) +- [ ] `cargo clippy --all-targets --all-features -- -D warnings` が警告ゼロ +- [ ] GNI インデックスが fresh: `npx gitnexus status --repo deterministic-task-protocol` ← 未インデックス、Phase 1 開始前に実行 +- [ ] refs/ シンボリックリンクが全て有効: `ls -la refs/` で broken link なし +- [ ] Codex 3体レビュー結果が `docs/reviews/` に存在する + +## 承認ゲート + +全チェックボックスが埋まったら Phase 1 へ進む。1つでも未完了なら修正してリトライ。 + +## リトライ条件 + +- clippy 警告 → 該当コードを修正して再度 clippy 実行 +- テスト失敗 → 失敗テストの原因を特定して修正、再度 `cargo test` +- GNI stale → `npx gitnexus analyze --force` を実行 +- シンボリックリンク切れ → 参照先パスを確認して再作成 diff --git a/autorun/phase-1-types/ASSIGNMENT.md b/autorun/phase-1-types/ASSIGNMENT.md new file mode 100644 index 0000000..458a4aa --- /dev/null +++ b/autorun/phase-1-types/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 1 + +- **Agent**: Codex A (worktree) +- **Parallel**: None +- **Estimated**: 30 min +- **Depends on**: Phase 0 GREEN +- **Blocks**: Phase 2, Phase 3 diff --git a/autorun/phase-1-types/GATE.md b/autorun/phase-1-types/GATE.md new file mode 100644 index 0000000..e56ada9 --- /dev/null +++ b/autorun/phase-1-types/GATE.md @@ -0,0 +1,12 @@ +# Approval Gate: Phase 1 + +## Pass Criteria +- [ ] `cargo test` → all GREEN +- [ ] `cargo clippy -- -D warnings` → 0 warnings +- [ ] At least 5 new tests added for new types +- [ ] All Codex R1/R2/R3 type changes reflected + +## On Failure +- Compile error → fix types, check ManagedTask::new() defaults +- Test failure → fix assertions +- Retry (max 3) diff --git a/autorun/phase-1-types/TASKS.md b/autorun/phase-1-types/TASKS.md new file mode 100644 index 0000000..84b97fd --- /dev/null +++ b/autorun/phase-1-types/TASKS.md @@ -0,0 +1,34 @@ +# Phase 1: 型定義のハードニング + +> 依存: Phase 0 GREEN +> 承認ゲート: `cargo test` GREEN + 新規テスト 5 件以上追加 + +## 現状 + +`src/types.rs` に ManagedTask, TaskState, TaskLock, TaskImpact, TasksDocument が定義済み。 +基本構造は動いているが、Codex レビュー指摘の以下が未反映: + +## タスク + +- [ ] TaskState に `Merged` バリアントを追加(R1-4: reviewing と done の間) +- [ ] TaskState に `AwaitingGithubSync` バリアントを追加(R3-2: GitHub 障害時) +- [ ] `CompletionMode` enum を追加: `GithubPr | Manual | ExternalOp`(R3-5) +- [ ] `GitHubEvidence` struct を追加: pr_number, pr_head_ref, pr_state, merge_commit_sha, merged_at, review_decision, issue_state, issue_closed_by_pr(R3-4) +- [ ] `HumanApproval` struct を追加: required, approved_by, approved_at, reason +- [ ] ManagedTask に `completion_mode`, `github_evidence`, `human_approval` フィールド追加 +- [ ] TaskImpact に `analyzed_commit`, `input_hash` フィールド追加(R1-3: 鮮度検証用) +- [ ] TaskLock に `last_heartbeat` フィールド追加(R1-5: lease + heartbeat) +- [ ] `TaskEvent` struct を追加(event log 用: id, ts, event_type, task_id, agent, node, payload) +- [ ] `GateResult` struct を追加(gate_id, passed, reason, checked_at) +- [ ] 各新型の serde roundtrip テストを追加(最低 5 件) + +## 承認ゲート + +- `cargo test` 全 GREEN +- `cargo clippy -- -D warnings` 警告ゼロ +- 新規テスト 5 件以上が追加されている + +## リトライ条件 + +- コンパイルエラー → 型定義の修正、既存コードとの整合性確認 +- 既存テスト破壊 → ManagedTask::new() のデフォルト値を適切に設定 diff --git a/autorun/phase-2-state-machine/ASSIGNMENT.md b/autorun/phase-2-state-machine/ASSIGNMENT.md new file mode 100644 index 0000000..7f97e73 --- /dev/null +++ b/autorun/phase-2-state-machine/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 2 + +- **Agent**: Codex B (worktree) +- **Parallel**: Phase 3 (parallel OK) +- **Estimated**: 45 min +- **Depends on**: Phase 1 GREEN +- **Blocks**: Phase 6 diff --git a/autorun/phase-2-state-machine/GATE.md b/autorun/phase-2-state-machine/GATE.md new file mode 100644 index 0000000..4c542d5 --- /dev/null +++ b/autorun/phase-2-state-machine/GATE.md @@ -0,0 +1,11 @@ +# Approval Gate: Phase 2 + +## Pass Criteria +- [ ] `cargo test` → all GREEN +- [ ] At least 5 invalid transition tests that correctly reject +- [ ] `can_transition()` takes `&ManagedTask` (not just from/to) +- [ ] `Merged` and `AwaitingGithubSync` states have transition rules + +## On Failure +- Protocol tests break → update protocol.rs to use new API +- Retry (max 3) diff --git a/autorun/phase-2-state-machine/TASKS.md b/autorun/phase-2-state-machine/TASKS.md new file mode 100644 index 0000000..552041e --- /dev/null +++ b/autorun/phase-2-state-machine/TASKS.md @@ -0,0 +1,37 @@ +# Phase 2: ステートマシンに GATE predicate を統合 + +> 依存: Phase 1 GREEN +> 承認ゲート: 全遷移ルールにテスト + 不正遷移が全て拒否される + +## 現状 + +`src/state.rs` は `can_transition(from, to) -> bool` の単純な matches! 式。 +conditions/predicates の実評価がない(Codex R1-1 の致命的指摘)。 + +## タスク + +- [ ] `TaskStateMachine` に `Merged` と `AwaitingGithubSync` の遷移ルールを追加 +- [ ] `can_transition()` を `can_transition(task: &ManagedTask, to: TaskState) -> Result<(), GateError>` に変更 + - draft → pending: title 非空 + github_issue_number > 0 + - pending → ready: dependencies.all(done) (DAG 経由で検証) + - ready → analyzing: 無条件 + - analyzing → implementing: impact.is_some() + lock.is_some() + HIGH/CRITICAL は human_approval + - implementing → reviewing: branch_name.is_some() + pr_number > 0 + - reviewing → merged: github_evidence.pr_state == MERGED + merge_commit_sha が 40hex + - merged → done: github_evidence.issue_state == CLOSED +- [ ] `skip_analysis` ルール(pending → implementing)を削除(R1-7) +- [ ] 不正遷移テスト: analyzing → implementing を impact なしで試みる → GateError +- [ ] 不正遷移テスト: reviewing → merged を不正 SHA で試みる → GateError +- [ ] 不正遷移テスト: merged → done を Issue Open で試みる → GateError +- [ ] 全合法遷移の happy path テスト + +## 承認ゲート + +- `cargo test` 全 GREEN +- 不正遷移テストが最低 5 件存在し、全て正しく拒否される +- `can_transition()` が ManagedTask を受け取る形に変更されている + +## リトライ条件 + +- 既存テスト破壊 → protocol.rs のテストが新しい can_transition() に対応しているか確認 +- gate.rs との責務重複 → gate.rs は個別 GATE 関数、state.rs は遷移の orchestration と明確に分離 diff --git a/autorun/phase-3-event-store/ASSIGNMENT.md b/autorun/phase-3-event-store/ASSIGNMENT.md new file mode 100644 index 0000000..1582274 --- /dev/null +++ b/autorun/phase-3-event-store/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 3 + +- **Agent**: Codex C (worktree) +- **Parallel**: Phase 2 (parallel OK) +- **Estimated**: 45 min +- **Depends on**: Phase 1 GREEN +- **Blocks**: Phase 4, Phase 5 diff --git a/autorun/phase-3-event-store/GATE.md b/autorun/phase-3-event-store/GATE.md new file mode 100644 index 0000000..8f3ed78 --- /dev/null +++ b/autorun/phase-3-event-store/GATE.md @@ -0,0 +1,13 @@ +# Approval Gate: Phase 3 + +## Pass Criteria +- [ ] `cargo test` → all GREEN +- [ ] Event append → replay roundtrip test GREEN +- [ ] Snapshot save → load roundtrip test GREEN +- [ ] CAS version conflict test GREEN +- [ ] Rebuild from events test GREEN + +## On Failure +- flock issues → check fs2 crate compatibility +- atomic rename fails → ensure same filesystem +- Retry (max 3) diff --git a/autorun/phase-3-event-store/TASKS.md b/autorun/phase-3-event-store/TASKS.md new file mode 100644 index 0000000..0119b4c --- /dev/null +++ b/autorun/phase-3-event-store/TASKS.md @@ -0,0 +1,39 @@ +# Phase 3: Event Store(JSONL append-only + Snapshot rebuild) + +> 依存: Phase 1 GREEN +> 承認ゲート: event append + snapshot rebuild のテスト GREEN +> 並列実行可能: Phase 2 と同時実行 OK(依存は Phase 1 のみ) + +## 現状 + +`src/store.rs` は in-memory の TaskStore。ファイル永続化なし。 +Codex R2-1/R2-5 指摘: monolithic JSON は concurrent write に弱い → JSONL event log + snapshot。 + +## タスク + +- [ ] `EventStore` struct を追加: `append(event: TaskEvent)`, `replay() -> Vec`, `replay_for_task(task_id) -> Vec` + - ファイルパス: `project_memory/task-events.jsonl` + - append は `OpenOptions::new().append(true).create(true)` で排他不要 +- [ ] `SnapshotStore` struct を追加: `load() -> TasksDocument`, `save(doc, expected_version) -> Result<()>`, `rebuild(events) -> TasksDocument` + - ファイルパス: `project_memory/tasks.snapshot.json` + - R2-1: `save()` は OS flock (`fs2::FileExt::lock_exclusive()`) + version CAS + atomic rename + - CAS: `current.version != expected_version` → Error + - atomic rename: write to `.tmp` → `fs::rename()` → done +- [ ] TaskStore に `persist()` メソッド追加: 現在の in-memory state を snapshot に書き出し +- [ ] TaskStore に `load_from_disk()` メソッド追加: snapshot を読み込んで in-memory に復元 +- [ ] `rebuild_from_events()`: event log を replay して snapshot を再構築するテスト +- [ ] CAS conflict テスト: 2回目の save が version mismatch で失敗する +- [ ] tempfile crate を使った一時ディレクトリでのテスト + +## 承認ゲート + +- `cargo test` 全 GREEN +- event append → replay roundtrip テスト GREEN +- snapshot save → load roundtrip テスト GREEN +- CAS conflict テスト GREEN +- rebuild テスト GREEN + +## リトライ条件 + +- flock が macOS で動かない → `fs2` crate は macOS 対応済み、パーミッション確認 +- atomic rename が cross-filesystem → 同一ディレクトリ内で tmp → rename diff --git a/autorun/phase-4-file-lock/ASSIGNMENT.md b/autorun/phase-4-file-lock/ASSIGNMENT.md new file mode 100644 index 0000000..dd03721 --- /dev/null +++ b/autorun/phase-4-file-lock/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 4 + +- **Agent**: Codex A (worktree) +- **Parallel**: None +- **Estimated**: 30 min +- **Depends on**: Phase 3 GREEN +- **Blocks**: Phase 6 diff --git a/autorun/phase-4-file-lock/GATE.md b/autorun/phase-4-file-lock/GATE.md new file mode 100644 index 0000000..cc250cf --- /dev/null +++ b/autorun/phase-4-file-lock/GATE.md @@ -0,0 +1,12 @@ +# Approval Gate: Phase 4 + +## Pass Criteria +- [ ] `cargo test` → all GREEN +- [ ] Lease acquire → heartbeat → release lifecycle test GREEN +- [ ] Stale detection test GREEN (no heartbeat → expired) +- [ ] Conflict test GREEN (2 tasks same file → reject) +- [ ] proptest for lock invariants GREEN + +## On Failure +- Timing issues → use fixed timestamps in tests +- Retry (max 3) diff --git a/autorun/phase-4-file-lock/TASKS.md b/autorun/phase-4-file-lock/TASKS.md new file mode 100644 index 0000000..0dad1da --- /dev/null +++ b/autorun/phase-4-file-lock/TASKS.md @@ -0,0 +1,41 @@ +# Phase 4: File Lock Manager(lease + heartbeat) + +> 依存: Phase 3 GREEN(EventStore が必要) +> 承認ゲート: lease lifecycle + stale detection + concurrent acquire テスト GREEN + +## 現状 + +`src/lock.rs` に FileLockManager が実装済み(acquire/release/has_conflict/release_expired)。 +TTL ベースの固定期限。Codex R1-5/R2-2 指摘の heartbeat/lease が未実装。 + +## タスク + +- [ ] TaskLock に `last_heartbeat: DateTime` フィールド追加(Phase 1 で追加済みのはず) +- [ ] `is_expired()` を heartbeat ベースに変更: `now - last_heartbeat > lease_duration + 2 * heartbeat_interval` +- [ ] `renew_lease(task_id, agent, node)` メソッド追加: `last_heartbeat` を更新 + event 記録 +- [ ] `acquire_lock()` を atomic 化(R2-2): + 1. `snapshot_store.lock_exclusive()` で OS flock 取得 + 2. snapshot 再読込 + 3. `has_conflict()` 再チェック + 4. stale lock の自動解放 + 5. lock 書き込み + event 記録 + 6. OS flock 解放 +- [ ] `LeaseConfig` struct: `lease_duration_sec: 300`, `heartbeat_interval_sec: 60`, `stale_after_missed: 2` +- [ ] テスト: lease 獲得 → heartbeat → 解放の lifecycle +- [ ] テスト: heartbeat なしで stale 検出 → 自動解放 +- [ ] テスト: 同一ファイルに 2 タスクが acquire → 2つ目が LockError::Conflict +- [ ] テスト: stale lock 解放後に新タスクが acquire 成功 +- [ ] proptest: ランダムなファイルセットで acquire/release を繰り返して不変条件検証 + +## 承認ゲート + +- `cargo test` 全 GREEN +- lease lifecycle テスト GREEN +- stale detection テスト GREEN +- conflict テスト GREEN +- proptest GREEN + +## リトライ条件 + +- heartbeat interval の計算が off-by-one → Duration の比較を `>=` で統一 +- OS flock のデッドロック → `try_lock_exclusive()` + timeout fallback diff --git a/autorun/phase-5-github-sync/ASSIGNMENT.md b/autorun/phase-5-github-sync/ASSIGNMENT.md new file mode 100644 index 0000000..b2720ee --- /dev/null +++ b/autorun/phase-5-github-sync/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 5 + +- **Agent**: Claude Code (local) — needs GitHub API access +- **Parallel**: None +- **Estimated**: 60 min +- **Depends on**: Phase 3 GREEN +- **Blocks**: Phase 6 diff --git a/autorun/phase-5-github-sync/GATE.md b/autorun/phase-5-github-sync/GATE.md new file mode 100644 index 0000000..5f136ad --- /dev/null +++ b/autorun/phase-5-github-sync/GATE.md @@ -0,0 +1,13 @@ +# Approval Gate: Phase 5 + +## Pass Criteria +- [ ] `cargo test` → all GREEN +- [ ] MockFetcher: PR merged → evidence → Merged transition GREEN +- [ ] MockFetcher: API down → AwaitingGithubSync GREEN +- [ ] MockFetcher: Issue manual close → NOT done GREEN +- [ ] GitHubEvidenceFetcher trait defined with mock + real impl + +## On Failure +- gh CLI not found → fall back to HTTP +- Rate limit → exponential backoff +- Retry (max 3) diff --git a/autorun/phase-5-github-sync/TASKS.md b/autorun/phase-5-github-sync/TASKS.md new file mode 100644 index 0000000..5aa66e8 --- /dev/null +++ b/autorun/phase-5-github-sync/TASKS.md @@ -0,0 +1,44 @@ +# Phase 5: GitHub 同期(Evidence Fetcher + Deterministic Sync) + +> 依存: Phase 3 GREEN +> 承認ゲート: mock GitHub API でのテスト GREEN + degraded mode テスト GREEN + +## 現状 + +GitHub 連携は未実装。protocol.rs の `record_merge()` は merge_commit 文字列を直接受け取るのみ。 +Codex R3-1/R3-3/R3-4 指摘: GitHub API から直接 evidence を取得して検証すべき。 + +## タスク + +- [ ] `src/github.rs` モジュールを新規作成 +- [ ] `GitHubEvidenceFetcher` trait を定義: + ```rust + pub trait GitHubEvidenceFetcher { + fn fetch_pr_evidence(&self, pr_number: u64) -> Result; + fn fetch_issue_state(&self, issue_number: u64) -> Result; + } + ``` +- [ ] `RealGitHubFetcher`: `gh` CLI 経由で実装(`gh pr view --json mergeCommit,state,...`) +- [ ] `MockGitHubFetcher`: テスト用 +- [ ] `DeterministicSync` struct: + - `pull_evidence(task_id)`: GitHub から evidence を取得して task に保存 + - `push_state(task_id)`: ローカル状態をラベル/Projects に反映 + - `sync_with_degraded_mode()`: API 障害時は AwaitingGithubSync に遷移 +- [ ] `SyncError` enum: `ApiUnavailable`, `RateLimited`, `NotFound`, `StateMismatch` +- [ ] R3-2: `AwaitingGithubSync` → リトライキュー → API 復旧後に `Merged` に遷移 +- [ ] R3-3: `Issue Closed` だけでは done にしない。`issue_closed_by_pr == true` を検証 +- [ ] protocol.rs の `record_merge()` を `verify_merge()` に改名: evidence fetcher 経由で merge を検証 +- [ ] テスト: MockFetcher で PR merged → evidence 取得 → Merged 遷移 +- [ ] テスト: MockFetcher で API 障害 → AwaitingGithubSync 遷移 → retry → Merged +- [ ] テスト: Issue が手動 close → done に遷移しない + +## 承認ゲート + +- `cargo test` 全 GREEN +- mock ベースの sync テスト GREEN +- degraded mode テスト GREEN + +## リトライ条件 + +- `gh` CLI が見つからない → PATH 確認、または HTTP client に fallback +- GitHub API rate limit → exponential backoff (1s, 2s, 4s, 8s, max 60s) diff --git a/autorun/phase-6-protocol/ASSIGNMENT.md b/autorun/phase-6-protocol/ASSIGNMENT.md new file mode 100644 index 0000000..a865819 --- /dev/null +++ b/autorun/phase-6-protocol/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 6 + +- **Agent**: Claude Code (local) — integration requires interactive debugging +- **Parallel**: None (depends on ALL previous) +- **Estimated**: 90 min +- **Depends on**: Phase 2 + 3 + 4 + 5 ALL GREEN +- **Blocks**: Phase 7 diff --git a/autorun/phase-6-protocol/GATE.md b/autorun/phase-6-protocol/GATE.md new file mode 100644 index 0000000..a5b8959 --- /dev/null +++ b/autorun/phase-6-protocol/GATE.md @@ -0,0 +1,14 @@ +# Approval Gate: Phase 6 + +## Pass Criteria +- [ ] `cargo test` → all GREEN (20+ tests) +- [ ] E2E happy path: draft → done test GREEN +- [ ] GATE rejection tests: 8+ tests, all correctly reject +- [ ] Escape hatch tests: force_unlock, manual_complete GREEN +- [ ] All operations emit events to EventStore +- [ ] Snapshot persists after every state change + +## On Failure +- Component integration mismatch → check trait bounds +- Event ordering → verify append is sequential +- Retry (max 3), then escalate diff --git a/autorun/phase-6-protocol/TASKS.md b/autorun/phase-6-protocol/TASKS.md new file mode 100644 index 0000000..5436930 --- /dev/null +++ b/autorun/phase-6-protocol/TASKS.md @@ -0,0 +1,60 @@ +# Phase 6: Protocol 統合(全 GATE を1クラスに結合) + +> 依存: Phase 2 + 3 + 4 + 5 全て GREEN +> 承認ゲート: E2E happy path + 全 GATE 拒否テスト + escape hatch テスト GREEN +> これが核心。全コンポーネントをここでつなぐ。 + +## 現状 + +`src/protocol.rs` に DeterministicExecutionProtocol が実装済み。 +register → check_dependencies → record_impact → assign_and_lock → record_branch → record_pr → record_merge の一連が動く。 +ただし Phase 1-5 の拡張がまだ統合されていない。 + +## タスク + +- [ ] Protocol に EventStore を統合: 全 GATE 通過/拒否を event として記録 +- [ ] Protocol に SnapshotStore を統合: 状態遷移を即座に永続化 +- [ ] Protocol に FileLockManager の atomic acquire を統合 +- [ ] Protocol に GitHubEvidenceFetcher を統合: verify_merge() で evidence を取得 +- [ ] `verify_merge()`: record_merge() を置き換え。evidence fetcher 経由で: + 1. PR API から GitHubEvidence を取得 + 2. pr_state == MERGED を検証 + 3. merge_commit_sha が 40hex を検証 + 4. review_decision == APPROVED を検証 + 5. evidence を task に保存 + 6. Reviewing → Merged に遷移 +- [ ] `confirm_done()`: Merged → Done の遷移。evidence fetcher 経由で: + 1. issue_state == CLOSED を検証 + 2. issue_closed_by_pr == true を検証(CompletionMode::GithubPr の場合) + 3. worklog に記録 + 4. Done に遷移 +- [ ] `heartbeat()`: lease 更新メソッド +- [ ] Escape hatches(R3-5): + - `force_unlock(task_id, reason, operator)`: 監査付き強制ロック解放 + - `manual_complete(task_id, reason, operator)`: PRなし完了(CompletionMode::Manual) + - `reconcile_from_github(task_id)`: GitHub の状態を強制 pull + - 全 escape hatch は event log に `reason` + `operator` + `timestamp` を記録 +- [ ] テスト: E2E happy path(draft → ... → done)全シーケンス +- [ ] テスト: GATE 0 拒否(Issue なし → 登録拒否) +- [ ] テスト: GATE 2 拒否(依存未解決 → blocked) +- [ ] テスト: GATE 3 拒否(impact なし → implementing 拒否) +- [ ] テスト: GATE 3 拒否(HIGH risk + 承認なし → 拒否) +- [ ] テスト: GATE 4 拒否(ロック競合 → 拒否) +- [ ] テスト: GATE 5 拒否(ブランチ名不正 → 拒否) +- [ ] テスト: GATE 7 拒否(不正 SHA → merged 拒否) +- [ ] テスト: escape hatch: force_unlock → event log に記録 +- [ ] テスト: escape hatch: manual_complete → Done with reason +- [ ] テスト: 2タスクの DAG 依存: task-0 done → task-1 ready → task-1 実行可能 + +## 承認ゲート + +- `cargo test` 全 GREEN(20 件以上) +- E2E happy path テスト GREEN +- 全 GATE 拒否テスト GREEN(8 件以上) +- escape hatch テスト GREEN(2 件以上) +- event log に全操作が記録されている + +## リトライ条件 + +- Protocol と StateMachine の責務境界で混乱 → Protocol が唯一の外部窓口、StateMachine は internal +- EventStore と SnapshotStore の初期化順序 → Protocol::new() で両方を初期化 diff --git a/autorun/phase-7-cli/ASSIGNMENT.md b/autorun/phase-7-cli/ASSIGNMENT.md new file mode 100644 index 0000000..40b18c6 --- /dev/null +++ b/autorun/phase-7-cli/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 7 + +- **Agent**: Codex B (worktree) +- **Parallel**: None +- **Estimated**: 45 min +- **Depends on**: Phase 6 GREEN +- **Blocks**: Phase 8 diff --git a/autorun/phase-7-cli/GATE.md b/autorun/phase-7-cli/GATE.md new file mode 100644 index 0000000..6f3df1c --- /dev/null +++ b/autorun/phase-7-cli/GATE.md @@ -0,0 +1,12 @@ +# Approval Gate: Phase 7 + +## Pass Criteria +- [ ] `cargo build` → dtp binary compiles +- [ ] `dtp register --issue 1 --title test` → creates task +- [ ] `dtp status` → JSON output with task list +- [ ] `dtp --help` → all subcommands listed +- [ ] Exit codes: 0=success, 1=gate-rejected, 2=input-error + +## On Failure +- clap derive issues → check feature flags +- Retry (max 3) diff --git a/autorun/phase-7-cli/TASKS.md b/autorun/phase-7-cli/TASKS.md new file mode 100644 index 0000000..5ed04c9 --- /dev/null +++ b/autorun/phase-7-cli/TASKS.md @@ -0,0 +1,41 @@ +# Phase 7: CLI(dtp コマンド) + +> 依存: Phase 6 GREEN +> 承認ゲート: 全サブコマンドが動作 + `dtp run-e2e` が GREEN + +## タスク + +- [ ] `src/main.rs` に clap ベースの CLI を実装(Cargo.toml に `clap = { version = "4", features = ["derive"] }` 追加) +- [ ] サブコマンド一覧: + ``` + dtp register --issue --title [--deps ] [--mode github-pr|manual|external-op] + dtp dag # DAG 可視化(テキスト) + dtp dispatchable # 実行可能タスク一覧 + dtp impact --risk --symbols [--approve] + dtp assign --agent --node --files + dtp heartbeat + dtp branch + dtp pr + dtp verify-merge # GitHub API 経由 + dtp confirm-done + dtp status [task-id] # JSON or human-readable + dtp locks # ロック一覧 + dtp unlock --reason --operator + dtp events [task-id] # event log 表示 + dtp rebuild-snapshot # event log から snapshot 再構築 + dtp run-e2e # 内蔵 E2E テスト(Phase 8 統合テスト) + ``` +- [ ] `--format json` オプション: 全コマンドで JSON 出力対応(rust-ai-pipeline の Phase1OutputFormat パターン) +- [ ] 終了コード: 0=成功, 1=GATE拒否, 2=入力不正(rust-ai-pipeline の EXIT_* パターン) +- [ ] テスト: register → status で task が存在する +- [ ] テスト: 不正入力(issue=0)→ 終了コード 2 + +## 承認ゲート + +- `cargo build` 成功 +- `dtp register --issue 1 --title test` が動作する +- `dtp status` が JSON を返す + +## リトライ条件 + +- clap の derive マクロでコンパイルエラー → features を確認 diff --git a/autorun/phase-8-e2e/ASSIGNMENT.md b/autorun/phase-8-e2e/ASSIGNMENT.md new file mode 100644 index 0000000..267e717 --- /dev/null +++ b/autorun/phase-8-e2e/ASSIGNMENT.md @@ -0,0 +1,7 @@ +# Assignment: Phase 8 + +- **Agent**: Claude Code (local) — needs real GitHub + OpenClaw +- **Parallel**: None (final phase) +- **Estimated**: 120 min +- **Depends on**: Phase 7 GREEN +- **Blocks**: Nothing (final) diff --git a/autorun/phase-8-e2e/GATE.md b/autorun/phase-8-e2e/GATE.md new file mode 100644 index 0000000..b3720cb --- /dev/null +++ b/autorun/phase-8-e2e/GATE.md @@ -0,0 +1,12 @@ +# Approval Gate: Phase 8 + +## Pass Criteria +- [ ] E2E: gh issue create → dtp register → ... → dtp confirm-done → Issue Closed +- [ ] OpenClaw integration design doc exists at docs/openclaw-integration.md +- [ ] agent-skill-bus record-run recorded +- [ ] All events in task-events.jsonl for the E2E run + +## On Failure +- GitHub API failure → run with mock mode +- OpenClaw unreachable → document as known limitation +- Retry (max 3), then human sign-off diff --git a/autorun/phase-8-e2e/TASKS.md b/autorun/phase-8-e2e/TASKS.md new file mode 100644 index 0000000..4db1396 --- /dev/null +++ b/autorun/phase-8-e2e/TASKS.md @@ -0,0 +1,40 @@ +# Phase 8: E2E 統合テスト + OpenClaw 連携設計 + +> 依存: Phase 7 GREEN +> 承認ゲート: E2E テスト GREEN + OpenClaw 連携仕様書完成 + +## タスク + +- [ ] E2E テスト(`dtp run-e2e`): + 1. `gh issue create` → Issue #N + 2. `dtp register --issue N --title "e2e test"` + 3. `dtp dispatchable` → task が ready + 4. `dtp impact task-001 --risk LOW --symbols 2` + 5. `dtp assign task-001 --agent test --node local --files "src/test.rs"` + 6. `dtp branch task-001 feature/issue-N-e2e` + 7. `dtp pr task-001 M` (実際に PR 作成) + 8. `dtp verify-merge task-001` (実際に merge) + 9. `dtp confirm-done task-001` + 10. `dtp status task-001` → Done ✅ + 11. `gh issue view N` → Closed ✅ + 12. `dtp events task-001` → 全ステップの event 記録あり +- [ ] OpenClaw 連携設計書作成: + - OpenClaw main エージェントが `dtp` CLI をどう呼ぶか + - エージェントの SOUL.md に DTP GATE を記述 + - heartbeat を cron で自動実行する仕組み + - 複数ノードでの tasks.snapshot.json 共有方法(git push or sshfs) +- [ ] rust-ai-pipeline 統合テスト: + - `dtp assign` 後に `ai-pipeline phase1 --format json` を実行 + - `all_passed == false` なら implementing に留まる(GATE 4.5) +- [ ] `npx agent-skill-bus record-run` で E2E 結果を記録 + +## 承認ゲート + +- E2E テスト GREEN +- OpenClaw 連携設計書が `docs/openclaw-integration.md` に存在 +- agent-skill-bus に記録済み + +## リトライ条件 + +- GitHub API で E2E が失敗 → mock mode で再テスト +- OpenClaw Gateway 到達不能 → Tailscale/SSH 確認 diff --git a/docs/dtp/GIT-RULES.md b/docs/dtp/GIT-RULES.md new file mode 100644 index 0000000..2881f40 --- /dev/null +++ b/docs/dtp/GIT-RULES.md @@ -0,0 +1,170 @@ +# Git 運用ルール + +> このリポジトリにおけるコミットとプルリクエストの出し方の定義。 +> 全エージェント・全人間がこのルールに従う。 + +--- + +## ローカルコミットのルール + +### いつコミットするか + +| 状況 | コミットする? | +|------|-------------| +| ファイルを1つ以上変更した | はい、すぐコミット | +| テストを追加した | はい、すぐコミット | +| ドキュメントを更新した | はい、すぐコミット | +| 設定ファイルを変更した | はい、すぐコミット | +| 作業途中だが一区切りついた | はい、途中でもコミット | +| 1時間以上コミットしていない | 危険。今すぐコミット | + +**原則: 小刻みにコミット。大きな変更を1コミットにまとめない。** + +### コミットメッセージの形式 + +日本語タグ形式を使用する。 + +``` +[タグ] 何をしたかの要約(1行、日本語) + +(必要なら)詳細・理由・影響範囲 +``` + +### タグ一覧 + +| タグ | いつ使う | 例 | +|------|---------|-----| +| `[追加]` | 新しい機能・ファイルを加えた | `[追加] DAGエンジンのトポロジカルソート実装` | +| `[修正]` | バグ・不具合を直した | `[修正] ロック競合チェックの off-by-one エラー` | +| `[改善]` | 既存コードの品質・性能を上げた | `[改善] Clippy警告を解消` | +| `[整備]` | 設定・CI・依存など開発環境の手入れ | `[整備] clap 依存を Cargo.toml に追加` | +| `[文書]` | ドキュメント・READMEの更新 | `[文書] HANDOFF プロトコルを追加` | +| `[削除]` | 不要なコード・ファイルを取り除いた | `[削除] 未使用の TaskState::Deploying を削除` | +| `[検証]` | テストの追加・修正 | `[検証] GATE拒否テスト5件追加` | +| `[完了]` | Phase が完了した(GATE GREEN) | `[完了] Phase 2: ステートマシンGATE統合` | + +### コミットの粒度 + +``` +良い例: + [追加] TaskLock に last_heartbeat フィールド追加 + [検証] lease 期限切れテスト追加 + [修正] is_expired() の閾値計算を修正 + +悪い例: + [追加] Phase 4 全部実装 ← 大きすぎ。分割すること +``` + +--- + +## リモート push のルール + +### いつ push するか + +| 状況 | push する? | +|------|-----------| +| Phase の GATE が GREEN になった | はい、必ず push | +| HANDOFF_NOTE.md を書いた | はい、必ず push(次のエージェントが pull する) | +| 作業の区切りがついた(3コミット以上溜まった) | はい、push | +| 1日の作業終了時 | はい、必ず push | +| テストが RED のまま | push してよい(ただしコミットメッセージに明記) | + +**原則: push は頻繁に。ローカルに溜め込まない。** + +--- + +## プルリクエストのルール + +### いつ PR を出すか + +このリポジトリでは **Phase 単位で PR を出す**。 + +| 状況 | PR を出す? | +|------|-----------| +| Phase の GATE.md が全て GREEN | はい、PR を出す | +| Phase の作業が途中 | 出さない(main に直接 push でよい) | +| 複数 Phase をまとめて | 出さない(1 PR = 1 Phase) | + +### PR のタイトル形式 + +``` +[完了] Phase {N}: {Phase の要約} +``` + +例: +``` +[完了] Phase 1: 型定義のハードニング +[完了] Phase 2: ステートマシンにGATE predicate統合 +[完了] Phase 6: Protocol統合(全GATEを1クラスに結合) +``` + +### PR の本文テンプレート + +```markdown +## 概要 +Phase {N} の全タスクを完了。 + +## 完了したタスク +- [x] タスク1 +- [x] タスク2 +- [x] タスク3 + +## テスト結果 +- cargo test: {N}/{M} GREEN +- cargo clippy: 警告ゼロ + +## GATE 状態 +autorun/phase-{N}-*/GATE.md の全条件 GREEN + +## 次の Phase +Phase {N+1} に引き継ぎ可能。 +HANDOFF_NOTE.md を autorun/phase-{N}-*/ に配置済み。 +``` + +### PR のマージ条件 + +| 条件 | 必須? | +|------|--------| +| cargo test GREEN | 必須 | +| cargo clippy 警告ゼロ | 必須 | +| GATE.md 全条件 GREEN | 必須 | +| HANDOFF_NOTE.md 作成済み | 必須 | +| 人間のレビュー承認 | Phase 6, 8 のみ必須。他は自動マージ可 | +| Codex レビュー | 推奨(必須ではない) | + +### ブランチ戦略 + +``` +main ← 全ての作業はここに直接 push + ← Phase 完了時に PR(main → main ではなく、記録用) + +feature/phase-{N}-{slug} ← Phase の作業ブランチ(任意) + 例: feature/phase-2-state-machine-gates + +worktree ← Codex が並列実行時に使用 +``` + +**原則: main に直接 push してよい。ブランチは並列作業時のみ使用。** + +--- + +## 現在のコミット履歴 + +``` +b3dcbfa [整備] autorun/ をトップレベルに移動 +5f8b4e2 [文書] Auto Run Playbook 階層化 + Handoff Protocol 完成 +c88f218 [追加] Rust 基盤: types + state machine with GATE predicates +c030294 [追加] 初期条件: SSOT ドキュメント + ワークスペース構造 +``` + +--- + +## 禁止事項 + +| 禁止 | 理由 | +|------|------| +| `git push --force` | 他エージェントの作業を消す | +| `git reset --hard` | ローカルの未コミット変更を消す | +| 1日以上 push しない | 他エージェントとの同期が切れる | +| コミットメッセージを英語で書く | このリポは日本語タグ形式 | +| 10ファイル以上を1コミットにまとめる | 分割すること | diff --git a/docs/dtp/PLAN-v1.md b/docs/dtp/PLAN-v1.md new file mode 100644 index 0000000..a404d67 --- /dev/null +++ b/docs/dtp/PLAN-v1.md @@ -0,0 +1,414 @@ +# Deterministic Task Execution Protocol — Implementation Plan + +## Context + +### 問題 +LLM エージェントは確率的にトークンを生成するため、「完了しました」と言いながら Issue を Close しない、impact 分析を飛ばす、別エージェントと同じファイルを同時編集する、といった **揺らぎ** が発生する。現在、この揺らぎを防ぐ仕組みが **3箇所に分散して不完全に存在** している。 + +### ゴール +`project_memory/tasks.json` を **確定的 DAG + ステートマシン + ファイルロック** の三位一体として実装し、LLM の発言ではなく **JSON の状態遷移だけが事実** となる実行プロトコルを構築する。ゴールに対して確定的な手順を踏めば、確実に成果物1点にたどり着く。 + +### North Star(一文) +> LLM の揺らぎを許さない。tasks.json の状態遷移が許可し、GitHub が確定し、git merge が不可逆にする。この三段階だけが「完了」を定義する。 + +--- + +## 既存ピースの統合マップ + +``` +統合先: @miyabi/task-manager (Miyabi/packages/task-manager/) + +① ステートマシン(既存・そのまま使う) + src/state/task-state-machine.ts + - 10状態、27遷移ルール + - applyTransition() で状態遷移を検証・適用 + - conditions/sideEffects 付き + +② DAG(移植元: KOTOWARI/skills/openclaw-crowd/src/scheduling/) + task-dependency-graph.ts + - Kahn のトポロジカルソート + - 循環検出(DFS) + - hard/soft 依存の区別 + - getReadyTasks() で実行可能タスクを返す + +③ ファイルロック(移植元: agent-skill-bus/src/queue.js) + PromptRequestQueue + - JSONL ベースのロック管理 + - TTL 付きロック(デフォルト 7200秒) + - startExecution() でロック獲得 + 競合チェック + - releaseExpiredLocks() で TTL 切れを自動解放 + - getDispatchable() で DAG 依存 + ロック競合を同時チェック +``` + +--- + +## 実装計画 + +### Phase 1: ManagedTask 型の拡張 + +**ファイル**: `Miyabi/packages/task-manager/src/types/task.ts` + +追加するフィールド: + +```typescript +export interface TaskLock { + lockedBy: string; // "{agentName}@{nodeName}" + lockedAt: string; // ISO 8601 + ttl: number; // 秒 + affectedFiles: string[]; // ロック対象ファイルパス +} + +export interface TaskImpact { + riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'; + affectedSymbols: number; + depth1: string[]; // 直接影響シンボル名 + analyzedAt: string; // GNI 分析タイムスタンプ +} + +// ManagedTask に追加 +export interface ManagedTask extends BaseTask { + // ...既存フィールド... + + // NEW: DAG + dependents: string[]; // このタスクが done になったら解放されるタスク + softDependencies: string[]; // あれば待つが、なくても進められる + + // NEW: ロック + lock: TaskLock | null; + + // NEW: Impact(C&I Phase A の結果) + impact: TaskImpact | null; + + // NEW: 確定的参照 + branchName: string | null; // feature/issue-{N}-{slug} + prNumber: number | null; + mergeCommit: string | null; // merge 後のハッシュ +} +``` + +### Phase 2: TaskDependencyGraph の移植 + +**新ファイル**: `Miyabi/packages/task-manager/src/dag/task-dag.ts` + +KOTOWARI の `task-dependency-graph.ts` から以下を移植: +- `addTask()`, `removeTask()`, `addDependency()` +- `topologicalSort()` (Kahn のアルゴリズム) +- `detectCycle()` (DFS) +- `getReadyTasks()` — dependencies が全て done のタスクを返す +- `getParallelGroups()` — 同一レベルのタスク群を返す(DAG levels) + +**変更点**: +- `ScheduledTask` → `ManagedTask` に型を合わせる +- `hard`/`soft` 依存を `dependencies`/`softDependencies` にマップ +- シリアライズ/デシリアライズを JSON 対応に + +### Phase 3: FileLockManager の移植 + +**新ファイル**: `Miyabi/packages/task-manager/src/lock/file-lock-manager.ts` + +agent-skill-bus の `queue.js` から以下を移植: +- ロック獲得: `acquireLock(taskId, agent, node, files, ttl)` +- ロック解放: `releaseLock(taskId)` +- TTL 切れ自動解放: `releaseExpiredLocks()` +- 競合チェック: `hasConflict(files): { conflicting: boolean, heldBy: string, taskId: string }` + +**変更点**: +- JSONL → tasks.json 内の `fileLocks` フィールドに統合 +- `prId` → `taskId` にリネーム +- TypeScript 化 + +### Phase 4: DeterministicExecutionProtocol — 確定的シーケンスの実装 + +**新ファイル**: `Miyabi/packages/task-manager/src/protocol/deterministic-execution.ts` + +これが核心。全ステップを **ステートマシンのゲート** として実装: + +```typescript +export class DeterministicExecutionProtocol { + private stateMachine: TaskStateMachine; + private dag: TaskDAG; + private lockManager: FileLockManager; + private store: TaskStore; // tasks.json の読み書き + + /** + * Step 0: タスク登録 — Issue が存在しなければ拒否 + * GATE: githubIssueNumber が null → 登録拒否 + */ + registerTask(input: CreateTaskInput & { githubIssueNumber: number }): ManagedTask + + /** + * Step 1: DAG に登録 — 循環依存があれば拒否 + * GATE: detectCycle() が non-null → 登録拒否 + */ + addToDAG(taskId: string, dependencies: string[]): void + + /** + * Step 2: 依存解決チェック — 全依存が done でなければ blocked + * GATE: dependencies.every(d => store.get(d).currentState === 'done') + */ + checkDependencies(taskId: string): 'ready' | 'blocked' + + /** + * Step 3: Impact 分析記録 — impact が null なら implementing に遷移不可 + * GATE: task.impact !== null && task.impact.riskLevel !== undefined + * GATE: HIGH/CRITICAL → humanApprovalRequired = true + */ + recordImpact(taskId: string, impact: TaskImpact): void + + /** + * Step 4: アサイン + ロック獲得 — 競合があれば拒否 + * GATE: lockManager.hasConflict(files) === false + * EFFECT: lock フィールドに書き込み、fileLocks テーブルに登録 + * EFFECT: 状態遷移 analyzing → implementing + */ + assignAndLock(taskId: string, agent: string, node: string, files: string[]): void + + /** + * Step 5: ブランチ作成 — branchName が null なら PR 作成不可 + * GATE: branchName matches /^(feature|fix|hotfix)\/issue-\d+-/ + */ + recordBranch(taskId: string, branchName: string): void + + /** + * Step 6: PR 作成 — prNumber が null なら reviewing に遷移不可 + * GATE: prNumber > 0 && branchName !== null + * EFFECT: 状態遷移 implementing → reviewing + */ + recordPR(taskId: string, prNumber: number): void + + /** + * Step 7: Merge — mergeCommit が null なら done に遷移不可 + * GATE: mergeCommit matches /^[0-9a-f]{40}$/ + * EFFECT: 状態遷移 reviewing → done + * EFFECT: ロック解放 + * EFFECT: dependents の blocked → pending 遷移 + * EFFECT: GitHub Issue Close 確認 + */ + recordMerge(taskId: string, mergeCommit: string): void + + /** + * Step 8: 監査記録 + * EFFECT: worklog.md に追記 + * EFFECT: agent-skill-bus に record-run + */ + recordAudit(taskId: string): void +} +``` + +**重要**: 各メソッドは **GATE 条件を満たさなければ例外を投げる**。LLM が「大丈夫です」と言っても、GATE が閉じていれば実行不可能。 + +### Phase 5: TaskStore — tasks.json の永続化 + +**新ファイル**: `Miyabi/packages/task-manager/src/store/task-store.ts` + +```typescript +export class TaskStore { + private filePath: string; // project_memory/tasks.json + + load(): TasksDocument + save(doc: TasksDocument): void + getTask(id: string): ManagedTask | null + updateTask(id: string, patch: Partial): void + getFileLocks(): Record // file → taskId + setFileLock(file: string, taskId: string): void + removeFileLock(file: string): void +} + +export interface TasksDocument { + version: number; + lastUpdated: string; + tasks: ManagedTask[]; + fileLocks: Record; + dagLevels: string[][]; // トポロジカルソート結果のキャッシュ +} +``` + +### Phase 6: GitHub 同期の強化 + +**既存ファイル**: `src/sync/bidirectional-sync.ts` + +変更: +- `syncTasks()` に **merge commit 検証** を追加 + - ローカルで done なのに GitHub で Issue が Open → **矛盾検出、done → blocked に巻き戻し** + - GitHub で Closed なのにローカルで implementing → **pull で done に更新** +- `conflictStrategy` に `'deterministic'` を追加 + - GitHub の状態を常に優先(LLM のローカル判断を信用しない) + +### Phase 7: CLI コマンド + +**新ファイル**: `Miyabi/packages/task-manager/src/cli/deterministic-cli.ts` + +```bash +# タスク登録(Issue 必須) +miyabi task register --issue 45 --title "JWT→セッション移行" --deps task-000 + +# DAG 可視化 +miyabi task dag + +# 依存解決 + ディスパッチ可能タスク一覧 +miyabi task dispatchable + +# アサイン + ロック +miyabi task assign task-001 --agent kotowari-dev --node macbook-pro --files "src/auth/*.ts" + +# 状態確認(JSON出力、人間可読) +miyabi task status [task-id] + +# ロック一覧 +miyabi task locks + +# 強制ロック解放(TTL切れ or 人間判断) +miyabi task unlock task-001 + +# 同期(GitHub → ローカル → GitHub) +miyabi task sync +``` + +--- + +## ファイル構成(最終) + +``` +Miyabi/packages/task-manager/src/ +├── types/ +│ ├── task.ts # ← Phase 1: TaskLock, TaskImpact, 拡張フィールド追加 +│ ├── config.ts # ← Phase 6: 'deterministic' conflict strategy 追加 +│ └── index.ts +├── state/ +│ └── task-state-machine.ts # 既存(変更なし) +├── dag/ # ← Phase 2: NEW +│ └── task-dag.ts +├── lock/ # ← Phase 3: NEW +│ └── file-lock-manager.ts +├── protocol/ # ← Phase 4: NEW(核心) +│ └── deterministic-execution.ts +├── store/ # ← Phase 5: NEW +│ └── task-store.ts +├── sync/ +│ ├── bidirectional-sync.ts # ← Phase 6: merge commit 検証追加 +│ └── ... +├── execution/ +│ └── ... # 既存(変更なし) +├── decomposition/ +│ └── ... # 既存(変更なし) +├── cli/ # ← Phase 7: NEW +│ └── deterministic-cli.ts +├── task-manager.ts # ← DeterministicExecutionProtocol を統合 +└── index.ts # ← 新モジュールの export 追加 +``` + +--- + +## 成果物: tasks.json の確定的スキーマ + +```jsonc +{ + "version": 1, + "lastUpdated": "2026-04-10T00:00:00Z", + "tasks": [ + { + "id": "task-001", + "title": "JWT→セッション認証移行", + "description": "...", + "type": "refactor", + "priority": 80, + "githubIssueNumber": 45, + "currentState": "implementing", + "stateHistory": [ + { "from": "draft", "to": "pending", "timestamp": "...", "triggeredBy": "user" }, + { "from": "pending", "to": "analyzing", "timestamp": "...", "triggeredBy": "system" }, + { "from": "analyzing", "to": "implementing", "timestamp": "...", "triggeredBy": "system" } + ], + "assignedAgent": "kotowari-dev", + "dependencies": ["task-000"], + "softDependencies": [], + "dependents": ["task-002", "task-003"], + "lock": { + "lockedBy": "kotowari-dev@macbook-pro", + "lockedAt": "2026-04-10T00:00:00Z", + "ttl": 7200, + "affectedFiles": ["src/auth/auth.controller.ts", "src/middleware/auth.middleware.ts"] + }, + "impact": { + "riskLevel": "HIGH", + "affectedSymbols": 12, + "depth1": ["LoginHandler", "RefreshHandler", "LogoutHandler"], + "analyzedAt": "2026-04-10T00:00:00Z" + }, + "branchName": "feature/issue-45-jwt-to-session", + "prNumber": null, + "mergeCommit": null, + "syncVersion": 3, + "retryCount": 0 + } + ], + "fileLocks": { + "src/auth/auth.controller.ts": "task-001", + "src/middleware/auth.middleware.ts": "task-001" + }, + "dagLevels": [ + ["task-000"], + ["task-001"], + ["task-002", "task-003"] + ] +} +``` + +--- + +## 確定的ゲート一覧(LLM の揺らぎを封じる全チェックポイント) + +| Step | 遷移 | GATE(これが false なら遷移不可) | +|------|------|------| +| 0 | → draft | `githubIssueNumber > 0` | +| 1 | draft → pending | `title.length > 0 && description.length > 0 && dependencies は明示的配列` | +| 2 | pending → analyzing | `dependencies.every(d => tasks[d].currentState === 'done')` | +| 3 | analyzing → implementing | `impact !== null && (impact.riskLevel !== 'HIGH' && !== 'CRITICAL' \|\| humanApproved)` | +| 4 | implementing 開始 | `lock !== null && fileLocks に競合なし` | +| 5 | implementing 中 | `branchName matches /^(feature\|fix\|hotfix)\/issue-\d+-/` | +| 6 | implementing → reviewing | `prNumber > 0` | +| 7 | reviewing → done | `mergeCommit matches /^[0-9a-f]{40}$/` | +| 8 | done 後 | `worklog に記録済み && Issue が Closed` | + +**全 GATE は JSON フィールドの値チェック。LLM の自然言語判断は一切介在しない。** + +--- + +## 検証方法 + +1. **ユニットテスト**: 各 GATE の境界値テスト(vitest、既存テスト基盤を使用) +2. **統合テスト**: draft → done の全シーケンスを1タスクで通す +3. **競合テスト**: 2つのタスクが同じファイルをロックしようとして拒否されることを検証 +4. **DAG テスト**: 循環依存を検出して拒否、依存解決後に正しい順序でディスパッチ +5. **GitHub 同期テスト**: ローカル done ↔ GitHub Closed の整合性検証 +6. **TTL テスト**: ロック TTL 切れ時の自動解放と failed 遷移 + +```bash +cd Miyabi/packages/task-manager +npm test # 全テスト +npm run test -- --grep "deterministic" # プロトコルテストのみ +``` + +--- + +## 先行研究・設計根拠 + +- **Temporal.io / Durable Execution**: ワークフローの各ステップを永続化し、障害後も再開可能にする。tasks.json はこれのファイルベース簡易版。 +- **Kubernetes Controller Pattern**: Desired State(tasks.json)と Actual State(GitHub)の差分を検出して reconcile する。bidirectional-sync がこれに相当。 +- **Database MVCC / Pessimistic Locking**: ファイルロックは悲観的ロック。TTL はデッドロック防止。 +- **Apache Airflow DAG**: タスク間の依存関係をDAGで表現し、トポロジカルソートで実行順序を決定。task-dag.ts がこれに相当。 +- **Harness Engineering(docs/design/harness-engineering.md)**: 「制約をファイルに書き込み、会話ではなくハーネスで強制する」— 本プロトコルの思想的根拠。 + +--- + +## 実装順序とリスク + +| Phase | 工数目安 | リスク | 依存 | +|-------|---------|--------|------| +| 1. 型拡張 | 小 | LOW | なし | +| 2. DAG 移植 | 中 | LOW | Phase 1 | +| 3. ロック移植 | 中 | MEDIUM(TTL設計) | Phase 1 | +| 4. プロトコル | 大 | HIGH(核心) | Phase 1,2,3 | +| 5. TaskStore | 中 | LOW | Phase 1 | +| 6. GitHub同期強化 | 中 | MEDIUM(API制限) | Phase 4,5 | +| 7. CLI | 小 | LOW | Phase 4,5 | diff --git a/docs/dtp/PLAYBOOK.md b/docs/dtp/PLAYBOOK.md new file mode 100644 index 0000000..d632708 --- /dev/null +++ b/docs/dtp/PLAYBOOK.md @@ -0,0 +1,921 @@ +# Deterministic Task Execution Protocol — Maestro Implementation Playbook + +_Version: 0.1.0 | Created: 2026-04-10 | Status: PLAN (未実装)_ + +--- + +## 0. North Star + +> LLM の揺らぎを許さない。tasks.json の GATE が許可し、GitHub が確定し、git merge が不可逆にする。この三段階だけが「完了」を定義する。 + +--- + +## 1. 前提条件チェックリスト(実装開始前に全て GREEN であること) + +### 1.1 リポジトリ・インデックス + +| # | 条件 | 検証コマンド | 状態 | +|---|------|------------|------| +| 1 | openclaw-workspace GNI インデックス fresh | `npx gitnexus status --repo openclaw-workspace` | GREEN (50,130 nodes) | +| 2 | miyabi-private GNI インデックス fresh | `npx gitnexus status --repo miyabi-private` | GREEN (12,448 nodes) | +| 3 | rust-ai-pipeline GNI インデックス fresh | `npx gitnexus status --repo rust-ai-pipeline` | GREEN (813 nodes) | +| 4 | agent-skill-bus GNI インデックス fresh | `npx gitnexus status --repo agent-skill-bus` | GREEN (154 nodes) | + +### 1.2 既存テスト GREEN + +| # | パッケージ | コマンド | 状態 | +|---|-----------|---------|------| +| 5 | @miyabi/task-manager | `cd Miyabi/packages/task-manager && npm test` | 要確認 | +| 6 | rust-ai-pipeline | `cd ~/dev/products/rust-ai-pipeline && cargo test` | 要確認 | +| 7 | agent-skill-bus | `cd ~/dev/tools/agent-skill-bus && npm test` | 要確認 | + +### 1.3 Codex 3体レビュー反映 + +| # | 指摘 | 対応方針 | 反映先 Phase | +|---|------|---------|-------------| +| R1-1 | canTransition() が conditions を評価していない | Protocol を唯一の遷移窓口にする | Phase 2 | +| R1-2 | applyTransition() の結果が store に永続化されない | store-backed immutable update に統一 | Phase 3 | +| R1-3 | GATE が「存在チェック」で「出自検証」がない | GitHub API で PR/merge を実検証 | Phase 5 | +| R1-4 | reviewing → done が既存 27 ルールと衝突 | `merged` 状態を追加 | Phase 2 | +| R1-5 | TTL 7200s 固定は不適切 | lease 300s + heartbeat 60s に変更 | Phase 4 | +| R2-1 | tasks.json の read-modify-write がクロスマシン race | atomic write + CAS version check | Phase 3 | +| R2-2 | assignAndLock() の TOCTOU 脆弱性 | OS flock + re-read + re-check + write | Phase 4 | +| R2-3 | softDependencies を DAG level に入れると過度に直列化 | soft は dispatch priority にのみ使用 | Phase 2 | +| R2-4 | dagLevels キャッシュは永続化しない方が安全 | 読み込み時再計算に変更 | Phase 2 | +| R2-5 | JSONL event log + snapshot JSON が安全 | task-events.jsonl + tasks.snapshot.json 構成 | Phase 3 | +| R3-1 | tasks.json を事実の正に昇格させすぎ | execution ledger に下げ、SSOT は GitHub | Phase 5 | +| R3-2 | GitHub API 障害時の degraded mode 未定義 | awaiting_github_sync 状態 + retry queue | Phase 5 | +| R3-3 | Issue closed → done は危険 | PR merged + issue linked で確定 | Phase 5 | +| R3-4 | mergeCommit の取得経路が未仕様 | PR API の merge_commit_sha から取得 | Phase 5 | +| R3-5 | PRなし正当完了の escape hatch がない | completionMode = manual/github-pr/external-op | Phase 6 | + +--- + +## 2. アーキテクチャ概要 + +### 2.1 二層 SSOT + Execution Ledger + +``` ++------------------------------------------+ +| GitHub (Fact SSOT) | +| Issue 状態 / PR merged / Review status | ++-----+------------------------------------+ + | pull (authoritative facts) + | push (proposed state) + v ++------------------------------------------+ +| tasks.snapshot.json | +| (Execution Ledger / Gate Cache) | +| ← materialized view of event log | ++-----+------------------------------------+ + ^ + | replay / rebuild + | ++------------------------------------------+ +| task-events.jsonl | +| (Append-only Event Log) | +| state_transition / lock_acquired / | +| lock_released / dag_changed / | +| github_synced / gate_passed / | +| gate_rejected / human_approved | ++------------------------------------------+ +``` + +### 2.2 状態遷移図(Codex R1-4 反映: merged 追加) + +``` +[*] ──GATE 0──→ draft + │ + GATE 1 + │ + v + pending ←──── blocked (dep未解決) + │ ^ + GATE 2 │ + (deps done) │ + │ │ + v │ + analyzing ─────→ blocked (lock競合) + │ + GATE 3 + (impact + human approval) + │ + v + implementing ──── GATE 4 (lock + heartbeat) + │ + GATE 5+6 + (branch + PR) + │ + v + reviewing + │ + GATE 7 + (PR merged verified via GitHub API) + │ + v + merged ←── NEW STATE + │ + GATE 8 + (Issue closed + worklog) + │ + v + done +``` + +### 2.3 コンポーネントマップ(全既存資産の統合先) + +``` ++-----------------------------------------------------------------------+ +| DeterministicExecutionProtocol (NEW: ~200 lines) | +| 唯一の状態遷移窓口。全 GATE をここに集約。 | ++--+--+--+--+--+--+--+-------------------------------------------------+ + | | | | | | | + v v v v v v v ++------+ +------+ +------+ +------+ +------+ +------+ +------+ +|State | |DAG | |Lock | |Store | |Sync | |Impact| |Audit | +|Machin| |Engine| |Mgr | | | | | | | | | ++------+ +------+ +------+ +------+ +------+ +------+ +------+ + 既存 移植 統合 NEW 改造 NEW 既存 + Miyabi KOTOW miyabi events bidirec GNI→ skill- + task- ARI -priv .jsonl tional Task bus + mgr crowd lock.ts +snap -sync Impact record + +a-s-b +a-s-b shot adapter -run + queue queue +``` + +--- + +## 3. Phase 定義(全 8 Phase) + +### Phase 0: 前提条件の確定(実装前) + +**目的**: 全テスト GREEN、全インデックス fresh、Codex レビュー反映方針確定 + +**入力**: なし +**出力**: 上記チェックリスト全項目 GREEN + +**手順**: +1. `cd ~/dev/ops/openclaw-workspace/Miyabi/packages/task-manager && npm test` +2. `cd ~/dev/products/rust-ai-pipeline && cargo test` +3. `cd ~/dev/tools/agent-skill-bus && npm test` +4. 全テスト GREEN でなければ修正してから Phase 1 に進む + +**完了条件**: チェックリスト 1.1 + 1.2 の全項目 GREEN + +--- + +### Phase 1: 型定義の拡張 + +**目的**: ManagedTask に DAG/Lock/Impact/確定的参照フィールドを追加 + +**対象ファイル**: +- `Miyabi/packages/task-manager/src/types/task.ts` + +**追加する型**: + +```typescript +// --- NEW: ファイルロック --- +export interface TaskLock { + lockedBy: string; // "{agentName}@{nodeName}" + lockedAt: string; // ISO 8601 + leaseDurationSec: number; // R1-5: 固定 TTL ではなく lease + lastHeartbeat: string; // R1-5: heartbeat timestamp + affectedFiles: string[]; +} + +// --- NEW: Impact 記録 --- +export interface TaskImpact { + riskLevel: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'; + affectedSymbols: number; + depth1: string[]; + analyzedAt: string; + analyzedCommit: string; // R1-3: 分析時の HEAD commit + inputHash: string; // R1-3: 対象ファイルセットのハッシュ +} + +// --- NEW: GitHub 証跡 --- +export interface GitHubEvidence { + prNumber: number; + prHeadRef: string; // R1-3: PR の head branch + prState: 'OPEN' | 'MERGED' | 'CLOSED'; + mergeCommitSha: string | null; + mergedAt: string | null; + reviewDecision: 'APPROVED' | 'CHANGES_REQUESTED' | 'REVIEW_REQUIRED' | null; + issueState: 'OPEN' | 'CLOSED'; + issueClosedByPR: boolean; +} + +// --- NEW: 完了モード (R3-5) --- +export type CompletionMode = 'github-pr' | 'manual' | 'external-op'; + +// --- TaskState 拡張 (R1-4: merged 追加) --- +export type TaskState = + | 'draft' | 'pending' | 'analyzing' | 'implementing' + | 'reviewing' | 'merged' // ← NEW + | 'deploying' | 'done' + | 'blocked' | 'failed' | 'cancelled' + | 'awaiting_github_sync'; // ← NEW (R3-2) +``` + +**ManagedTask への追加フィールド**: + +```typescript +export interface ManagedTask extends BaseTask { + // ...既存フィールド... + + // DAG (R2-3: soft は別扱い) + dependents: string[]; + softDependencies: string[]; + + // Lock + lock: TaskLock | null; + + // Impact + impact: TaskImpact | null; + + // 確定的参照 + branchName: string | null; + githubEvidence: GitHubEvidence | null; // R1-3: 単なる prNumber/mergeCommit ではなく検証済み証跡 + + // 完了モード (R3-5) + completionMode: CompletionMode; + + // 人間承認 (R1-3) + humanApproval: { + required: boolean; + approvedBy: string | null; + approvedAt: string | null; + reason: string | null; + } | null; +} +``` + +**createManagedTask() の更新**: 新フィールドのデフォルト値追加 + +**テスト**: 既存テストが壊れないことを確認 + 新フィールドの存在テスト + +**Rust 連携**: `rust-ai-pipeline/src/pipeline.rs` の `PipelineReport` 構造体を参考に、`StepResult` パターン(name/success/duration/stdout/stderr)を GATE の通過記録に流用 + +**完了条件**: `npm test` GREEN、新型が compile 通る + +--- + +### Phase 2: ステートマシン拡張 + DAG 統合 + +**目的**: +- `merged` 状態と `awaiting_github_sync` 状態を追加 +- conditions を実評価するように変更 +- DAG エンジンを移植・統合 + +**対象ファイル**: +- `Miyabi/packages/task-manager/src/state/task-state-machine.ts`(改造) +- `Miyabi/packages/task-manager/src/dag/task-dag.ts`(NEW: 移植元 KOTOWARI) + +#### 2.1 ステートマシン改造 + +**R1-1 対応**: `conditions` を文字列配列から **predicate 関数** に変更 + +```typescript +export interface StateTransitionRule { + from: TaskState; + to: TaskState; + predicates?: ((task: ManagedTask, context: TransitionContext) => boolean)[]; + sideEffects?: string[]; +} + +export interface TransitionContext { + store: TaskStore; // 他タスクの状態を参照するため + triggeredBy: 'user' | 'agent' | 'system' | 'github-webhook'; + reason?: string; + humanApproval?: boolean; +} +``` + +**追加する遷移ルール**: + +```typescript +// merged (NEW) +{ from: 'reviewing', to: 'merged', predicates: [ + (task) => task.githubEvidence !== null, + (task) => task.githubEvidence?.prState === 'MERGED', + (task) => task.githubEvidence?.mergeCommitSha !== null, + (task) => /^[0-9a-f]{40}$/.test(task.githubEvidence?.mergeCommitSha ?? ''), +]}, +{ from: 'merged', to: 'done', predicates: [ + (task) => task.githubEvidence?.issueState === 'CLOSED', +]}, + +// awaiting_github_sync (NEW, R3-2) +{ from: 'reviewing', to: 'awaiting_github_sync', predicates: [] }, +{ from: 'merged', to: 'awaiting_github_sync', predicates: [] }, +{ from: 'awaiting_github_sync', to: 'merged', predicates: [ + (task) => task.githubEvidence !== null, +]}, +{ from: 'awaiting_github_sync', to: 'reviewing', predicates: [] }, + +// skip_analysis を削除 (R1-7 対応) +// { from: 'pending', to: 'implementing' } ← 削除 +``` + +**R1-2 対応**: `applyTransition()` を store-backed に変更 + +```typescript +applyTransition( + task: ManagedTask, + to: TaskState, + context: TransitionContext +): TransitionResult & { task?: ManagedTask } { + // 1. predicates を全て評価 + const rule = this.getTransitionRule(task.currentState, to); + if (!rule) return { valid: false, ... }; + + for (const pred of rule.predicates ?? []) { + if (!pred(task, context)) { + return { valid: false, error: `Predicate failed for ${task.currentState} → ${to}` }; + } + } + + // 2. 新 task を生成 + const updatedTask = { ...task, currentState: to, stateHistory: [...] }; + + // 3. store に即座に永続化(R1-2) + context.store.updateTask(task.id, updatedTask); + + return { valid: true, task: updatedTask }; +} +``` + +#### 2.2 DAG エンジン移植 + +**移植元**: `KOTOWARI/skills/openclaw-crowd/src/scheduling/task-dependency-graph.ts` + +**新ファイル**: `Miyabi/packages/task-manager/src/dag/task-dag.ts` + +**変更点**: +- `ScheduledTask` → `ManagedTask` 型変換 +- `hard`/`soft` を `dependencies`/`softDependencies` にマップ +- R2-3: soft deps は level 計算に含めない(dispatch priority のみ) +- R2-4: `dagLevels` は永続化せず、`computeLevels()` で毎回再計算 +- `getReadyTasks(store)`: `dependencies` が全て `done` or `merged` のタスクを返す +- `getDispatchScore(task)`: priority × age × softSatisfied × lockAvailability の合成スコア + +**テスト**: KOTOWARI の既存テストを移植 + ManagedTask 型でのテスト追加 + +**完了条件**: DAG テスト GREEN、循環検出テスト GREEN、soft deps が level に影響しないことのテスト GREEN + +--- + +### Phase 3: Event Store(JSONL + Snapshot) + +**目的**: R2-1/R2-5 対応。tasks.json を monolithic truth から event log + snapshot に分離 + +**新ファイル**: +- `Miyabi/packages/task-manager/src/store/event-store.ts` +- `Miyabi/packages/task-manager/src/store/snapshot-store.ts` + +#### 3.1 Event Store + +```typescript +export interface TaskEvent { + id: string; // ascending ID + ts: string; // ISO 8601 + type: 'state_transition' | 'lock_acquired' | 'lock_released' | 'lock_heartbeat' + | 'dag_changed' | 'github_synced' | 'gate_passed' | 'gate_rejected' + | 'human_approved' | 'impact_recorded' | 'branch_created' | 'pr_created' + | 'merge_verified' | 'audit_recorded'; + taskId: string; + agent: string; + node: string; + payload: Record; + version: number; // R2-1: CAS 用バージョン +} + +export class EventStore { + private filePath: string; // project_memory/task-events.jsonl + + append(event: TaskEvent): void; // append-only + replay(since?: string): TaskEvent[]; // 全イベント再生 + replayForTask(taskId: string): TaskEvent[]; +} +``` + +#### 3.2 Snapshot Store + +```typescript +export interface TasksSnapshot { + version: number; // R2-1: CAS バージョン + generatedAt: string; + generatedFromEventId: string; // どこまで replay したか + tasks: ManagedTask[]; + fileLocks: Record; +} + +export class SnapshotStore { + private filePath: string; // project_memory/tasks.snapshot.json + private lockFilePath: string; // project_memory/.tasks.lock + + // R2-1: atomic write with OS flock + CAS + load(): TasksSnapshot; + save(snapshot: TasksSnapshot, expectedVersion: number): void; // version mismatch → throw + rebuild(eventStore: EventStore): TasksSnapshot; // event log から再構築 +} +``` + +**R2-1 対応: atomic write の実装**: + +```typescript +save(snapshot: TasksSnapshot, expectedVersion: number): void { + // 1. OS flock を取得(fd-level) + const lockFd = fs.openSync(this.lockFilePath, 'w'); + flock(lockFd, LOCK_EX); // blocking + + try { + // 2. 現在の version を再読込(CAS) + const current = this.load(); + if (current.version !== expectedVersion) { + throw new Error(`CAS conflict: expected v${expectedVersion}, got v${current.version}`); + } + + // 3. atomic rename で書き込み + const tmpPath = this.filePath + '.tmp'; + fs.writeFileSync(tmpPath, JSON.stringify({ ...snapshot, version: expectedVersion + 1 }, null, 2)); + fs.renameSync(tmpPath, this.filePath); + } finally { + flock(lockFd, LOCK_UN); + fs.closeSync(lockFd); + } +} +``` + +**Rust 連携**: `rust-ai-pipeline` の `PipelineReport` の JSON シリアライズパターン(`serde::Serialize`)を参考に、event の JSON 形式を統一。将来的に Rust 側で event log を消費する際に互換性を持たせる。 + +**完了条件**: event append テスト、snapshot rebuild テスト、CAS conflict テスト、concurrent write テスト(fork して同時書き込み) + +--- + +### Phase 4: File Lock Manager(lease + heartbeat) + +**目的**: R1-5/R2-2 対応。固定 TTL → renewable lease に変更 + +**新ファイル**: `Miyabi/packages/task-manager/src/lock/file-lock-manager.ts` + +**移植元**: +- `miyabi-private/packages/miyabi/src/util/lock.ts`(RWLock パターン) +- `agent-skill-bus/src/queue.js`(TTL + 競合チェック) + +```typescript +export interface LeaseConfig { + leaseDurationSec: number; // default: 300 + heartbeatIntervalSec: number; // default: 60 + staleAfterMissedHeartbeats: number; // default: 2 +} + +export class FileLockManager { + constructor( + private eventStore: EventStore, + private snapshotStore: SnapshotStore, + private config: LeaseConfig + ) {} + + // R2-2: 原子的な lock 獲得 + acquireLock(taskId: string, agent: string, node: string, files: string[]): void { + // 1. OS flock で snapshot をロック + // 2. snapshot を再読込 + // 3. 競合チェック(files が他タスクにロック中か) + // 4. stale lock の自動解放(heartbeat 2回 miss) + // 5. lock 書き込み + event 記録 + // 6. OS flock 解放 + } + + // heartbeat(lease 更新) + renewLease(taskId: string, agent: string, node: string): void { + // event: lock_heartbeat を append + // snapshot の lastHeartbeat を更新 + } + + // 解放 + releaseLock(taskId: string): void { + // event: lock_released を append + // snapshot から fileLock エントリを削除 + } + + // stale 検出 + releaseExpiredLeases(): { released: string[]; active: string[] } { + // lastHeartbeat + leaseDuration + staleAfterMissed × heartbeatInterval < now → stale + } + + // 競合チェック + hasConflict(files: string[]): { conflicting: boolean; heldBy?: string; taskId?: string } { + // snapshot の fileLocks をチェック + } +} +``` + +**Rust 連携**: `rust-ai-pipeline/src/signal.rs` の `Signal.is_fresh(max_age_ms)` パターンを heartbeat の鮮度判定に流用。同じ「timestamp + threshold → fresh/stale」の設計思想。 + +**テスト**: +- lease 獲得 → heartbeat → 解放 のライフサイクル +- 競合検出(同一ファイルに2タスク) +- stale 検出(heartbeat なしで lease 期限切れ) +- concurrent acquire(2プロセスが同時に acquireLock)→ 1つだけ成功 + +**完了条件**: 全テスト GREEN + +--- + +### Phase 5: GitHub 同期の再設計 + +**目的**: R3-1/R3-2/R3-3/R3-4 対応。二層 SSOT を正しく実装 + +**対象ファイル**: +- `Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts`(大幅改造) +- `Miyabi/packages/task-manager/src/sync/github-evidence-fetcher.ts`(NEW) + +#### 5.1 GitHub Evidence Fetcher + +```typescript +export class GitHubEvidenceFetcher { + constructor(private octokit: Octokit, private owner: string, private repo: string) {} + + // R3-4: PR API から merge commit を取得 + async fetchPREvidence(prNumber: number): Promise { + const { data: pr } = await this.octokit.pulls.get({ + owner: this.owner, repo: this.repo, pull_number: prNumber + }); + return { + prNumber, + prHeadRef: pr.head.ref, + prState: pr.merged ? 'MERGED' : pr.state === 'closed' ? 'CLOSED' : 'OPEN', + mergeCommitSha: pr.merge_commit_sha, + mergedAt: pr.merged_at, + reviewDecision: await this.fetchReviewDecision(prNumber), + issueState: await this.fetchLinkedIssueState(prNumber), + issueClosedByPR: await this.checkIssueClosedByPR(prNumber), + }; + } + + // R3-3: Issue が PR merge で close されたか検証 + private async checkIssueClosedByPR(prNumber: number): Promise { + // GitHub GraphQL: pullRequest.closingIssuesReferences + } +} +``` + +#### 5.2 同期の再設計(R3-1: authority-aware bidirectional) + +```typescript +export type SyncDirection = + | 'push_proposal' // ローカル → GitHub(提案) + | 'pull_authoritative'; // GitHub → ローカル(事実の取得) + +export class DeterministicSync { + // R3-1: 何の事実はどちらが authoritative かを状態ごとに定義 + private authority: Record = { + 'taskCompletion': 'github', // done/merged は GitHub が authority + 'executionLock': 'local', // lock/DAG はローカルが authority + 'reviewStatus': 'github', // review 結果は GitHub が authority + 'assignedAgent': 'local', // アサインはローカルが authority + }; + + // R3-2: GitHub API 障害時 + async syncWithDegradedMode(tasks: ManagedTask[]): Promise { + try { + return await this.fullSync(tasks); + } catch (error) { + if (this.isGitHubAPIError(error)) { + // awaiting_github_sync に遷移 + // retry queue に追加 + // event: github_sync_failed を記録 + return { degraded: true, retryAt: new Date(Date.now() + 60000) }; + } + throw error; + } + } +} +``` + +**Rust 連携**: `rust-ai-pipeline/src/remote.rs` の `ssh_exec()` + `RemoteResult` パターンを GitHub API 呼び出しの結果型に流用。success/stdout/stderr の三つ組は `GitHubAPIResult` にそのまま適用可能。 + +**テスト**: +- PR merged → evidence 取得 → merged 遷移 +- Issue close by non-PR → done に遷移しないことを検証 +- GitHub API 障害 → awaiting_github_sync 遷移 +- retry 後の復旧 + +**完了条件**: 全テスト GREEN + +--- + +### Phase 6: DeterministicExecutionProtocol(核心) + +**目的**: 全 GATE を1つのクラスに集約。唯一の状態遷移窓口。 + +**新ファイル**: `Miyabi/packages/task-manager/src/protocol/deterministic-execution.ts` + +**依存**: Phase 1-5 の全コンポーネント + +```typescript +export class DeterministicExecutionProtocol { + private stateMachine: TaskStateMachine; // Phase 2 + private dag: TaskDAG; // Phase 2 + private lockManager: FileLockManager; // Phase 4 + private eventStore: EventStore; // Phase 3 + private snapshotStore: SnapshotStore; // Phase 3 + private sync: DeterministicSync; // Phase 5 + private evidenceFetcher: GitHubEvidenceFetcher; // Phase 5 + + // ========================================== + // GATE 0: タスク登録(Issue 必須) + // ========================================== + registerTask(input: CreateTaskInput & { + githubIssueNumber: number; + completionMode: CompletionMode; + }): ManagedTask { + // GATE: githubIssueNumber > 0 + // GATE: completionMode は明示的に指定 + // EVENT: state_transition (→ draft) + // STORE: snapshot に追加 + } + + // ========================================== + // GATE 1: DAG 登録(循環依存拒否) + // ========================================== + addToDAG(taskId: string, dependencies: string[], softDependencies: string[]): void { + // GATE: dag.detectCycle() === null + // GATE: dependencies は全て既存タスク ID + // EVENT: dag_changed + } + + // ========================================== + // GATE 2: 依存解決チェック + // ========================================== + checkDependencies(taskId: string): 'ready' | 'blocked' { + // GATE: dependencies.every(d => ['done', 'merged'].includes(store.get(d).currentState)) + // blocked → pending は dep 解決後に自動遷移 + // EVENT: state_transition (pending → analyzing) or (pending → blocked) + } + + // ========================================== + // GATE 3: Impact 分析記録 + // ========================================== + recordImpact(taskId: string, impact: TaskImpact): void { + // GATE: impact.analyzedCommit === current HEAD + // GATE: impact.inputHash matches current file set + // GATE: HIGH/CRITICAL → humanApproval.required = true + // EVENT: impact_recorded + } + + // ========================================== + // GATE 3.5: 人間承認(HIGH/CRITICAL のみ) + // ========================================== + recordHumanApproval(taskId: string, approvedBy: string, reason: string): void { + // GATE: task.impact.riskLevel in ['HIGH', 'CRITICAL'] + // GATE: task.humanApproval.required === true + // EVENT: human_approved + } + + // ========================================== + // GATE 4: アサイン + ロック獲得(原子的) + // ========================================== + assignAndLock(taskId: string, agent: string, node: string, files: string[]): void { + // GATE: impact !== null + // GATE: HIGH/CRITICAL → humanApproval.approvedAt !== null + // GATE: lockManager.hasConflict(files) === false + // ATOMIC: OS flock → re-read → re-check → write → release + // EVENT: lock_acquired + // EVENT: state_transition (analyzing → implementing) + } + + // ========================================== + // Heartbeat(実装中に定期実行) + // ========================================== + heartbeat(taskId: string, agent: string, node: string): void { + // GATE: task.lock.lockedBy === `${agent}@${node}` + // EVENT: lock_heartbeat + } + + // ========================================== + // GATE 5: ブランチ記録 + // ========================================== + recordBranch(taskId: string, branchName: string): void { + // GATE: /^(feature|fix|hotfix)\/issue-\d+-/.test(branchName) + // GATE: git branch --list で実在確認 + // EVENT: branch_created + } + + // ========================================== + // GATE 6: PR 記録 + // ========================================== + async recordPR(taskId: string, prNumber: number): Promise { + // GATE: prNumber > 0 + // GATE: branchName !== null + // VERIFY: evidenceFetcher.fetchPREvidence(prNumber) + // GATE: evidence.prHeadRef === task.branchName + // GATE: evidence.prState === 'OPEN' + // EVENT: pr_created + // EVENT: state_transition (implementing → reviewing) + } + + // ========================================== + // GATE 7: Merge 検証 + // ========================================== + async verifyMerge(taskId: string): Promise { + // FETCH: evidenceFetcher.fetchPREvidence(task.githubEvidence.prNumber) + // GATE: evidence.prState === 'MERGED' + // GATE: evidence.mergeCommitSha matches /^[0-9a-f]{40}$/ + // GATE: evidence.reviewDecision === 'APPROVED' + // EVENT: merge_verified + // EVENT: state_transition (reviewing → merged) + // EFFECT: lockManager.releaseLock(taskId) + // EFFECT: dependents の blocked → pending 遷移 + } + + // ========================================== + // GATE 8: 完了確定 + // ========================================== + async confirmDone(taskId: string): Promise { + // FETCH: evidence.issueState + // GATE: evidence.issueClosedByPR === true (github-pr mode) + // OR: completionMode === 'manual' + humanApproval + // OR: completionMode === 'external-op' + humanApproval + // EVENT: state_transition (merged → done) + // EVENT: audit_recorded + // EFFECT: worklog に記録 + // EFFECT: agent-skill-bus に record-run + } + + // ========================================== + // Escape Hatches(R3-5: 監査可能な例外経路) + // ========================================== + forceUnlock(taskId: string, reason: string, operator: string): void { + // EVENT: lock_released (forced=true, reason, operator) + } + + reconcileFromGitHub(taskId: string): Promise { + // GitHub の状態を pull して snapshot を上書き + // EVENT: github_synced (reconcile) + } + + manualComplete(taskId: string, reason: string, operator: string): void { + // GATE: completionMode === 'manual' or 'external-op' + // EVENT: state_transition (→ done) + human_approved + } +} +``` + +**Rust 連携**: `rust-ai-pipeline` の Phase 1 パイプライン(build → clippy → test、最初の失敗で打ち切り)と同じパターン。GATE 0 → 1 → ... → 8 を順に実行し、最初の GATE 失敗で打ち切る。`PipelineReport` の `steps[]` と `failure_kind` をそのまま GATE の通過/拒否記録に流用。 + +将来的には、Rust で Phase 1 パイプライン(build/clippy/test)を GATE 4.5 として組み込み、「コンパイルが通らないコードは implementing → reviewing に遷移できない」という品質ゲートにする。 + +**テスト**: +- happy path: draft → ... → done の全シーケンス +- GATE 拒否: 各 GATE で条件不足 → 遷移不可 +- 競合: 2タスクが同じファイルをロック → 1つだけ成功 +- escape hatch: forceUnlock → 理由記録 +- GitHub 障害: verifyMerge → awaiting_github_sync → retry → merged + +**完了条件**: 全テスト GREEN + +--- + +### Phase 7: CLI コマンド + +**目的**: Protocol を CLI から操作可能にする + +**新ファイル**: `Miyabi/packages/task-manager/src/cli/deterministic-cli.ts` + +```bash +miyabi task register --issue 45 --title "JWT移行" --deps task-000 --mode github-pr +miyabi task dag # DAG 可視化(ターミナル) +miyabi task dispatchable # 実行可能タスク一覧 +miyabi task assign task-001 --agent kotowari-dev --node macbook-pro --files "src/auth/*.ts" +miyabi task heartbeat task-001 # lease 更新 +miyabi task status [task-id] # 状態確認(JSON or human-readable) +miyabi task locks # ロック一覧 +miyabi task unlock task-001 --reason "agent crashed" --operator hayashi +miyabi task verify-merge task-001 # GitHub から merge 証跡を取得 +miyabi task confirm-done task-001 # 完了確定 +miyabi task sync # GitHub 同期 +miyabi task events [task-id] # event log 表示 +miyabi task rebuild-snapshot # snapshot を event log から再構築 +miyabi task reconcile task-001 # GitHub から強制同期 +``` + +**Rust 連携**: `rust-ai-pipeline` の CLI パターン(`parse_cli_args()` → `CliMode` enum → `run_*()` 関数)を参考に、サブコマンドを enum で定義。`ensure_cargo_project()` のような前提条件チェックを各コマンドの冒頭に配置。 + +**完了条件**: 全サブコマンドのヘルプ表示 + register → assign → verify-merge → confirm-done の E2E テスト + +--- + +### Phase 8: 統合テスト + Agent Skill Bus 記録 + +**目的**: 全 Phase を結合して1タスクの draft → done を通す + +**テストシナリオ**: + +``` +1. gh issue create → Issue #N +2. miyabi task register --issue N --title "test" --mode github-pr +3. miyabi task dag → Level 0 に task 表示 +4. miyabi task dispatchable → task が ready +5. GNI impact 実行 → miyabi task record-impact +6. miyabi task assign --agent test --node local --files "src/test.ts" +7. git checkout -b feature/issue-N-test +8. miyabi task record-branch feature/issue-N-test +9. echo "// test" >> src/test.ts && git commit +10. gh pr create → PR #M +11. miyabi task record-pr M +12. gh pr merge M +13. miyabi task verify-merge +14. miyabi task confirm-done +15. miyabi task status → done ✅ +16. gh issue view N → Closed ✅ +17. npx agent-skill-bus record-run → recorded ✅ +``` + +**rust-ai-pipeline 統合テスト(将来)**: + +``` +Step 6.5: ai-pipeline phase1 --project . --format json + → all_passed === true なら GATE 4.5 通過 + → failure_kind !== null なら implementing に留まる +``` + +**完了条件**: E2E テスト GREEN、event log に全ステップの記録あり、snapshot が正しく再構築可能 + +--- + +## 4. 実装順序と依存関係 DAG + +``` +Phase 0 ──→ Phase 1 ──→ Phase 2 ──→ Phase 3 ──→ Phase 4 + │ │ + └─────┬─────┘ + │ + v + Phase 5 ──→ Phase 6 ──→ Phase 7 ──→ Phase 8 +``` + +| Phase | 依存 | 新規コード量 | リスク | +|-------|------|-------------|--------| +| 0 | なし | 0 | LOW | +| 1 | 0 | ~80 行 | LOW | +| 2 | 1 | ~250 行 | MEDIUM(ステートマシン改造) | +| 3 | 1 | ~200 行 | MEDIUM(atomic write) | +| 4 | 3 | ~150 行 | MEDIUM(lease + heartbeat) | +| 5 | 3 | ~200 行 | HIGH(GitHub API 依存) | +| 6 | 2,3,4,5 | ~300 行 | HIGH(核心の統合) | +| 7 | 6 | ~150 行 | LOW(CLI ボイラープレート) | +| 8 | 7 | ~100 行 | LOW(E2E テスト) | +| **合計** | | **~1,430 行** | | + +--- + +## 5. エージェント割り当て(Maestro Playbook 実行時) + +| Phase | 推奨エージェント | 理由 | +|-------|---------------|------| +| 0 | Claude Code(ローカル) | テスト実行 + インデックス確認 | +| 1 | Codex(worktree) | 型定義のみ、副作用なし | +| 2 | Claude Code(ローカル) | ステートマシン改造は対話が必要 | +| 3 | Codex(worktree) | event store は独立実装可能 | +| 4 | Codex(worktree) | lock manager も独立実装可能 | +| 5 | Claude Code(ローカル) | GitHub API の挙動確認が必要 | +| 6 | Claude Code(ローカル) | 全コンポーネント統合は対話が必要 | +| 7 | Codex(worktree) | CLI ボイラープレート | +| 8 | Claude Code(ローカル) | E2E テストは実環境が必要 | + +**並列実行可能**: +- Phase 3 と Phase 4 は同時実行可能(Codex 2体) +- Phase 2 は Phase 3/4 と並行可能(依存は Phase 1 のみ) + +--- + +## 6. リスク軽減策 + +| リスク | 軽減策 | +|--------|--------| +| ステートマシン改造で既存テストが壊れる | Phase 2 開始前に既存テストを全部 GREEN にする | +| atomic write の OS flock がクロスプラットフォームで動かない | macOS + Linux でテスト。Windows は SMB 経由のため advisory lock は非保証 → 単一 writer に fallback | +| GitHub API レート制限 | secondary rate limit 対応の exponential backoff + 5000 req/hr の予算管理 | +| event log が肥大化 | 30日以上前の event は archive + snapshot rebuild で圧縮 | +| Rust pipeline 統合が遅れる | Phase 6 の GATE 4.5 は optional。なくても draft → done は通る | + +--- + +## 7. 検証基準(全 Phase 完了後) + +| # | 検証項目 | 方法 | +|---|---------|------| +| 1 | 全 GATE が条件不足で拒否する | 各 GATE の境界値ユニットテスト | +| 2 | LLM が GATE をバイパスできない | Protocol 以外の経路で applyTransition を呼ぶテスト → 失敗 | +| 3 | 2エージェントが同一ファイルをロックできない | concurrent acquire テスト | +| 4 | 依存未解決タスクが analyzing に入れない | DAG + ステートマシン結合テスト | +| 5 | merge commit なしに done に遷移できない | GATE 7 のユニットテスト | +| 6 | GitHub 障害時に安全に停止する | mock API + awaiting_github_sync テスト | +| 7 | event log から snapshot を正確に再構築できる | rebuild + diff テスト | +| 8 | E2E: draft → done の全シーケンス | Phase 8 の統合テスト | + +--- + +_This playbook is the single source of truth for implementation. Do not start coding until Phase 0 is GREEN._ diff --git a/docs/dtp/UML.md b/docs/dtp/UML.md new file mode 100644 index 0000000..4a5d0c1 --- /dev/null +++ b/docs/dtp/UML.md @@ -0,0 +1,427 @@ +# Deterministic Task Execution Protocol — UML Diagrams + +--- + +## 1. As-Is vs To-Be: タスク完了までのシーケンス比較 + +### As-Is(現状: LLM の自己申告ベース) + +```mermaid +sequenceDiagram + participant H as Human + participant LLM as Agent (LLM) + participant GH as GitHub + + H->>LLM: 「認証機能を直して」 + LLM->>LLM: (考える) + LLM->>LLM: ファイルを編集 + Note over LLM: impact分析なし + Note over LLM: ロックなし + Note over LLM: 別エージェントも同時編集可能 + LLM-->>H: 「完了しました!」 + + H->>GH: Issue確認 + Note over GH: Issue: Open ❌ + Note over GH: PR: なし ❌ + Note over GH: merge: なし ❌ + + H-->>LLM: 「完了してないじゃん」 + LLM->>LLM: (やり直し...) +``` + +### To-Be(実装後: GATE による確定的制御) + +```mermaid +sequenceDiagram + participant H as Human + participant P as Protocol (tasks.json) + participant LLM as Agent (LLM) + participant GNI as GitNexus + participant GH as GitHub + + H->>GH: Issue #45 作成 + H->>P: registerTask(issue=45) + P->>P: GATE 0: issue存在? ✅ + + P->>P: GATE 2: 依存解決? ✅ + P->>P: draft → pending → analyzing + + LLM->>GNI: gitnexus_impact("AuthController") + GNI-->>P: {riskLevel: HIGH, symbols: 12} + P->>P: GATE 3: impact記録済 ✅ + P->>H: ⚠️ HIGH risk — 承認必要 + H-->>P: 承認 + + P->>P: GATE 4: ファイルロック競合なし? ✅ + P->>P: lock獲得 + analyzing → implementing + + LLM->>LLM: ブランチ作成 + コード修正 + LLM->>P: recordBranch("feature/issue-45-auth") + P->>P: GATE 5: ブランチ名検証 ✅ + + LLM->>GH: gh pr create → PR #78 + LLM->>P: recordPR(78) + P->>P: GATE 6: prNumber > 0 ✅ + P->>P: implementing → reviewing + + H->>GH: Review → Approve → Merge + GH-->>P: mergeCommit = "a1b2c3d4..." + P->>P: GATE 7: 40文字hex ✅ + P->>P: reviewing → done + P->>P: ロック解放 + 後続タスク解放 + P->>GH: Issue #45 → Closed ✅ +``` + +--- + +## 2. ステートマシン: 既存 vs 拡張 + +### As-Is(既存: 条件チェックなし) + +```mermaid +stateDiagram-v2 + [*] --> draft + draft --> pending + pending --> analyzing + pending --> implementing: skip_analysis + analyzing --> implementing + implementing --> reviewing + reviewing --> done + reviewing --> implementing: needs_revision + + note right of pending: 依存チェックなし\n誰でも遷移可能 + note right of analyzing: impact なしでも\nimplementingに行ける + note right of reviewing: PRなくても\ndoneに行ける +``` + +### To-Be(拡張: 全遷移に GATE) + +```mermaid +stateDiagram-v2 + [*] --> draft: GATE 0\ngithubIssueNumber > 0 + + draft --> pending: GATE 1\ntitle + description + deps[] + + pending --> analyzing: GATE 2\ndeps.every(done) + pending --> blocked: deps未解決 + + analyzing --> implementing: GATE 3 + 4\nimpact != null\nlock獲得済\nHIGH→人間承認 + analyzing --> blocked: ロック競合 + + implementing --> reviewing: GATE 5 + 6\nbranchName valid\nprNumber > 0 + + reviewing --> done: GATE 7\nmergeCommit = /[0-9a-f]{40}/ + + done --> [*]: GATE 8\nworklog記録 + Issue Closed + + blocked --> pending: 依存解決 or ロック解放 + + note right of draft: Issue がなければ\n登録すら不可能 + note right of analyzing: GNI分析が空なら\n実装に入れない + note right of implementing: ロックがなければ\nコード編集不可 + note right of reviewing: merge commitなしに\n完了は不可能 +``` + +--- + +## 3. ファイルロック: 競合防止の仕組み + +### As-Is(ロックなし) + +```mermaid +sequenceDiagram + participant A as Agent A (MacBook) + participant F as auth.controller.ts + participant B as Agent B (Windows) + + A->>F: 編集開始 + B->>F: 編集開始(同時) + Note over F: 💥 競合! + A->>F: コミット + B->>F: コミット(上書き) + Note over F: Agent A の変更が消失 +``` + +### To-Be(ファイルロック付き) + +```mermaid +sequenceDiagram + participant A as Agent A (MacBook) + participant T as tasks.json + participant B as Agent B (Windows) + participant F as auth.controller.ts + + A->>T: assignAndLock(task-001, files=[auth.controller.ts]) + T->>T: fileLocks["auth.controller.ts"] = "task-001" ✅ + T-->>A: ロック獲得OK + + B->>T: assignAndLock(task-002, files=[auth.controller.ts]) + T->>T: fileLocks["auth.controller.ts"] 既にロック中 ❌ + T-->>B: Error: "task-001がロック中" + + A->>F: 編集 → commit → PR → merge + A->>T: recordMerge(task-001, "a1b2c3...") + T->>T: fileLocks["auth.controller.ts"] 解放 + + B->>T: assignAndLock(task-002, files=[auth.controller.ts]) + T->>T: fileLocks 空 ✅ + T-->>B: ロック獲得OK + B->>F: 編集開始(安全) +``` + +--- + +## 4. DAG 依存関係: 実行順序の強制 + +```mermaid +graph TD + subgraph "DAG Level 0(最初に実行)" + T0[task-000
DBスキーマ変更
state: done ✅] + end + + subgraph "DAG Level 1(Level 0 完了後)" + T1[task-001
API実装
state: implementing 🔒] + end + + subgraph "DAG Level 2(Level 1 完了後)" + T2[task-002
フロントエンド
state: blocked ⏳] + T3[task-003
テスト作成
state: blocked ⏳] + end + + T0 -->|"hard dep"| T1 + T1 -->|"hard dep"| T2 + T1 -->|"hard dep"| T3 + + style T0 fill:#22c55e,color:#fff + style T1 fill:#3b82f6,color:#fff + style T2 fill:#f59e0b,color:#fff + style T3 fill:#f59e0b,color:#fff +``` + +```mermaid +sequenceDiagram + participant DAG as DAG Engine + participant SM as State Machine + participant T0 as task-000 (DB) + participant T1 as task-001 (API) + participant T2 as task-002 (Frontend) + + Note over DAG: Level 0 実行 + T0->>SM: pending → implementing → done ✅ + + DAG->>DAG: task-000 done → Level 1 解放 + + Note over DAG: Level 1 実行 + T1->>SM: blocked → pending(依存解決) + T1->>SM: pending → analyzing → implementing + + Note over T2: task-001 がまだ done じゃない + T2->>SM: pending → analyzing を試みる + SM-->>T2: ❌ GATE 2 拒否: task-001 が done ではない + + T1->>SM: implementing → reviewing → done ✅ + DAG->>DAG: task-001 done → Level 2 解放 + + Note over DAG: Level 2 実行(並列可能) + T2->>SM: blocked → pending ✅ +``` + +--- + +## 5. 全体アーキテクチャ: 3ピース統合 + +### As-Is(分散・未接続) + +```mermaid +graph LR + subgraph "Miyabi task-manager" + SM[State Machine
27 rules] + SYNC[GitHub Sync
bidirectional] + EXEC[Task Executor
worktree] + end + + subgraph "KOTOWARI openclaw-crowd" + DAG[DAG Engine
topological sort] + SCHED[Scheduler
priority queue] + end + + subgraph "agent-skill-bus" + QUEUE[JSONL Queue
prompt requests] + LOCK[File Locks
TTL-based] + end + + SM -.->|"未接続"| DAG + SM -.->|"未接続"| LOCK + DAG -.->|"未接続"| LOCK + + style SM fill:#ef4444,color:#fff + style DAG fill:#ef4444,color:#fff + style LOCK fill:#ef4444,color:#fff +``` + +### To-Be(統合: DeterministicExecutionProtocol) + +```mermaid +graph TB + subgraph "DeterministicExecutionProtocol" + direction TB + + subgraph "GATE Layer(門番)" + G0[GATE 0: Issue存在] + G2[GATE 2: 依存解決] + G3[GATE 3: Impact記録] + G4[GATE 4: ロック獲得] + G6[GATE 6: PR作成] + G7[GATE 7: Merge確定] + end + + subgraph "Engine Layer(実行)" + SM[State Machine
27 rules + GATE条件] + DAG[DAG Engine
Kahn sort + 依存解決] + LOCK[File Lock Manager
TTL + 競合検出] + end + + subgraph "Store Layer(永続化)" + TJ[tasks.json
確定的スキーマ] + GH[GitHub Issues/PR
Fact SSOT] + end + end + + G0 --> SM + G2 --> DAG + G3 --> SM + G4 --> LOCK + G6 --> SM + G7 --> SM + + SM --> TJ + DAG --> TJ + LOCK --> TJ + TJ <-->|"bidirectional sync"| GH + + style G0 fill:#f59e0b,color:#000 + style G2 fill:#f59e0b,color:#000 + style G3 fill:#f59e0b,color:#000 + style G4 fill:#f59e0b,color:#000 + style G6 fill:#f59e0b,color:#000 + style G7 fill:#f59e0b,color:#000 + style SM fill:#22c55e,color:#fff + style DAG fill:#22c55e,color:#fff + style LOCK fill:#22c55e,color:#fff + style TJ fill:#3b82f6,color:#fff + style GH fill:#3b82f6,color:#fff +``` + +--- + +## 6. マルチマシン協調: Before / After + +### As-Is + +```mermaid +graph LR + subgraph MacBook + A1[Agent A
auth.ts 編集中] + M1[MEMORY.md
v3] + end + + subgraph Windows + A2[Agent B
auth.ts 編集中] + M2[MEMORY.md
v2 ← 古い!] + end + + subgraph mainmini + A3[Agent C
何をしてるか不明] + M3[MEMORY.md
v1 ← もっと古い!] + end + + A1 -.->|"💥 競合"| A2 + M1 -.->|"❌ ズレ"| M2 + M2 -.->|"❌ ズレ"| M3 +``` + +### To-Be + +```mermaid +graph TB + subgraph "tasks.json(共有 SSOT)" + TJ["task-001: implementing
lock: Agent A @ MacBook
files: [auth.ts] 🔒

task-002: blocked ⏳
depends: task-001

task-003: pending
files: [user.ts] — 別ファイルなので並行OK"] + end + + subgraph MacBook + A1[Agent A
auth.ts 編集 ✅
ロック保持] + end + + subgraph Windows + A2[Agent B
auth.ts 触れない 🚫
task-002 blocked] + end + + subgraph mainmini + A3[Agent C
user.ts 編集 ✅
task-003 別ロック] + end + + A1 -->|"read/write"| TJ + A2 -->|"read only"| TJ + A3 -->|"read/write"| TJ + TJ <-->|"sync"| GH[GitHub Issues
Fact SSOT] +``` + +--- + +## 7. GATE チェーン: 1タスクの完全ライフサイクル + +```mermaid +graph LR + START((Start)) --> G0{GATE 0
Issue?} + G0 -->|No| REJECT0[❌ 登録拒否] + G0 -->|Yes| DRAFT[draft] + + DRAFT --> G1{GATE 1
title+desc?} + G1 -->|No| REJECT1[❌] + G1 -->|Yes| PENDING[pending] + + PENDING --> G2{GATE 2
deps done?} + G2 -->|No| BLOCKED[blocked ⏳] + G2 -->|Yes| ANALYZING[analyzing] + + ANALYZING --> G3{GATE 3
impact?} + G3 -->|No| REJECT3[❌ GNI回せ] + G3 -->|HIGH| HUMAN{人間承認?} + G3 -->|LOW/MED| G4 + HUMAN -->|No| REJECT_H[❌] + HUMAN -->|Yes| G4 + + G4{GATE 4
lock?} -->|競合| REJECT4[❌ 待て] + G4 -->|OK| IMPL[implementing 🔒] + + IMPL --> G5{GATE 5
branch?} + G5 -->|No| REJECT5[❌] + G5 -->|Yes| G6{GATE 6
PR?} + G6 -->|No| REJECT6[❌] + G6 -->|Yes| REVIEW[reviewing] + + REVIEW --> G7{GATE 7
merge?} + G7 -->|No| REJECT7[❌] + G7 -->|Yes| DONE[done ✅] + + DONE --> G8{GATE 8
logged?} + G8 -->|Yes| FIN((End)) + + BLOCKED -->|"dep解決"| PENDING + + style REJECT0 fill:#ef4444,color:#fff + style REJECT1 fill:#ef4444,color:#fff + style REJECT3 fill:#ef4444,color:#fff + style REJECT_H fill:#ef4444,color:#fff + style REJECT4 fill:#ef4444,color:#fff + style REJECT5 fill:#ef4444,color:#fff + style REJECT6 fill:#ef4444,color:#fff + style REJECT7 fill:#ef4444,color:#fff + style DONE fill:#22c55e,color:#fff + style BLOCKED fill:#f59e0b,color:#000 + style IMPL fill:#3b82f6,color:#fff +``` + +--- + +_Generated: 2026-04-10 | Deterministic Task Execution Protocol UML_ diff --git a/docs/dtp/reviews/codex-review-dag-coordination.md b/docs/dtp/reviews/codex-review-dag-coordination.md new file mode 100644 index 0000000..d26e955 --- /dev/null +++ b/docs/dtp/reviews/codex-review-dag-coordination.md @@ -0,0 +1,194 @@ +# DAG Integrity / Distributed Coordination Review + +対象: +- Plan: `/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md` +- DAG 実装: `KOTOWARI/skills/openclaw-crowd/src/scheduling/task-dependency-graph.ts` +- Queue/Lock 実装: `/Users/shunsukehayashi/dev/tools/agent-skill-bus/src/queue.js` + +## Findings + +### 1. Critical: `tasks.json` を単一の truth + lock table に集約すると、分散書き込みで整合性が壊れます + +- Plan は `fileLocks` と `dagLevels` を `tasks.json` に統合する前提です (`snuggly-bouncing-turtle.md:115-117`, `:217-223`, `:345-353`)。 +- しかし現行 queue は JSONL を append 中心で扱い、少なくとも lock 取得だけは append-only に寄せています (`queue.js:25-27`, `:149-157`)。一方で status 更新は全体書き換えで、ここでも既に lost update リスクがあります (`queue.js:241-246`)。 +- Plan の TaskStore は `load() -> mutate -> save()` 型に見え、分散ノードが同時に `tasks.json` を更新すると、lock 取得・状態遷移・dagLevels 再計算が互いを上書きしやすいです (`snuggly-bouncing-turtle.md:204-223`)。 + +影響: +- 2 エージェントが同時に lock 取得できたように見える +- `currentState` と `fileLocks` が食い違う +- `dagLevels` が古いまま残る +- GitHub sync の巻き戻しが他のローカル更新を消す + +推奨: +- `tasks.json` を monolithic source of truth にするなら、必須で「単一 writer」か「OS ファイルロック + compare-and-swap + revision 番号」を入れるべきです。 +- そうしないなら、`tasks.json` は snapshot/read model に留め、競合しやすいイベントは JSONL event log に分離する方が安全です。 +- 最低でも `syncVersion` を gate に使い、`save()` は `expectedVersion` 不一致なら失敗させるべきです。 + +### 2. Critical: lock 競合判定が TOCTOU で、分散協調の核心がまだ閉じていません + +- queue 実装でも `getDispatchable()` の判定と `startExecution()` の lock 獲得は別ステップです (`queue.js:79-131`, `:134-161`)。これは単機能 queue としては妥当ですが、分散 coordinator の最終形には不十分です。 +- Plan の `assignAndLock()` も `hasConflict(files) === false` を gate にしていますが (`snuggly-bouncing-turtle.md:159-164`)、チェックと書き込みが原子的である保証が計画上ありません。 + +影響: +- 2 ノードが同時に `hasConflict=false` を観測して両方進む +- 同一ファイルの逐次タスクが並行実行される + +推奨: +- `assignAndLock()` は「read-check-write」を 1 つの原子操作にするべきです。 +- 具体案: + - `flock` 相当で store 全体に短時間のメタロックを取る + - 再読込 + - conflict 再判定 + - lock 書き込み + state 遷移 + version increment + - fsync/atomic rename +- sequential task は「同じファイルを使うから level を落とす」のではなく、dispatch 時に lock が直列化する設計で十分です。ただしその lock は原子的である必要があります。 + +### 3. High: `softDependencies` を DAG edge として扱うと、parallel levels が過度に直列化されます + +- 現行 DAG は soft edge を保持しても、実行可否では hard のみブロックし、soft は待ちません (`task-dependency-graph.ts:92-129`, `:272-279`)。 +- Plan は `hard/soft` を `dependencies`/`softDependencies` に分けると言いながら (`snuggly-bouncing-turtle.md:100-103`)、`dagLevels` をトポロジカルソート結果のキャッシュとして持ちます (`:217-223`)。 + +問題: +- soft dep を level 計算に混ぜると、「本来は並列に走れるが、できれば後にしたい」タスクまで別レベルへ押し出されます。 +- 逆に soft dep を level 計算から完全に外すと、UI の見た目と dispatch 順がずれる可能性があります。 + +推奨: +- hard DAG と soft preference を分離してください。 +- `dagLevels` は hard dependencies のみで計算する。 +- soft deps は level を変えず、同一 ready set 内の優先順位付けにだけ使う。 +- ルール例: + - hard dep 未完了: `blocked` + - hard dep 完了かつ soft dep 未完了: `ready_with_soft_wait` + - dispatcher は `ready` より後ろに置くが、空いていれば実行可 + +### 4. High: `dagLevels` キャッシュは無効化条件が広く、現行計画だと stale になりやすいです + +- Plan は `dagLevels` を永続キャッシュとして持ちます (`snuggly-bouncing-turtle.md:217-223`, `:349-353`)。 +- しかし DAG level は「依存辺の変更」だけでなく、「タスク追加/削除」「hard/soft の切替」「依存先の存在消失」「reopen/retry に伴う実行可能性の再評価」の影響を受けます。 +- さらに level 自体は構造キャッシュであり、`done`/`blocked` は構造ではなく状態です。両者を同じ配列に期待すると意味が混ざります。 + +推奨: +- `dagLevels` を durable state にしない方がよいです。読み込み時再計算、または memoized cache に留めるのが安全です。 +- 永続化するなら `dagHash` を併記し、次の変更で必ず invalidate: + - task add/remove + - dependency add/remove + - hard/soft type change + - import/sync による DAG 修正 +- task status 変更では `dagLevels` 自体は invalidate せず、別に `readySetComputedAt` などを持つ方が責務分離できます。 + +### 5. Medium: GitHub を常に優先すると DAG truth と execution truth がねじれます + +- Plan は `conflictStrategy: deterministic` で GitHub を常に優先し、ローカル done を blocked に巻き戻す方針です (`snuggly-bouncing-turtle.md:230-235`)。 +- ただし GitHub Issue の open/closed は DAG 実行状態の完全な proxy ではありません。merge 後に close が遅れる、手動 reopen、Issue と PR が 1:1 でない、などが普通に起きます。 + +影響: +- `recordMerge()` で done にした後、sync が blocked に戻す +- downstream 解放済みなのに upstream を blocked に戻し、DAG の一貫性が崩れる + +推奨: +- 完了確定は Issue 状態単独ではなく、`mergeCommit` と PR merge state を優先してください。 +- GitHub Issue open/closed は advisory signal に留めるべきです。 + +## Direct Answers + +### Is Kahn algorithm the right choice? + +はい、`hard dependency` の DAG 構造検証と level 計算には妥当です。 +ただし用途は限定すべきです。 + +- 向いている: + - cycle のない順序の算出 + - hard deps ベースの parallel levels + - ready set の基礎計算 +- 向いていない: + - soft deps を含む dispatch policy + - file lock を含む最終 dispatch 判定 + - retry/reopen/lease expiry を含む実行状態管理 + +結論: +- `Kahn for structure` +- `state machine for lifecycle` +- `lock manager for actual dispatch` + +この三層分離がよいです。 + +### How should soft deps work with DAG levels? + +soft dep は level を作る edge ではなく、同一 level 内の優先順ヒントとして扱うのが安全です。 + +推奨モデル: +- `hardDependencies`: 実行可否を決める +- `softDependencies`: 実行順 preference を決める +- `dagLevels`: hard のみで計算 +- `dispatch score`: priority, age, softSatisfied, lockAvailability を合成 + +状態名を増やせるなら `ready` と `ready_soft_blocked` を分けると観測しやすいです。 + +### dagLevels cache invalidation? + +永続化しないのが第一候補です。 +永続化するなら DAG 構造専用キャッシュにしてください。 + +invalidate 条件: +- task add/remove +- dependency add/remove +- dependency type hard/soft change +- sync/import による DAG repair + +invalidate 不要: +- `pending -> analyzing -> implementing -> done` などの状態遷移だけ +- lock acquire/release + +### File lock for sequential tasks sharing files? + +必要です。しかも task-level ではなく file-set lease として扱うべきです。 + +推奨: +- lock key は正規化済みファイルパス集合 +- `assignAndLock()` で原子的に lease 取得 +- heartbeat/renew を入れる +- TTL expiry は即 failed 固定ではなく、`orphaned_lock` として再取得可能にする + +同じファイルを触る sequential tasks は DAG level が同じでも問題ありません。 +実行順は lock で直列化し、必要なら soft dep で順序 preference を足します。 + +### JSONL vs monolithic JSON for concurrent writes? + +結論: +- 高頻度イベント: JSONL が向いています +- snapshot/read model: monolithic JSON が向いています +- 単一 writer なしで monolithic JSON だけに寄せるのは危険です + +おすすめ構成: +- `task-events.jsonl` + - state transition + - lock acquired/released/expired + - DAG changed + - sync reconciled +- `tasks.snapshot.json` + - 現在状態の materialized view + - 起動時または定期で再生成 + +もし `tasks.json` 1ファイルにこだわるなら、少なくとも: +- OS lock +- atomic rename +- version check +- crash recovery + +この 4 点が必要です。 + +## Suggested Design Adjustment + +最小変更で守りを固めるなら、以下の形がよいです。 + +1. DAG は hard deps のみで Kahn。 +2. soft deps は dispatch priority にだけ使う。 +3. lock/state/sync は event log に記録。 +4. `tasks.json` は snapshot とし、壊れても再構築可能にする。 +5. `assignAndLock()` を唯一の dispatch commit point にする。 +6. GitHub は external confirmation であって、唯一の truth にはしない。 + +## Residual Risk + +この計画は「LLM の揺らぎを封じる」方向性自体は非常に良いです。 +ただし現状のまま `tasks.json` に lock table と cached dagLevels を押し込むと、DAG の正しさより先に分散更新の競合で壊れる可能性が高いです。最初に固めるべきはアルゴリズム選定より commit/lock の原子性です。 diff --git a/docs/dtp/reviews/codex-review-github-sync.md b/docs/dtp/reviews/codex-review-github-sync.md new file mode 100644 index 0000000..fa6d90e --- /dev/null +++ b/docs/dtp/reviews/codex-review-github-sync.md @@ -0,0 +1,139 @@ +# Review: Deterministic Task Execution Protocol + +## Findings + +### 1. High: 計画は二層SSOTをまだ完全には実装しておらず、`tasks.json` を「事実の正」に昇格させすぎています + +- 計画は「JSON の状態遷移だけが事実」と置いていますが、ビジョン文書と仕様文書はタスク状態のファクトSSOTを GitHub Issue / Projects に置いています。ここが正面衝突しています。 + - Plan: [`snuggly-bouncing-turtle.md`](/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md#L8) [`snuggly-bouncing-turtle.md`](/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md#L12) + - Vision: [`agent-execution-and-memory-vision.md`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/docs/design/agent-execution-and-memory-vision.md#L87) [`agent-execution-and-memory-vision.md`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/docs/design/agent-execution-and-memory-vision.md#L110) + - Spec: [`context-and-impact-spec.md`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/docs/design/context-and-impact-spec.md#L54) [`context-and-impact-spec.md`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/docs/design/context-and-impact-spec.md#L67) +- 現状の設計だと `tasks.json` は execution ledger と gate engine であるべきなのに、完了判定の authority まで持っています。二層SSOTを守るなら、 + - GitHub: 受入・完了・人間承認の正 + - `tasks.json`: ローカル実行状態、ロック、依存、再試行、監査 + に役割を分ける必要があります。 +- `done` の必要条件は「`tasks.json` に `mergeCommit` があること」ではなく、「GitHub 上で受理された完了証跡を同期済みであること」にすべきです。`tasks.json` はそのミラーであるべきです。 + +### 2. High: GitHub 障害時の劣化モードが未定義で、決定性より停止性を失う可能性があります + +- 計画は GitHub を強いゲートにしていますが、「GitHub API が落ちた」「レート制限」「ネットワーク断」のときに何が許可され、何が禁止されるかが定義されていません。 + - Plan merge/close gating: [`snuggly-bouncing-turtle.md`](/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md#L179) + - Current sync has no degraded mode, retry journal, or outage classification: [`bidirectional-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts#L105) [`bidirectional-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts#L163) +- このままだと障害時に次の2択になります。 + - `done` を絶対に付けられず運用停止 + - 人が裏口で進めて決定性崩壊 +- 必要なのは「安全に止まる」中間状態です。少なくとも以下が必要です。 + - `awaiting_github_sync` または `externally_completed_unverified` + - GitHub API 障害と論理矛盾を分ける error class + - pull/push の retry queue と backoff + - 最後に確認できた GitHub 証跡の timestamp + - 明示的な human override と理由記録 + +### 3. High: 双方向同期の計画が危険で、Issue close をそのまま `done` に写像すると誤完了になります + +- 計画は「GitHub で Closed なのにローカルで implementing → pull で done に更新」としていますが、Issue は PR merge 以外の理由でも閉じられます。これをそのまま `done` とみなすのは強すぎます。 + - Plan: [`snuggly-bouncing-turtle.md`](/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md#L230) +- 逆方向の「ローカル done なのに Issue Open → blocked に巻き戻し」も不自然です。現行状態機械では `done -> blocked` は無効で、許されるのは `done -> pending` だけです。 + - State machine: [`task-state-machine.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/state/task-state-machine.ts#L76) + - Plan rollback idea: [`snuggly-bouncing-turtle.md`](/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md#L231) +- しかも既存 `BidirectionalSync.sync()` は pull 結果をローカル task/store に適用しておらず、衝突を集めるだけです。計画の「pull で done に更新」は、既存の延長では実現されません。 + - [`bidirectional-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts#L254) +- 推奨は one-way authority を明確化することです。 + - GitHub -> ローカル: 受理・クローズ・PR merge の事実を pull + - ローカル -> GitHub: 提案された状態を push + - ただし `done` は PR merge 証跡つき close のときだけ確定 + +### 4. Medium: merge commit hash の取得方法と検証経路が仕様化されていません + +- 計画は `recordMerge(taskId, mergeCommit)` を置いていますが、その hash をどこから、どうやって、何と突き合わせて取得するかが未定義です。 + - Plan: [`snuggly-bouncing-turtle.md`](/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md#L179) +- 現状の sync 実装には PR 取得や merge SHA 取得の処理がありません。Issue/Label/Project しか見ていません。 + - [`github-label-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/github-label-sync.ts#L216) + - [`projects-v2-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/projects-v2-sync.ts#L344) +- 取得方法は次のように固定するのがよいです。 + - `recordPR()` 時に `prNumber` を必須保存する + - merge 検証時は Issue API ではなく PR API を見る + - 第一候補: REST `GET /repos/{owner}/{repo}/pulls/{pull_number}` の `merge_commit_sha` + - 代替: GraphQL `pullRequest.mergeCommit.oid` + - 追加で `mergedAt`, `merged`, `state`, `baseRefOid`, `headRefOid` を保存する +- 重要なのは「Issue close から merge hash を推測しない」ことです。`prNumber` がないタスクは merge-based completion を確定できません。 + +### 5. Medium: 正当な例外経路が不足しており、現実運用で手動回避が増えそうです + +- 仕様は HIGH/CRITICAL に human approval を要求していますが、実際の例外経路が定義されていません。 + - Plan: [`snuggly-bouncing-turtle.md`](/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md#L152) +- 必要なのは「抜け道」ではなく「監査可能な escape hatch」です。例えば: + - GitHub 障害時に一時的に `reviewing` のまま凍結する + - 外部で既に merge/close されたタスクを `reconcile` で取り込む + - ドキュメント作業や運用作業のように PR を伴わない legitimate completion を `completionMode=manual|github-pr|external-op` で区別する + - 強制 unlock / reopen / superseded / abandoned の理由欄を必須化する +- 今の計画はコード変更中心の happy path には強いですが、実運用で必ず出る「PRなしで正当に終わる仕事」「外部作業」「GitHub障害中の継続作業」を吸収できません。 + +### 6. Medium: 現行同期実装の前提を踏まえると、Phase 6 は「強化」ではなく再設計に近いです + +- 現在の `BidirectionalSync` は conflict strategy も `local-wins` がデフォルトで、`newest-wins` も未実装同然です。 + - [`bidirectional-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts#L23) + - [`bidirectional-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts#L40) + - [`bidirectional-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts#L384) +- さらに current sync は `ManagedTask` を mutate せず、store 連携もありません。 + - [`task-manager.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/task-manager.ts#L240) + - [`bidirectional-sync.ts`](/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/sync/bidirectional-sync.ts#L258) +- そのため deterministic sync を本当にやるなら、必要なのは conflict strategy 追加だけではなく、 + - state reconciliation engine + - persistent sync cursor / version + - per-task sync status + - webhook event idempotency + - API failure taxonomy + の実装です。 + +## Direct Answers + +### What if GitHub API is down? + +- 今の計画だけでは未対応です。 +- 推奨は fail-closed ですが、完全停止ではなく `awaiting_github_sync` へ遷移させることです。 +- 実行中の作業は続けてもよいですが、`done` と lock release の一部は「GitHub未検証」のまま分離保存すべきです。 +- 監査上は `completionRequestedAt`, `githubVerifiedAt`, `githubSyncError` を残すべきです。 + +### One-way push vs bidirectional? + +- 完全双方向より「authority-aware bidirectional」が適切です。 +- Push はローカルの提案状態を GitHub に反映するために使う。 +- Pull は GitHub の受理状態をローカルへ反映するために使う。 +- ただし authority は対称ではありません。`done`/`accepted` は GitHub 優先、実行中ロックや DAG はローカル優先です。 +- 要するに transport は bidirectional、SSOT は asymmetric にすべきです。 + +### How to get mergeCommit hash? + +- Issue ではなく PR から取得します。 +- `recordPR()` 時に `prNumber` を保存し、reconcile 時にその PR を取得します。 +- 実装候補: + - REST: `pulls.get(...).data.merge_commit_sha` + - GraphQL: `pullRequest.mergeCommit.oid` +- `mergeCommit` だけでなく `merged`, `mergedAt`, `headSha`, `baseSha`, `mergeMethod` も一緒に保持すると検証が安定します。 + +### Does the plan fully implement two-layer SSOT? + +- いいえ、未完成です。 +- 現状は `tasks.json` を事実の正に寄せすぎており、Vision/Spec が要求する「GitHub=ファクトSSOT、repo/tasks.json=文脈・実行ミラー」という分離が崩れています。 + +### Escape hatches for legitimate work? + +- まだ不十分です。 +- 少なくとも `manual-completion`, `external-completion`, `awaiting-github-sync`, `force-unlock-with-reason`, `reconcile-from-github` は必要です。 +- どれも「自由に bypass」ではなく、理由・操作者・時刻・承認者を残すべきです。 + +## Recommended Changes + +1. `tasks.json` の位置づけを「execution ledger / gate cache」に下げ、ファクトSSOTは GitHub のまま明記する。 +2. `done` を単純 state ではなく `accepted=true` を含む受理イベントで確定させる。 +3. sync を再設計し、`push proposal` と `pull authoritative facts` を分ける。 +4. `Issue closed => done` を廃止し、`PR merged + issue closed/linked accepted` を確定条件にする。 +5. GitHub 障害時の `awaiting_github_sync` 系ステートと retry journal を追加する。 +6. manual/external completion の監査可能な escape hatch を先に仕様化する。 + +## Overall + +計画の方向性自体はかなり良いです。特に DAG、ロック、ゲートを `tasks.json` に集約して LLM の自然言語を無力化する発想は強いです。ただし GitHub Sync と end-to-end deterministic guarantee の観点では、いまの文面は「ローカル deterministic engine」は強い一方で、「GitHub を authority とした分散整合」はまだ甘いです。 + +決定的にしたいなら、`tasks.json` と GitHub のどちらも同じように信じるのではなく、「何の事実はどちらが authoritative か」を状態ごとに切り分ける必要があります。 diff --git a/docs/dtp/reviews/codex-review-state-machine.md b/docs/dtp/reviews/codex-review-state-machine.md new file mode 100644 index 0000000..28bd86b --- /dev/null +++ b/docs/dtp/reviews/codex-review-state-machine.md @@ -0,0 +1,310 @@ +# State Machine Gates Correctness Review + +対象: +- Plan: `/Users/shunsukehayashi/.claude/plans/snuggly-bouncing-turtle.md` +- State machine: `/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/state/task-state-machine.ts` +- Types: `/Users/shunsukehayashi/dev/ops/openclaw-workspace/Miyabi/packages/task-manager/src/types/task.ts` + +レビュー観点: +- 9つの GATE が十分か +- LLM が手順を飛ばせる抜け道があるか +- 既存 27 遷移ルールと整合するか +- 追加 state が必要か +- TTL 7200s の妥当性 +- 複数マシンでの file lock race + +## Findings + +### 1. Critical: 現行 state machine は conditions を実評価していないため、plan の GATE を state machine へ載せても素通りする + +`TaskStateMachine.canTransition()` は `from -> to` の組み合わせしか見ておらず、`conditions` は一切評価していません。`transition()` と `applyTransition()` も同様です。したがって plan の「GATE を state machine の gate として実装」という意図に対して、既存実装を「変更なし」で使う案は成立しません。 + +根拠: +- `conditions` が定義されている: `task-state-machine.ts:34-80` +- `canTransition()` は `r.to === to` しか見ない: `task-state-machine.ts:103-106` +- `applyTransition()` も `transition()` の結果だけで更新 task を返す: `task-state-machine.ts:149-189` + +影響: +- `skip_analysis`, `dependency_blocked`, `review_approved`, `requires_deployment`, `no_deployment` など既存 27 ルール側の条件文字列も現在は強制力ゼロです。 +- plan 側で 9 GATE を追加しても、別経路から `applyTransition(task, 'done', ...)` を呼べば通ります。 + +必要修正: +- state machine 自体に `canTransition(task, to, context)` を追加して条件評価を内包する +- もしくは protocol を唯一の遷移窓口にして、`TaskStateMachine` を外から直接呼べないようにする + +### 2. Critical: 既存呼び出し元は applyTransition() の戻り値 task を保存しておらず、状態遷移そのものが永続化されない + +現行コードでは `applyTransition()` は新しい `task` を返しますが、主要呼び出し元がそれを map に戻していません。つまり transition が valid でも、実 task は更新されない経路があります。 + +根拠: +- `TaskManager.updateTaskState()` は `result.valid` を返すだけ: `task-manager.ts:148-158` +- `TaskExecutor.execute()` でも `transitionResult.task` を採用していない: `task-executor.ts:107-126`, `157-177`, `186-187` + +影響: +- plan の gate を増やす前に、現行 state progression が fact になっていない +- 「JSON の状態遷移だけが事実」という North Star と真逆 + +必要修正: +- 全遷移を store-backed immutable update に統一 +- `applyTransition` で返した task を即 store に commit できないなら、その API を使わせない + +### 3. High: 9 GATE は「必要証跡」は見ているが、「順序の完全性」をまだ保証していない + +現行 9 GATE は以下の点で不足があります。 + +- Step 3 は `impact !== null` だけで、impact が対象 branch / commit / file set に対する最新分析かを検証していない +- Step 4 は `lock !== null` を見るが、「lock を取った主体」と「今実行している agent」が一致するかを見ていない +- Step 5 は `branchName` の書式だけで、実際に remote / worktree 上に存在するかを見ていない +- Step 6 は `prNumber > 0` だけで、PR がその branch に紐づくか、Draft/Ready/Closed/Merged のどれかを見ていない +- Step 7 は `mergeCommit` の SHA 形式だけで、対象 PR が merged 済みか、merge commit がその PR/head を含むかを見ていない +- Step 8 は `Issue Closed` を見るが、close reason や linked PR による close か、人手クローズかを区別しない + +LLM の飛ばし方の例: +- 適当な 40 hex を `mergeCommit` に書けば reviewing -> done 条件を満たせる +- 別 task の PR 番号を流用して implementing -> reviewing へ進める +- 古い impact 結果を再利用して analyzing -> implementing に進める + +追加すべき gate: +- `impact.analyzedAt >= latestTaskMaterializationAt` +- `impact.fileSet` or `impact.inputHash` が現在の対象と一致 +- `lock.lockedBy === assignee@node` かつ lock 未期限切れ +- `branchExists && branchHeadCommit !== null` +- `pr.headRefName === branchName && pr.state === OPEN` +- `pr.reviewDecision === APPROVED` または required checks success +- `mergeCommit` が GitHub 上で当該 PR の merge commit と一致 +- `issue.closedByPullRequest === prNumber` 相当の検証 + +### 4. High: plan の reviewing -> done は既存 27 ルールと衝突する + +plan では Step 7 を `reviewing -> done` にしていますが、既存 state machine では: +- `reviewing -> done` は `review_approved && no_deployment` +- `deploying -> done` は sideEffects `close_issue`, `merge_pr` + +根拠: +- `task-state-machine.ts:57-65` + +衝突内容: +- plan では merge が done の必須条件になっている +- 既存ルールでは `reviewing -> done` は「デプロイ不要なら done」、`deploying -> done` が merge/close を担う + +このままだと: +- デプロイ不要タスクの完了 semantic が変わる +- 既存の `deploying` state がほぼ空洞化する + +結論: +- 既存 27 ルールと互換にするなら、`reviewing -> done` を廃止して `reviewing -> merging|deploying` を経由させるべきです +- 少なくとも `merge` と `deploy` は別の irreversible event なので同じ gate にしない方が安全です + +### 5. High: 追加 state が必要。少なくとも `ready`, `locked`, `merged` のどれかを state として持たないと gate が metadata 化して見落とされやすい + +現行 state は: +- draft, pending, analyzing, implementing, reviewing, deploying, done, blocked, failed, cancelled + +不足している意味上の段階: +- `ready`: dependencies 解消済みだが未着手 +- `locked` または `assigned`: 実装開始前に資源確保済み +- `merged`: review 承認済みで merge 完了、ただし post-merge audit/close 未完 +- `auditing` または `finalizing`: worklog/skill-bus/issue close 整合を取る終端前処理 + +理由: +- 現 plan は Step 4, 5 を「implementing 中の metadata」で表しており、state 上は見えません +- そのためオペレーション側が `currentState === implementing` だけ見たときに、lock 未取得・branch 未作成・PR 未作成の task を同列に扱ってしまいます + +最小提案: +- `pending -> ready -> analyzing -> locked -> implementing -> reviewing -> merged -> done` +- deploy が必要なら `reviewing -> deploying -> merged -> done` でもよい + +もし state を増やしたくないなら: +- state ではなく `phaseChecklist` の必須項目を machine-evaluable に持つ必要があります + +### 6. Medium: pending -> analyzing gate が既存 semantics と少しズレている + +plan Step 2 は `dependencies.every(done)` を `pending -> analyzing` の gate にしていますが、既存 state machine には: +- `pending -> blocked` with `dependency_blocked` +- `blocked -> pending` with `dependency_resolved` + +根拠: +- `task-state-machine.ts:38-40`, `68-69` + +気になる点: +- dependency 未解決の task は本来 `blocked` に入る設計 +- plan の `checkDependencies(): 'ready' | 'blocked'` はあるが、gate table 上は `pending -> analyzing` しか記述がなく、`pending` に留め続ける実装にも見える + +提案: +- dependency 未解決時は必ず `blocked` +- 依存解決後に `blocked -> pending` を踏ませる +- `ready` を導入するなら `pending` は単なる backlog、`ready` が dispatchable を表す方が明確 + +### 7. Medium: `pending -> implementing (skip_analysis)` ルールとの整合が未定義 + +既存 27 ルールには `pending -> implementing` が存在します。 + +根拠: +- `task-state-machine.ts:39` + +plan では Step 3 で impact 記録が implementing の必須条件なので、skip_analysis を事実上禁止する方向に見えます。これは設計としてはよいですが、既存ルールを「変更なし」とは両立しません。 + +提案: +- `skip_analysis` を削除する +- もしくは `analysis_mode: full | waived` を明示し、waived でも human approval 必須にする + +### 8. Medium: TTL 7200s は固定値としては長すぎ、しかも heartbeat 前提がない + +7200 秒は 2 時間です。長時間の実装には短すぎることもありますが、「エージェントが死んだのに他ノードが2時間待つ」という意味では長すぎます。 + +問題: +- lock refresh/heartbeat が plan にない +- 実装中に TTL 失効すると、別ノードが同じファイルを取りに行ける +- 逆に crash 後の回復は最長 2 時間止まる + +提案: +- 固定 TTL ではなく short lease + heartbeat 方式 +- 例: lease 300s, renew every 60s, stale after 2 missed renewals +- 人手 override 用の `force unlock` は残す + +7200s が妥当なのは: +- 単一マシン +- 長いジョブ +- 他ノードが少ない + +今回は「across machines」が前提なので、固定 7200s は非推奨です。 + +### 9. Medium: tasks.json 内 `fileLocks` 更新はクロスマシン race に弱い + +plan は JSONL から `tasks.json` の `fileLocks` へ統合しますが、単一 JSON ドキュメントの read-modify-write は append-only より競合に弱いです。 + +想定 race: +- Machine A, B が同時に load +- 両者とも conflict なしと判断 +- 両者が別々に save +- 後勝ち write で片方の lock が消える、または両方が lock 獲得したと誤認する + +JSONL append-only より悪化する点: +- append-only は競合検査を後から再生しやすい +- 単一 JSON overwrite は lost update しやすい + +最低限必要: +- OS レベルの lock file か atomic rename +- compare-and-swap 用の version check +- `syncVersion` ではなく document version で CAS +- lock acquisition を「check + write」でなく「single atomic claim」に寄せる + +クロスマシンで本当に安全にするなら: +- 共有 filesystem の advisory lock 依存は弱い +- ローカル files しかないなら coordinator 1台に集約すべき +- もしくは GitHub/SQLite/Postgres 等の単一調停者が必要 + +## Are all 9 GATEs sufficient? + +結論: 不十分です。 + +足りていないのは主に 3 系統です。 + +1. Provenance gate +- その証跡が「今の task/branch/PR のものか」を確認していない + +2. Freshness gate +- impact / lock / PR / merge 情報が最新かを見ていない + +3. Authority gate +- 誰がその state を進めてよいか、誰の lock か、human approval がどこに記録されているかを見ていない + +## Can an LLM still skip steps? + +はい。現案のままだと skip できます。 + +代表例: +- metadata を直接埋めて `recordPR()` や `recordMerge()` を呼ぶ +- 既存の `updateTaskState()` / `applyTransition()` 経路から protocol をバイパスする +- stale な lock を握ったまま別ノードが再取得する +- 実 branch/PR/GitHub state を見ずに JSON だけ整えて done にする + +## Compatibility with existing 27 rules + +結論: そのままでは非互換です。 + +非互換ポイント: +- `conditions` が未評価なので既存ルールの意味が実装されていない +- `pending -> implementing (skip_analysis)` と plan の mandatory impact gate が衝突 +- `reviewing -> done` / `deploying -> done` の意味が plan の merge 必須完了定義と衝突 + +互換にしたいなら: +- 27 ルールを単に残すのでなく、条件を実評価する新 API へ移行 +- 完了定義を `done = merged + issue closed + audit recorded` に寄せるなら、ルール表も更新が必要 + +## Should additional states exist? + +結論: 追加した方が安全です。 + +推奨候補: +- `ready` +- `locked` or `assigned` +- `merged` +- `auditing` or `finalizing` + +最小でも `merged` は有用です。`review approved` と `merge complete` と `audit complete` は別イベントだからです。 + +## Is TTL 7200s appropriate? + +結論: 固定値としては不適切です。 + +- 単一マシンなら許容 +- 複数マシン協調では長すぎる +- heartbeat/renewal がないため safety より liveness も safety も中途半端 + +推奨: +- lease 300-600s +- renew interval 60-120s +- owner heartbeat 必須 +- expired lock reacquire 時に fencing token を使う + +## Race conditions in file locks across machines + +結論: 現案のままでは race があります。 + +主な懸念: +- lost update +- double-acquire +- stale lock resurrection +- clock skew による TTL 判定ズレ +- network partition 中の二重実行 + +対策優先順: +1. Single writer/coordinator に lock 判定を集約 +2. atomic write + CAS version check +3. lease renewal と fencing token +4. monotonic timestamp source を一元化 + +## Recommended design changes + +1. `TaskStateMachine` を gate-aware にする +- `canTransition(task, to, context)` を導入 +- `conditions` を文字列配列でなく predicate 群へ寄せる + +2. `DeterministicExecutionProtocol` を唯一の state mutation 経路にする +- `TaskManager.updateTaskState()` などの素通し経路は封鎖または内部専用化 + +3. 完了 state を分解する +- `reviewing -> merged -> done` +- deploy が必要なら `reviewing -> deploying -> merged -> done` + +4. lock は fixed TTL でなく renewable lease にする + +5. `tasks.json` overwrite で lock を持たない +- 少なくとも CAS + atomic rename +- 可能なら coordinator/SQLite 等へ分離 + +6. gate を「presence check」から「verified linkage check」に上げる +- PR 番号がある、ではなく「その task の branch を head に持つ open/merged PR がある」 + +## Bottom line + +plan の方向性自体は正しいです。ただし現在の state machine を「そのまま使う」前提だと、GATE は仕様書上の強い言葉に留まり、実装上は抜けられます。 + +最優先で直すべき点は次の 3 つです。 + +1. state machine に条件評価を実装する +2. state transition を store に永続反映する +3. merge / audit / close を `done` 前に明示的に分離する + +この 3 点を入れない限り、「LLM の揺らぎを JSON state だけで封じる」保証には届きません。 diff --git a/project_memory/tasks.json b/project_memory/tasks.json new file mode 100644 index 0000000..c684d6e --- /dev/null +++ b/project_memory/tasks.json @@ -0,0 +1,160 @@ +{ + "version": 1, + "last_updated": "2026-04-10T00:00:00Z", + "tasks": [ + { + "id": "phase-0", + "title": "前提条件の確定", + "github_issue_number": 0, + "state": "done", + "dependencies": [], + "dependents": ["phase-1"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:55:00Z" + }, + { + "id": "phase-1", + "title": "型定義ハードニング", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-0"], + "dependents": ["phase-2", "phase-3"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + }, + { + "id": "phase-2", + "title": "ステートマシンにGATE predicate統合", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-1"], + "dependents": ["phase-6"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + }, + { + "id": "phase-3", + "title": "Event Store (JSONL + Snapshot)", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-1"], + "dependents": ["phase-4", "phase-5"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + }, + { + "id": "phase-4", + "title": "File Lock Manager (lease + heartbeat)", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-3"], + "dependents": ["phase-6"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + }, + { + "id": "phase-5", + "title": "GitHub同期 (Evidence Fetcher + Deterministic Sync)", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-3"], + "dependents": ["phase-6"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + }, + { + "id": "phase-6", + "title": "Protocol統合(全GATEを1クラスに結合)", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-2", "phase-3", "phase-4", "phase-5"], + "dependents": ["phase-7"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + }, + { + "id": "phase-7", + "title": "CLI (dtp コマンド)", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-6"], + "dependents": ["phase-8"], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + }, + { + "id": "phase-8", + "title": "E2E統合テスト + OpenClaw連携", + "github_issue_number": 0, + "state": "pending", + "dependencies": ["phase-7"], + "dependents": [], + "soft_dependencies": [], + "lock": null, + "impact": null, + "branch_name": null, + "pr_number": null, + "merge_commit": null, + "created_at": "2026-04-10T00:00:00Z", + "updated_at": "2026-04-10T00:00:00Z" + } + ], + "file_locks": {}, + "dag_levels": [ + ["phase-0"], + ["phase-1"], + ["phase-2", "phase-3"], + ["phase-4", "phase-5"], + ["phase-6"], + ["phase-7"], + ["phase-8"] + ] +} diff --git a/skills/polaris-ops/SKILL.md b/skills/polaris-ops/SKILL.md new file mode 100644 index 0000000..a286515 --- /dev/null +++ b/skills/polaris-ops/SKILL.md @@ -0,0 +1,103 @@ +# Polaris Ops — DTP 開発オペレーションスキル + +## 概要 + +Deterministic Task Protocol (Polaris) の開発・テスト・デプロイを自動化するスキル。 +Phase 実行、品質チェック、引き継ぎ、進捗アナウンスを一括管理。 + +## トリガー + +polaris, dtp, 確定的, deterministic, phase, gate, autorun + +## コマンド + +### 品質チェック(毎回実行) + +```bash +# 3点セット: これが全 GREEN = 品質 OK +cargo build && cargo test && cargo clippy --all-targets --all-features -- -D warnings +``` + +### Phase 実行 + +```bash +# Phase N の TASKS.md を読んでチェックボックスを埋める +cat autorun/phase-{N}-*/TASKS.md + +# GATE 確認 +cat autorun/phase-{N}-*/GATE.md + +# Phase 完了時にタグを打つ +git tag v0.{N+1}-phase{N}-done +git push origin v0.{N+1}-phase{N}-done +``` + +### 引き継ぎ + +```bash +# HANDOFF_NOTE.md を作成(autorun/HANDOFF.md のテンプレートに従う) +# git commit + push +# 次のエージェントに通知 +~/bin/announce "Polaris: Phase {N} 完了。Phase {N+1} に引き継ぎます。" +``` + +### 進捗アナウンス + +```bash +# Phase 開始 +~/bin/announce "Polaris: Phase {N} 開始。{Phase名}を実行します。" + +# GATE 通過 +~/bin/announce "Polaris: Phase {N} GATE 通過。テスト全て成功。" + +# エラー +~/bin/announce "Polaris: Phase {N} エラー。{理由}。リトライ {M} 回目。" + +# 完了 +~/bin/announce "Polaris: Phase {N} 完了。次は Phase {N+1} です。" +``` + +### ロールバック + +```bash +# 安全地点に戻る +git tag # タグ一覧を確認 +git checkout v0.{N}-phase{N-1}-done +git checkout -b rollback/phase-{N}-retry +# 修正後 +git checkout main && git merge rollback/phase-{N}-retry +``` + +### Codex ディスパッチ + +```bash +# Phase N を Codex にアサイン +tmux send-keys -t %{pane} "codex 'Execute Phase {N}. Read autorun/phase-{N}-*/TASKS.md. Run cargo test when done. Write HANDOFF_NOTE.md.'" Enter +``` + +### Agent Skill Bus 記録 + +```bash +npx agent-skill-bus record-run \ + --agent {agent} \ + --skill polaris-ops \ + --task "Phase {N}: {summary}" \ + --result {success|fail} \ + --score {0.0-1.0} +``` + +## 判断基準 + +| 状況 | アクション | +|------|-----------| +| cargo test GREEN + clippy GREEN | Phase 続行 | +| cargo test FAIL | 修正してリトライ(最大3回) | +| 3回連続 FAIL | エスカレーション(人間に報告) | +| 人間が「止めろ」 | 即座に停止、commit + push | + +## 関連スキル + +- `context-and-impact`: コンテキスト収集(Phase A) +- `gitnexus-impact-analysis`: GNI 影響分析 +- `miyabi-ops`: エージェント振り分け +- `agent-skill-bus`: タスクキュー + 自己改善 diff --git a/skills/rust-llm-pitfalls/SKILL.md b/skills/rust-llm-pitfalls/SKILL.md new file mode 100644 index 0000000..d75fc8c --- /dev/null +++ b/skills/rust-llm-pitfalls/SKILL.md @@ -0,0 +1,210 @@ +# Rust LLM Pitfalls — LLM が Rust を書くとき間違いやすいポイント + +> LLM(Claude, GPT, Codex)が Rust コードを生成する際に頻出するミス。 +> エージェントはこのリストを実装前に必ず読み、該当パターンを避けること。 + +## 1. 所有権・借用 + +### 1.1 ❌ clone() の乱用 + +LLM は borrow checker エラーを `.clone()` で解決しようとする。 + +```rust +// ❌ LLM がやりがち +let tasks = self.store.tasks().clone(); // Vec 全体をコピー +for task in &tasks { ... } + +// ✅ 正しい: 参照で十分 +for task in self.store.tasks() { ... } +``` + +**ルール**: `clone()` を書く前に「参照で済まないか」を考える。`clone()` は最後の手段。 + +### 1.2 ❌ 可変参照と不変参照の同時使用 + +```rust +// ❌ LLM がやりがち: 同じ構造体から可変と不変を同時に借用 +let task = self.store.get_task(id); // &self(不変参照) +self.store.update_task(id, |t| { ... }); // &mut self(可変参照)← コンパイルエラー + +// ✅ 正しい: スコープを分ける +let state = { + let task = self.store.get_task(id).unwrap(); + task.state // Copy 型なのでスコープ外に持ち出せる +}; +self.store.update_task(id, |t| { t.state = state; }); +``` + +### 1.3 ❌ String と &str の混同 + +```rust +// ❌ LLM がやりがち +fn find_task(id: String) -> Option<&Task> { ... } + +// ✅ 正しい: 入力は &str、保存は String +fn find_task(id: &str) -> Option<&Task> { ... } +``` + +## 2. エラー処理 + +### 2.1 ❌ unwrap() の乱用 + +LLM は `Option` / `Result` を `unwrap()` で解決しようとする。 + +```rust +// ❌ 本番コードで unwrap → panic +let task = store.get_task(id).unwrap(); + +// ✅ 正しい: ? 演算子 or 明示的エラー +let task = store.get_task(id) + .ok_or_else(|| StoreError::UnknownTask(id.to_string()))?; +``` + +**ルール**: `unwrap()` はテストコード内のみ許可。本番コードでは `?` を使う。 + +### 2.2 ❌ エラー型の設計不足 + +LLM は `String` をエラー型にしようとする。 + +```rust +// ❌ LLM がやりがち +fn register_task(task: Task) -> Result<(), String> { ... } + +// ✅ 正しい: 専用 enum +fn register_task(task: Task) -> Result<(), ProtocolError> { ... } +``` + +## 3. serde / JSON + +### 3.1 ❌ rename_all の不整合 + +```rust +// ❌ snake_case と SCREAMING_SNAKE_CASE が混在 +#[derive(Serialize)] +#[serde(rename_all = "snake_case")] +pub enum RiskLevel { + LOW, // → "low" になる(意図と違う?) + MEDIUM, +} + +// ✅ 正しい: 意図を明確にする +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] // JSON では "LOW", "MEDIUM" +// または +#[serde(rename_all = "snake_case")] // JSON では "low", "medium" +``` + +### 3.2 ❌ Option フィールドのスキップ忘れ + +```rust +// ❌ null が JSON に出力される +pub merge_commit: Option, // → "merge_commit": null + +// ✅ null を消したいなら +#[serde(skip_serializing_if = "Option::is_none")] +pub merge_commit: Option, // → フィールドごと消える +``` + +### 3.3 ❌ DateTime のシリアライズ形式 + +```rust +// ❌ LLM が独自フォーマットを書こうとする +pub created_at: String, // "2026-04-10 00:00:00" ← 危険 + +// ✅ 正しい: chrono::DateTime + serde +pub created_at: DateTime, // ISO 8601 自動 +``` + +## 4. 並行性・ファイル操作 + +### 4.1 ❌ ファイル操作の非原子性 + +```rust +// ❌ LLM がやりがち: read → modify → write が原子的でない +let content = fs::read_to_string("tasks.json")?; +let mut doc: TasksDocument = serde_json::from_str(&content)?; +doc.tasks.push(new_task); +fs::write("tasks.json", serde_json::to_string_pretty(&doc)?)?; +// ↑ 書き込み中にクラッシュすると tasks.json が壊れる + +// ✅ 正しい: atomic rename +let tmp = "tasks.json.tmp"; +fs::write(tmp, serde_json::to_string_pretty(&doc)?)?; +fs::rename(tmp, "tasks.json")?; // OS レベルで原子的 +``` + +### 4.2 ❌ flock の使い方 + +```rust +// ❌ LLM が flock を忘れる +let content = fs::read_to_string(path)?; +// ↑ 別プロセスが同時に読んでいる可能性 + +// ✅ 正しい: fs2 で排他ロック +use fs2::FileExt; +let file = File::open(path)?; +file.lock_exclusive()?; // 他プロセスをブロック +let content = fs::read_to_string(path)?; +// ... 操作 ... +file.unlock()?; +``` + +### 4.3 ❌ HashMap の順序依存 + +```rust +// ❌ LLM が HashMap の順序に依存するコードを書く +let locks: HashMap = ...; +for (file, task_id) in &locks { + // 順序は保証されない!テストが環境で変わる +} + +// ✅ 正しい: BTreeMap を使うか、ソートしてから処理 +let mut files: Vec<_> = locks.keys().collect(); +files.sort(); +``` + +## 5. テスト + +### 5.1 ❌ テストで実ファイルシステムを汚す + +```rust +// ❌ /tmp に直接書く → テスト並列実行で衝突 +fs::write("/tmp/test-tasks.json", "...")?; + +// ✅ 正しい: tempfile crate +use tempfile::TempDir; +let dir = TempDir::new()?; +let path = dir.path().join("tasks.json"); +``` + +### 5.2 ❌ 時刻依存テスト + +```rust +// ❌ Utc::now() に依存 → テストが時間で変わる +let lock = TaskLock { locked_at: Utc::now(), ttl_secs: 300, ... }; +assert!(!lock.is_expired(Utc::now())); // 瞬間的に通るが不安定 + +// ✅ 正しい: 固定時刻を注入 +let now = Utc.with_ymd_and_hms(2026, 4, 10, 0, 0, 0).unwrap(); +let lock = TaskLock { locked_at: now, ttl_secs: 300, ... }; +assert!(!lock.is_expired(now + chrono::Duration::seconds(100))); +assert!(lock.is_expired(now + chrono::Duration::seconds(500))); +``` + +## 6. clippy 頻出警告 + +LLM が生成するコードで clippy が止めるもの: + +| 警告 | 原因 | 対処 | +|------|------|------| +| `needless_pass_by_value` | 引数を値で受けて消費しない | `&` に変更 | +| `derivable_impls` | 手書き Default が derive で済む | `#[derive(Default)]` | +| `cast_possible_wrap` | `u64 as i64` のオーバーフロー | `.try_into()` or `.cast_signed()` | +| `unnested_or_patterns` | matches! の書き方が冗長 | パターンをネスト | +| `redundant_clone` | 不要な clone | 削除 | +| `single_match` | match で1パターンだけ | if let に変更 | + +## このスキルの使い方 + +実装前にこのリストを一読し、該当パターンを避ける。 +clippy が deny なので、上記の多くはコンパイル時に自動検出される。 +ただし **ロジックの間違い(unwrap の本番使用、ファイル操作の非原子性)は clippy では検出されない**。人間またはレビューエージェントが確認する。