[追加] DTP (Deterministic Task Protocol) 設計文書・指示書を移植

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) <noreply@anthropic.com>
This commit is contained in:
林 駿甫 (Shunsuke Hayashi) 2026-04-10 01:07:25 +09:00
parent 2d500c3654
commit 146fcafc5e
50 changed files with 4895 additions and 0 deletions

View file

@ -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 <model>` | LLM model (default: minimax/minimax-m2.5) |
| `--base-url <url>` | LLM API base URL |
| `--api-key <key>` | LLM API key |
| `--concurrency <n>` | 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

View file

@ -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: "<error or symptom>"}) → Find related execution flows
2. gitnexus_context({name: "<suspect>"}) → 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
```

View file

@ -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: "<what you want to understand>"}) → Find related execution flows
4. gitnexus_context({name: "<symbol>"}) → 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
```

View file

@ -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
```

View file

@ -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
```

View file

@ -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
```

20
.codex/instructions.md Normal file
View file

@ -0,0 +1,20 @@
# Codex Instructions for Polaris
## 必読ファイル
1. CLAUDE.mdルール
2. autorun/INDEX.mdPhase DAG
3. 該当 Phase の TASKS.md
## 品質チェック(タスク完了前に必ず実行)
```bash
cargo test && cargo clippy --all-targets --all-features -- -D warnings
```
## コミットルール
日本語タグ形式: `[追加]`, `[修正]`, `[改善]`, `[整備]`, `[文書]`, `[検証]`, `[完了]`
## 禁止事項
- unsafe コード
- GATE バイパス
- テスト RED のまま完了宣言
- git push --force

1
.gitignore vendored
View file

@ -175,3 +175,4 @@ yarn-error.log*
.worktrees/
.ai/
.env
.gitnexus

102
AGENTS.md
View file

@ -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:start -->
# 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: "<error or symptom>"})` — find execution flows related to the issue
2. `gitnexus_context({name: "<suspect function>"})` — 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` |
<!-- gitnexus:end -->

102
CLAUDE.md
View file

@ -117,3 +117,105 @@ miyabi agent run coordinator --issue <番号>
---
**このファイルはClaude Codeが自動参照します。**
<!-- gitnexus:start -->
# 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: "<error or symptom>"})` — find execution flows related to the issue
2. `gitnexus_context({name: "<suspect function>"})` — 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` |
<!-- gitnexus:end -->

299
autorun/HANDOFF.md Normal file
View file

@ -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 → Codexworktree 間)
```
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 Codeworktree → 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 → Codexlocal → 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 mainlocal → 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 → GREENsender の作業が壊れていないことを確認)
[ ] 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
```

111
autorun/INDEX.md Normal file
View file

@ -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
```

148
autorun/ROLLBACK.md Normal file
View file

@ -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 参照 |

View file

@ -0,0 +1,7 @@
# Assignment: Phase 0
- **Agent**: Claude Code (local)
- **Parallel**: None (first phase)
- **Estimated**: 10 min
- **Depends on**: Nothing
- **Blocks**: Phase 1

View file

@ -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

View file

@ -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` を実行
- シンボリックリンク切れ → 参照先パスを確認して再作成

View file

@ -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

View file

@ -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)

View file

@ -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_prR3-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() のデフォルト値を適切に設定

View file

@ -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

View file

@ -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)

View file

@ -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 と明確に分離

View file

@ -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

View file

@ -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)

View file

@ -0,0 +1,39 @@
# Phase 3: Event StoreJSONL 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<TaskEvent>`, `replay_for_task(task_id) -> Vec<TaskEvent>`
- ファイルパス: `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

View file

@ -0,0 +1,7 @@
# Assignment: Phase 4
- **Agent**: Codex A (worktree)
- **Parallel**: None
- **Estimated**: 30 min
- **Depends on**: Phase 3 GREEN
- **Blocks**: Phase 6

View file

@ -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)

View file

@ -0,0 +1,41 @@
# Phase 4: File Lock Managerlease + heartbeat
> 依存: Phase 3 GREENEventStore が必要)
> 承認ゲート: 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<Utc>` フィールド追加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

View file

@ -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

View file

@ -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)

View file

@ -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<GitHubEvidence, SyncError>;
fn fetch_issue_state(&self, issue_number: u64) -> Result<IssueState, SyncError>;
}
```
- [ ] `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)

View file

@ -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

View file

@ -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

View file

@ -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 hatchesR3-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 pathdraft → ... → 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` 全 GREEN20 件以上)
- E2E happy path テスト GREEN
- 全 GATE 拒否テスト GREEN8 件以上)
- escape hatch テスト GREEN2 件以上)
- event log に全操作が記録されている
## リトライ条件
- Protocol と StateMachine の責務境界で混乱 → Protocol が唯一の外部窓口、StateMachine は internal
- EventStore と SnapshotStore の初期化順序 → Protocol::new() で両方を初期化

View file

@ -0,0 +1,7 @@
# Assignment: Phase 7
- **Agent**: Codex B (worktree)
- **Parallel**: None
- **Estimated**: 45 min
- **Depends on**: Phase 6 GREEN
- **Blocks**: Phase 8

View file

@ -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)

View file

@ -0,0 +1,41 @@
# Phase 7: CLIdtp コマンド)
> 依存: Phase 6 GREEN
> 承認ゲート: 全サブコマンドが動作 + `dtp run-e2e` が GREEN
## タスク
- [ ] `src/main.rs` に clap ベースの CLI を実装Cargo.toml に `clap = { version = "4", features = ["derive"] }` 追加)
- [ ] サブコマンド一覧:
```
dtp register --issue <N> --title <T> [--deps <id,...>] [--mode github-pr|manual|external-op]
dtp dag # DAG 可視化(テキスト)
dtp dispatchable # 実行可能タスク一覧
dtp impact <task-id> --risk <L|M|H|C> --symbols <N> [--approve]
dtp assign <task-id> --agent <A> --node <N> --files <F,...>
dtp heartbeat <task-id>
dtp branch <task-id> <branch-name>
dtp pr <task-id> <pr-number>
dtp verify-merge <task-id> # GitHub API 経由
dtp confirm-done <task-id>
dtp status [task-id] # JSON or human-readable
dtp locks # ロック一覧
dtp unlock <task-id> --reason <R> --operator <O>
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 を確認

View file

@ -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)

View file

@ -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

View file

@ -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 確認

170
docs/dtp/GIT-RULES.md Normal file
View file

@ -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 完了時に PRmain → 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コミットにまとめる | 分割すること |

414
docs/dtp/PLAN-v1.md Normal file
View file

@ -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: ImpactC&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<ManagedTask>): void
getFileLocks(): Record<string, string> // file → taskId
setFileLock(file: string, taskId: string): void
removeFileLock(file: string): void
}
export interface TasksDocument {
version: number;
lastUpdated: string;
tasks: ManagedTask[];
fileLocks: Record<string, string>;
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 Statetasks.jsonと Actual StateGitHubの差分を検出して reconcile する。bidirectional-sync がこれに相当。
- **Database MVCC / Pessimistic Locking**: ファイルロックは悲観的ロック。TTL はデッドロック防止。
- **Apache Airflow DAG**: タスク間の依存関係をDAGで表現し、トポロジカルソートで実行順序を決定。task-dag.ts がこれに相当。
- **Harness Engineeringdocs/design/harness-engineering.md**: 「制約をファイルに書き込み、会話ではなくハーネスで強制する」— 本プロトコルの思想的根拠。
---
## 実装順序とリスク
| Phase | 工数目安 | リスク | 依存 |
|-------|---------|--------|------|
| 1. 型拡張 | 小 | LOW | なし |
| 2. DAG 移植 | 中 | LOW | Phase 1 |
| 3. ロック移植 | 中 | MEDIUMTTL設計 | Phase 1 |
| 4. プロトコル | 大 | HIGH核心 | Phase 1,2,3 |
| 5. TaskStore | 中 | LOW | Phase 1 |
| 6. GitHub同期強化 | 中 | MEDIUMAPI制限 | Phase 4,5 |
| 7. CLI | 小 | LOW | Phase 4,5 |

921
docs/dtp/PLAYBOOK.md Normal file
View file

@ -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 StoreJSONL + 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<string, unknown>;
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<string, { taskId: string; agent: string; node: string; expiresAt: string }>;
}
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 Managerlease + 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 解放
}
// heartbeatlease 更新)
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 acquire2プロセスが同時に 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<GitHubEvidence> {
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<boolean> {
// 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<string, 'github' | 'local'> = {
'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<SyncResult> {
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<void> {
// 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<void> {
// 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<void> {
// 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 HatchesR3-5: 監査可能な例外経路)
// ==========================================
forceUnlock(taskId: string, reason: string, operator: string): void {
// EVENT: lock_released (forced=true, reason, operator)
}
reconcileFromGitHub(taskId: string): Promise<void> {
// 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 行 | MEDIUMatomic write |
| 4 | 3 | ~150 行 | MEDIUMlease + heartbeat |
| 5 | 3 | ~200 行 | HIGHGitHub API 依存) |
| 6 | 2,3,4,5 | ~300 行 | HIGH核心の統合 |
| 7 | 6 | ~150 行 | LOWCLI ボイラープレート) |
| 8 | 7 | ~100 行 | LOWE2E テスト) |
| **合計** | | **~1,430 行** | |
---
## 5. エージェント割り当てMaestro Playbook 実行時)
| Phase | 推奨エージェント | 理由 |
|-------|---------------|------|
| 0 | Claude Codeローカル | テスト実行 + インデックス確認 |
| 1 | Codexworktree | 型定義のみ、副作用なし |
| 2 | Claude Codeローカル | ステートマシン改造は対話が必要 |
| 3 | Codexworktree | event store は独立実装可能 |
| 4 | Codexworktree | lock manager も独立実装可能 |
| 5 | Claude Codeローカル | GitHub API の挙動確認が必要 |
| 6 | Claude Codeローカル | 全コンポーネント統合は対話が必要 |
| 7 | Codexworktree | 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._

427
docs/dtp/UML.md Normal file
View file

@ -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<br/>DBスキーマ変更<br/>state: done ✅]
end
subgraph "DAG Level 1Level 0 完了後)"
T1[task-001<br/>API実装<br/>state: implementing 🔒]
end
subgraph "DAG Level 2Level 1 完了後)"
T2[task-002<br/>フロントエンド<br/>state: blocked ⏳]
T3[task-003<br/>テスト作成<br/>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<br/>27 rules]
SYNC[GitHub Sync<br/>bidirectional]
EXEC[Task Executor<br/>worktree]
end
subgraph "KOTOWARI openclaw-crowd"
DAG[DAG Engine<br/>topological sort]
SCHED[Scheduler<br/>priority queue]
end
subgraph "agent-skill-bus"
QUEUE[JSONL Queue<br/>prompt requests]
LOCK[File Locks<br/>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<br/>27 rules + GATE条件]
DAG[DAG Engine<br/>Kahn sort + 依存解決]
LOCK[File Lock Manager<br/>TTL + 競合検出]
end
subgraph "Store Layer永続化"
TJ[tasks.json<br/>確定的スキーマ]
GH[GitHub Issues/PR<br/>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<br/>auth.ts 編集中]
M1[MEMORY.md<br/>v3]
end
subgraph Windows
A2[Agent B<br/>auth.ts 編集中]
M2[MEMORY.md<br/>v2 ← 古い!]
end
subgraph mainmini
A3[Agent C<br/>何をしてるか不明]
M3[MEMORY.md<br/>v1 ← もっと古い!]
end
A1 -.->|"💥 競合"| A2
M1 -.->|"❌ ズレ"| M2
M2 -.->|"❌ ズレ"| M3
```
### To-Be
```mermaid
graph TB
subgraph "tasks.json共有 SSOT"
TJ["task-001: implementing<br/>lock: Agent A @ MacBook<br/>files: [auth.ts] 🔒<br/><br/>task-002: blocked ⏳<br/>depends: task-001<br/><br/>task-003: pending<br/>files: [user.ts] — 別ファイルなので並行OK"]
end
subgraph MacBook
A1[Agent A<br/>auth.ts 編集 ✅<br/>ロック保持]
end
subgraph Windows
A2[Agent B<br/>auth.ts 触れない 🚫<br/>task-002 blocked]
end
subgraph mainmini
A3[Agent C<br/>user.ts 編集 ✅<br/>task-003 別ロック]
end
A1 -->|"read/write"| TJ
A2 -->|"read only"| TJ
A3 -->|"read/write"| TJ
TJ <-->|"sync"| GH[GitHub Issues<br/>Fact SSOT]
```
---
## 7. GATE チェーン: 1タスクの完全ライフサイクル
```mermaid
graph LR
START((Start)) --> G0{GATE 0<br/>Issue?}
G0 -->|No| REJECT0[❌ 登録拒否]
G0 -->|Yes| DRAFT[draft]
DRAFT --> G1{GATE 1<br/>title+desc?}
G1 -->|No| REJECT1[❌]
G1 -->|Yes| PENDING[pending]
PENDING --> G2{GATE 2<br/>deps done?}
G2 -->|No| BLOCKED[blocked ⏳]
G2 -->|Yes| ANALYZING[analyzing]
ANALYZING --> G3{GATE 3<br/>impact?}
G3 -->|No| REJECT3[❌ GNI回せ]
G3 -->|HIGH| HUMAN{人間承認?}
G3 -->|LOW/MED| G4
HUMAN -->|No| REJECT_H[❌]
HUMAN -->|Yes| G4
G4{GATE 4<br/>lock?} -->|競合| REJECT4[❌ 待て]
G4 -->|OK| IMPL[implementing 🔒]
IMPL --> G5{GATE 5<br/>branch?}
G5 -->|No| REJECT5[❌]
G5 -->|Yes| G6{GATE 6<br/>PR?}
G6 -->|No| REJECT6[❌]
G6 -->|Yes| REVIEW[reviewing]
REVIEW --> G7{GATE 7<br/>merge?}
G7 -->|No| REJECT7[❌]
G7 -->|Yes| DONE[done ✅]
DONE --> G8{GATE 8<br/>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_

View file

@ -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 の原子性です。

View file

@ -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 か」を状態ごとに切り分ける必要があります。

View file

@ -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 だけで封じる」保証には届きません。

160
project_memory/tasks.json Normal file
View file

@ -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"]
]
}

103
skills/polaris-ops/SKILL.md Normal file
View file

@ -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`: タスクキュー + 自己改善

View file

@ -0,0 +1,210 @@
# Rust LLM Pitfalls — LLM が Rust を書くとき間違いやすいポイント
> LLMClaude, 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<String>, // → "merge_commit": null
// ✅ null を消したいなら
#[serde(skip_serializing_if = "Option::is_none")]
pub merge_commit: Option<String>, // → フィールドごと消える
```
### 3.3 ❌ DateTime のシリアライズ形式
```rust
// ❌ LLM が独自フォーマットを書こうとする
pub created_at: String, // "2026-04-10 00:00:00" ← 危険
// ✅ 正しい: chrono::DateTime<Utc> + serde
pub created_at: DateTime<Utc>, // 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<String, String> = ...;
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 では検出されない**。人間またはレビューエージェントが確認する。