From 247b500449650ede41935683360470fe984df9ca Mon Sep 17 00:00:00 2001 From: nrslib <38722970+nrslib@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:53:39 +0900 Subject: [PATCH] =?UTF-8?q?Faceted=20Prompting=20=E5=B0=8E=E5=85=A5:=20kno?= =?UTF-8?q?wledge=20=E3=82=BB=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=80=81=E3=82=B9=E3=82=AD=E3=83=AB=E3=83=89=E3=82=AD?= =?UTF-8?q?=E3=83=A5=E3=83=A1=E3=83=B3=E3=83=88=E5=88=B7=E6=96=B0=E3=80=81?= =?UTF-8?q?=E3=82=B3=E3=83=9E=E3=83=B3=E3=83=89=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E5=BB=83=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + builtins/en/knowledge/backend.md | 485 ++++++++++++++++++ builtins/en/piece-categories.yaml | 42 +- builtins/en/pieces/coding-hybrid-codex.yaml | 51 +- builtins/en/pieces/coding.yaml | 6 + builtins/en/pieces/default-hybrid-codex.yaml | 64 +-- builtins/en/pieces/default.yaml | 6 +- .../en/pieces/expert-cqrs-hybrid-codex.yaml | 101 ++-- builtins/en/pieces/expert-cqrs.yaml | 8 +- builtins/en/pieces/expert-hybrid-codex.yaml | 108 ++-- builtins/en/pieces/expert.yaml | 7 +- builtins/en/pieces/minimal-hybrid-codex.yaml | 43 +- .../en/pieces/passthrough-hybrid-codex.yaml | 16 +- .../review-fix-minimal-hybrid-codex.yaml | 43 +- builtins/en/pieces/review-only.yaml | 6 + builtins/ja/knowledge/backend.md | 485 ++++++++++++++++++ builtins/ja/knowledge/cqrs-es.md | 313 +++++++++++ builtins/ja/knowledge/frontend.md | 203 +++++++- builtins/ja/piece-categories.yaml | 41 +- builtins/ja/pieces/coding-hybrid-codex.yaml | 51 +- builtins/ja/pieces/coding.yaml | 6 + builtins/ja/pieces/default-hybrid-codex.yaml | 55 +- builtins/ja/pieces/default.yaml | 6 +- .../ja/pieces/expert-cqrs-hybrid-codex.yaml | 110 ++-- builtins/ja/pieces/expert-cqrs.yaml | 8 +- builtins/ja/pieces/expert-hybrid-codex.yaml | 96 ++-- builtins/ja/pieces/expert.yaml | 7 +- builtins/ja/pieces/minimal-hybrid-codex.yaml | 51 +- .../ja/pieces/passthrough-hybrid-codex.yaml | 16 +- .../review-fix-minimal-hybrid-codex.yaml | 51 +- builtins/ja/pieces/review-only.yaml | 6 + builtins/skill/SKILL.md | 70 ++- builtins/skill/references/engine.md | 112 ++-- builtins/skill/references/yaml-schema.md | 96 +++- builtins/skill/takt-command.md | 37 -- docs/README.ja.md | 1 + docs/prompt-composition.ja.md | 149 ++++++ docs/prompt-composition.md | 149 ++++++ src/features/config/deploySkill.ts | 44 +- tools/generate-hybrid-codex.mjs | 260 ++++++++++ 40 files changed, 2613 insertions(+), 797 deletions(-) create mode 100644 builtins/en/knowledge/backend.md create mode 100644 builtins/ja/knowledge/backend.md delete mode 100644 builtins/skill/takt-command.md create mode 100644 docs/prompt-composition.ja.md create mode 100644 docs/prompt-composition.md create mode 100644 tools/generate-hybrid-codex.mjs diff --git a/README.md b/README.md index a9066cc..c0bafbc 100644 --- a/README.md +++ b/README.md @@ -787,6 +787,7 @@ export TAKT_OPENAI_API_KEY=sk-... ## Documentation +- [Faceted Prompting](./docs/prompt-composition.md) - Separation of Concerns for AI prompts (Persona, Stance, Instruction, Knowledge, Report Format) - [Piece Guide](./docs/pieces.md) - Creating and customizing pieces - [Agent Guide](./docs/agents.md) - Configuring custom agents - [Changelog](../CHANGELOG.md) - Version history diff --git a/builtins/en/knowledge/backend.md b/builtins/en/knowledge/backend.md new file mode 100644 index 0000000..2e0010f --- /dev/null +++ b/builtins/en/knowledge/backend.md @@ -0,0 +1,485 @@ +# Backend Expertise + +## Hexagonal Architecture (Ports and Adapters) + +Dependency direction flows from outer to inner layers. Reverse dependencies are prohibited. + +``` +adapter (external) → application (use cases) → domain (business logic) +``` + +Directory structure: + +``` +{domain-name}/ +├── domain/ # Domain layer (framework-independent) +│ ├── model/ +│ │ └── aggregate/ # Aggregate roots, value objects +│ └── service/ # Domain services +├── application/ # Application layer (use cases) +│ ├── usecase/ # Orchestration +│ └── query/ # Query handlers +├── adapter/ # Adapter layer (external connections) +│ ├── inbound/ # Input adapters +│ │ └── rest/ # REST Controller, Request/Response DTOs +│ └── outbound/ # Output adapters +│ └── persistence/ # Entity, Repository implementations +└── api/ # Public interface (referenceable by other domains) + └── events/ # Domain events +``` + +Layer responsibilities: + +| Layer | Responsibility | May Depend On | Must Not Depend On | +|-------|---------------|---------------|-------------------| +| domain | Business logic, invariants | Standard library only | Frameworks, DB, external APIs | +| application | Use case orchestration | domain | Concrete adapter implementations | +| adapter/inbound | HTTP request handling, DTO conversion | application, domain | outbound adapter | +| adapter/outbound | DB persistence, external API calls | domain (interfaces) | application | + +```kotlin +// CORRECT - Domain layer is framework-independent +data class Order(val orderId: String, val status: OrderStatus) { + fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) + return OrderConfirmedEvent(orderId, confirmedBy) + } +} + +// WRONG - Spring annotations in domain layer +@Entity +data class Order( + @Id val orderId: String, + @Enumerated(EnumType.STRING) val status: OrderStatus +) { + fun confirm(confirmedBy: String) { ... } +} +``` + +| Criteria | Judgment | +|----------|----------| +| Framework dependencies in domain layer (@Entity, @Component, etc.) | REJECT | +| Controller directly referencing Repository | REJECT. Must go through UseCase layer | +| Outward dependencies from domain layer (DB, HTTP, etc.) | REJECT | +| Direct dependencies between adapters (inbound → outbound) | REJECT | + +## API Layer Design (Controller) + +Keep Controllers thin. Their only job: receive request → delegate to UseCase → return response. + +```kotlin +// CORRECT - Thin Controller +@RestController +@RequestMapping("/api/orders") +class OrdersController( + private val placeOrderUseCase: PlaceOrderUseCase, + private val queryGateway: QueryGateway +) { + // Command: state change + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + fun post(@Valid @RequestBody request: OrderPostRequest): OrderPostResponse { + val output = placeOrderUseCase.execute(request.toInput()) + return OrderPostResponse(output.orderId) + } + + // Query: read + @GetMapping("/{id}") + fun get(@PathVariable id: String): ResponseEntity { + val detail = queryGateway.query(FindOrderQuery(id), OrderDetail::class.java).join() + ?: return ResponseEntity.notFound().build() + return ResponseEntity.ok(OrderGetResponse.from(detail)) + } +} + +// WRONG - Business logic in Controller +@PostMapping +fun post(@RequestBody request: OrderPostRequest): ResponseEntity { + // Validation, stock check, calculation... should NOT be in Controller + val stock = inventoryRepository.findByProductId(request.productId) + if (stock.quantity < request.quantity) { + return ResponseEntity.badRequest().body("Insufficient stock") + } + val total = request.quantity * request.unitPrice * 1.1 // Tax calculation + orderRepository.save(OrderEntity(...)) + return ResponseEntity.ok(...) +} +``` + +### Request/Response DTO Design + +Define Request and Response as separate types. Never expose domain models directly via API. + +```kotlin +// Request: validation annotations + init block +data class OrderPostRequest( + @field:NotBlank val customerId: String, + @field:NotNull val items: List +) { + init { + require(items.isNotEmpty()) { "Order must contain at least one item" } + } + + fun toInput() = PlaceOrderInput(customerId = customerId, items = items.map { it.toItem() }) +} + +// Response: factory method from() for conversion +data class OrderGetResponse( + val orderId: String, + val status: String, + val customerName: String +) { + companion object { + fun from(detail: OrderDetail) = OrderGetResponse( + orderId = detail.orderId, + status = detail.status.name, + customerName = detail.customerName + ) + } +} +``` + +| Criteria | Judgment | +|----------|----------| +| Returning domain model directly as response | REJECT | +| Business logic in Request DTO | REJECT. Only validation is allowed | +| Domain logic (calculations, etc.) in Response DTO | REJECT | +| Same type for Request and Response | REJECT | + +### RESTful Action Design + +Express state transitions as verb sub-resources. + +``` +POST /api/orders → Create order +GET /api/orders/{id} → Get order +GET /api/orders → List orders +POST /api/orders/{id}/approve → Approve (state transition) +POST /api/orders/{id}/cancel → Cancel (state transition) +``` + +| Criteria | Judgment | +|----------|----------| +| PUT/PATCH for domain operations (approve, cancel, etc.) | REJECT. Use POST + verb sub-resource | +| Single endpoint branching into multiple operations | REJECT. Separate endpoints per operation | +| DELETE for soft deletion | REJECT. Use POST + explicit operation like cancel | + +## Validation Strategy + +Validation has different roles at each layer. Do not centralize everything in one place. + +| Layer | Responsibility | Mechanism | Example | +|-------|---------------|-----------|---------| +| API layer | Structural validation | `@NotBlank`, `init` block | Required fields, types, format | +| UseCase layer | Business rule verification | Read Model queries | Duplicate checks, precondition existence | +| Domain layer | State transition invariants | `require` | "Cannot approve unless PENDING" | + +```kotlin +// API layer: "Is the input structurally correct?" +data class OrderPostRequest( + @field:NotBlank val customerId: String, + val from: LocalDateTime, + val to: LocalDateTime +) { + init { + require(!to.isBefore(from)) { "End date must be on or after start date" } + } +} + +// UseCase layer: "Is this business-wise allowed?" (Read Model reference) +fun execute(input: PlaceOrderInput) { + customerRepository.findById(input.customerId) + ?: throw CustomerNotFoundException("Customer does not exist") + validateNoOverlapping(input) // Duplicate check + commandGateway.send(buildCommand(input)) +} + +// Domain layer: "Is this operation allowed in current state?" +fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) { "Cannot confirm in current state" } + return OrderConfirmedEvent(orderId, confirmedBy) +} +``` + +| Criteria | Judgment | +|----------|----------| +| Domain state transition rules in API layer | REJECT | +| Business rule verification in Controller | REJECT. Belongs in UseCase layer | +| Structural validation (@NotBlank, etc.) in domain | REJECT. Belongs in API layer | +| UseCase-level validation inside Aggregate | REJECT. Read Model queries belong in UseCase layer | + +## Error Handling + +### Exception Hierarchy Design + +Domain exceptions are hierarchized using sealed classes. HTTP status code mapping is done at the Controller layer. + +```kotlin +// Domain exceptions: sealed class ensures exhaustiveness +sealed class OrderException(message: String) : RuntimeException(message) +class OrderNotFoundException(message: String) : OrderException(message) +class InvalidOrderStateException(message: String) : OrderException(message) +class InsufficientStockException(message: String) : OrderException(message) + +// Controller layer maps to HTTP status codes +@RestControllerAdvice +class OrderExceptionHandler { + @ExceptionHandler(OrderNotFoundException::class) + fun handleNotFound(e: OrderNotFoundException) = + ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse(e.message)) + + @ExceptionHandler(InvalidOrderStateException::class) + fun handleInvalidState(e: InvalidOrderStateException) = + ResponseEntity.status(HttpStatus.CONFLICT).body(ErrorResponse(e.message)) + + @ExceptionHandler(InsufficientStockException::class) + fun handleInsufficientStock(e: InsufficientStockException) = + ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(ErrorResponse(e.message)) +} +``` + +| Criteria | Judgment | +|----------|----------| +| HTTP status codes in domain exceptions | REJECT. Domain must not know about HTTP | +| Throwing generic Exception or RuntimeException | REJECT. Use specific exception types | +| Empty try-catch blocks | REJECT | +| Controller swallowing exceptions and returning 200 | REJECT | + +## Domain Model Design + +### Immutable + require + +Domain models are designed as `data class` (immutable), with invariants enforced via `init` blocks and `require`. + +```kotlin +data class Order( + val orderId: String, + val status: OrderStatus = OrderStatus.PENDING +) { + // Static factory method via companion object + companion object { + fun place(orderId: String, customerId: String): OrderPlacedEvent { + require(customerId.isNotBlank()) { "Customer ID cannot be blank" } + return OrderPlacedEvent(orderId, customerId) + } + } + + // Instance method for state transition → returns event + fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) { "Cannot confirm in current state" } + return OrderConfirmedEvent(orderId, confirmedBy, LocalDateTime.now()) + } + + // Immutable state update + fun apply(event: OrderEvent): Order = when (event) { + is OrderPlacedEvent -> Order(orderId = event.orderId) + is OrderConfirmedEvent -> copy(status = OrderStatus.CONFIRMED) + is OrderCancelledEvent -> copy(status = OrderStatus.CANCELLED) + } +} +``` + +| Criteria | Judgment | +|----------|----------| +| `var` fields in domain model | REJECT. Use `copy()` for immutable updates | +| Factory without validation | REJECT. Enforce invariants with `require` | +| Domain model calling external services | REJECT. Pure functions only | +| Direct field mutation via setters | REJECT | + +### Value Objects + +Wrap primitive types (String, Int) with domain meaning. + +```kotlin +// ID types: prevent mix-ups via type safety +data class OrderId(@get:JsonValue val value: String) { + init { require(value.isNotBlank()) { "Order ID cannot be blank" } } + override fun toString(): String = value +} + +// Range types: enforce compound invariants +data class DateRange(val from: LocalDateTime, val to: LocalDateTime) { + init { require(!to.isBefore(from)) { "End date must be on or after start date" } } +} + +// Metadata types: ancillary information in event payloads +data class ApprovalInfo(val approvedBy: String, val approvalTime: LocalDateTime) +``` + +| Criteria | Judgment | +|----------|----------| +| Same-typed IDs that can be mixed up (orderId and customerId both String) | Consider wrapping in value objects | +| Same field combinations (from/to, etc.) appearing in multiple places | Extract to value object | +| Value object without init block | REJECT. Enforce invariants | + +## Repository Pattern + +Define interface in domain layer, implement in adapter/outbound. + +```kotlin +// domain/: Interface (port) +interface OrderRepository { + fun findById(orderId: String): Order? + fun save(order: Order) +} + +// adapter/outbound/persistence/: Implementation (adapter) +@Repository +class JpaOrderRepository( + private val jpaRepository: OrderJpaRepository +) : OrderRepository { + override fun findById(orderId: String): Order? { + return jpaRepository.findById(orderId).orElse(null)?.toDomain() + } + override fun save(order: Order) { + jpaRepository.save(OrderEntity.from(order)) + } +} +``` + +### Read Model Entity (JPA Entity) + +Read Model JPA Entities are defined separately from domain models. `var` (mutable) fields are acceptable here. + +```kotlin +@Entity +@Table(name = "orders") +data class OrderEntity( + @Id val orderId: String, + var customerId: String, + @Enumerated(EnumType.STRING) var status: OrderStatus, + var metadata: String? = null +) +``` + +| Criteria | Judgment | +|----------|----------| +| Domain model doubling as JPA Entity | REJECT. Separate them | +| Business logic in Entity | REJECT. Entity is data structure only | +| Repository implementation in domain layer | REJECT. Belongs in adapter/outbound | + +## Authentication & Authorization Placement + +Authentication and authorization are cross-cutting concerns handled at the appropriate layer. + +| Concern | Placement | Mechanism | +|---------|-----------|-----------| +| Authentication (who) | Filter / Interceptor layer | JWT verification, session validation | +| Authorization (permissions) | Controller layer | `@PreAuthorize("hasRole('ADMIN')")` | +| Data access control (own data only) | UseCase layer | Verified as business rule | + +```kotlin +// Controller layer: role-based authorization +@PostMapping("/{id}/approve") +@PreAuthorize("hasRole('FACILITY_ADMIN')") +fun approve(@PathVariable id: String, @RequestBody request: ApproveRequest) { ... } + +// UseCase layer: data access control +fun execute(input: DeleteInput, currentUserId: String) { + val entity = repository.findById(input.id) + ?: throw NotFoundException("Not found") + require(entity.ownerId == currentUserId) { "Cannot operate on another user's data" } + // ... +} +``` + +| Criteria | Judgment | +|----------|----------| +| Authorization logic in UseCase or domain layer | REJECT. Belongs in Controller layer | +| Data access control in Controller | REJECT. Belongs in UseCase layer | +| Authentication processing inside Controller | REJECT. Belongs in Filter/Interceptor | + +## Test Strategy + +### Test Pyramid + +``` + ┌─────────────┐ + │ E2E Test │ ← Few: verify full API flow + ├─────────────┤ + │ Integration │ ← Repository, Controller integration verification + ├─────────────┤ + │ Unit Test │ ← Many: independent tests for domain models, UseCases + └─────────────┘ +``` + +### Domain Model Testing + +Domain models are framework-independent, enabling pure unit tests. + +```kotlin +class OrderTest { + // Helper: build aggregate in specific state + private fun pendingOrder(): Order { + val event = Order.place("order-1", "customer-1") + return Order.from(event) + } + + @Nested + inner class Confirm { + @Test + fun `can confirm from PENDING state`() { + val order = pendingOrder() + val event = order.confirm("admin-1") + assertEquals("order-1", event.orderId) + } + + @Test + fun `cannot confirm from CONFIRMED state`() { + val order = pendingOrder().let { it.apply(it.confirm("admin-1")) } + assertThrows { + order.confirm("admin-2") + } + } + } +} +``` + +Testing rules: +- Build state transitions via helper methods (each test is independent) +- Group by operation using `@Nested` +- Test both happy path and error cases (invalid state transitions) +- Verify exception types with `assertThrows` + +### UseCase Testing + +Test UseCases with mocks. Inject external dependencies. + +```kotlin +class PlaceOrderUseCaseTest { + private val commandGateway = mockk() + private val customerRepository = mockk() + private val useCase = PlaceOrderUseCase(commandGateway, customerRepository) + + @Test + fun `throws error when customer does not exist`() { + every { customerRepository.findById("unknown") } returns null + + assertThrows { + useCase.execute(PlaceOrderInput(customerId = "unknown", items = listOf(...))) + } + } +} +``` + +| Criteria | Judgment | +|----------|----------| +| Using mocks for domain model tests | REJECT. Test domain purely | +| UseCase tests connecting to real DB | REJECT. Use mocks | +| Tests requiring framework startup | REJECT for unit tests | +| Missing error case tests for state transitions | REJECT | + +## Anti-Pattern Detection + +REJECT when these patterns are found: + +| Anti-Pattern | Problem | +|--------------|---------| +| Smart Controller | Business logic concentrated in Controller | +| Anemic Domain Model | Domain model is just a data structure with setters/getters | +| God Service | All operations concentrated in a single Service class | +| Direct Repository Access | Controller directly referencing Repository | +| Domain Leakage | Domain logic leaking into adapter layer | +| Entity Reuse | JPA Entity reused as domain model | +| Swallowed Exceptions | Empty catch blocks | +| Magic Strings | Hardcoded status strings, etc. | diff --git a/builtins/en/piece-categories.yaml b/builtins/en/piece-categories.yaml index a06cfb7..a7b6d6a 100644 --- a/builtins/en/piece-categories.yaml +++ b/builtins/en/piece-categories.yaml @@ -1,46 +1,38 @@ piece_categories: - "🚀 Quick Start": + 🚀 Quick Start: pieces: - default - passthrough - coding - minimal - - "🔍 Review & Fix": + 🔍 Review & Fix: pieces: - review-fix-minimal - - "🎨 Frontend": - {} - - "⚙️ Backend": - {} - - "🔧 Expert": - "Full Stack": + 🎨 Frontend: {} + ⚙️ Backend: {} + 🔧 Expert: + Full Stack: pieces: - expert - expert-cqrs - - "🔀 Hybrid (Codex Coding)": - "🚀 Quick Start": + 🔀 Hybrid (Codex Coding): + 🚀 Quick Start: pieces: + - coding-hybrid-codex - default-hybrid-codex - - passthrough-hybrid-codex - minimal-hybrid-codex - "🔍 Review & Fix": + - passthrough-hybrid-codex + 🔧 Expert: + pieces: + - expert-cqrs-hybrid-codex + - expert-hybrid-codex + 🔍 Review & Fix: pieces: - review-fix-minimal-hybrid-codex - "🔧 Expert": - pieces: - - expert-hybrid-codex - - expert-cqrs-hybrid-codex - - "Others": + Others: pieces: - research - magi - review-only - show_others_category: true -others_category_name: "Others" +others_category_name: Others diff --git a/builtins/en/pieces/coding-hybrid-codex.yaml b/builtins/en/pieces/coding-hybrid-codex.yaml index 4ba8f4f..3bf74fe 100644 --- a/builtins/en/pieces/coding-hybrid-codex.yaml +++ b/builtins/en/pieces/coding-hybrid-codex.yaml @@ -1,62 +1,31 @@ -# Coding TAKT Piece -# Plan -> Implement -> Parallel Review (AI + Architecture) -> Fix if needed -# -# Lightweight development piece with planning and parallel reviews. -# architect-planner investigates and organizes requirements, resolving unknowns by reading code. -# After parallel reviews, completes directly if no issues, enabling fast feedback loops. -# -# Flow: -# plan (requirements investigation & planning) -# ↓ -# implement (implementation) -# ↓ -# reviewers (parallel review) -# ├─ ai_review (AI-specific issue detection) -# └─ arch-review (design compliance check) -# ↓ -# [judgment] -# ├─ all(approved) → COMPLETE -# └─ any(needs_fix) → fix → reviewers (re-review) -# -# Template Variables (auto-injected by buildInstruction): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request -# {previous_response} - Output from the previous movement -# {user_inputs} - Accumulated user inputs during piece -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from coding.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: coding-hybrid-codex description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete) - max_iterations: 20 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + architecture: ../knowledge/architecture.md personas: architect-planner: ../personas/architect-planner.md coder: ../personas/coder.md ai-antipattern-reviewer: ../personas/ai-antipattern-reviewer.md architecture-reviewer: ../personas/architecture-reviewer.md - instructions: plan: ../instructions/plan.md implement: ../instructions/implement.md ai-review: ../instructions/ai-review.md review-arch: ../instructions/review-arch.md fix: ../instructions/fix.md - report_formats: plan: ../report-formats/plan.md ai-review: ../report-formats/ai-review.md architecture-review: ../report-formats/architecture-review.md - initial_movement: plan - movements: - name: plan edit: false @@ -79,15 +48,15 @@ movements: - condition: Requirements are unclear, insufficient information next: ABORT instruction: plan - - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: architecture report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -113,7 +82,6 @@ movements: requires_user_input: true interactive_only: true instruction: implement - - name: reviewers parallel: - name: ai_review @@ -133,11 +101,11 @@ movements: - condition: No AI-specific issues - condition: AI-specific issues found instruction: ai-review - - name: arch-review edit: false persona: architecture-reviewer stance: review + knowledge: architecture report: name: 05-architect-review.md format: architecture-review @@ -151,20 +119,19 @@ movements: - condition: approved - condition: needs_fix instruction: review-arch - rules: - condition: all("No AI-specific issues", "approved") next: COMPLETE - condition: any("AI-specific issues found", "needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: architecture allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/coding.yaml b/builtins/en/pieces/coding.yaml index 2941174..6e0077b 100644 --- a/builtins/en/pieces/coding.yaml +++ b/builtins/en/pieces/coding.yaml @@ -37,6 +37,9 @@ stances: review: ../stances/review.md testing: ../stances/testing.md +knowledge: + architecture: ../knowledge/architecture.md + personas: architect-planner: ../personas/architect-planner.md coder: ../personas/coder.md @@ -87,6 +90,7 @@ movements: - coding - testing session: refresh + knowledge: architecture report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -137,6 +141,7 @@ movements: edit: false persona: architecture-reviewer stance: review + knowledge: architecture report: name: 05-architect-review.md format: architecture-review @@ -163,6 +168,7 @@ movements: stance: - coding - testing + knowledge: architecture allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/default-hybrid-codex.yaml b/builtins/en/pieces/default-hybrid-codex.yaml index 497ae4c..21f6224 100644 --- a/builtins/en/pieces/default-hybrid-codex.yaml +++ b/builtins/en/pieces/default-hybrid-codex.yaml @@ -1,34 +1,16 @@ -# Default TAKT Piece -# Plan -> Architect -> Implement -> AI Review -> Reviewers (parallel: Architect + QA) -> Supervisor Approval -# -# Boilerplate sections (Piece Context, User Request, Previous Response, -# Additional User Inputs, Instructions heading) are auto-injected by buildInstruction(). -# Only movement-specific content belongs in instruction_template. -# -# Template Variables (available in instruction_template): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {previous_response} - Output from the previous movement (only when pass_previous_response: true) -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") -# -# Movement-level Fields: -# report: - Report file(s) for the movement (auto-injected as Report File/Files in Piece Context) -# Single: report: 00-plan.md -# Multiple: report: -# - Scope: 01-coder-scope.md -# - Decisions: 02-coder-decisions.md +# Auto-generated from default.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: default-hybrid-codex description: Standard development piece with planning and specialized reviews - max_iterations: 30 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + backend: ../knowledge/backend.md + architecture: ../knowledge/architecture.md personas: planner: ../personas/planner.md architect-planner: ../personas/architect-planner.md @@ -37,7 +19,6 @@ personas: architecture-reviewer: ../personas/architecture-reviewer.md qa-reviewer: ../personas/qa-reviewer.md supervisor: ../personas/supervisor.md - instructions: plan: ../instructions/plan.md architect: ../instructions/architect.md @@ -49,7 +30,6 @@ instructions: review-qa: ../instructions/review-qa.md fix: ../instructions/fix.md supervise: ../instructions/supervise.md - report_formats: plan: ../report-formats/plan.md architecture-design: ../report-formats/architecture-design.md @@ -58,11 +38,11 @@ report_formats: qa-review: ../report-formats/qa-review.md validation: ../report-formats/validation.md summary: ../report-formats/summary.md - initial_movement: plan - loop_monitors: - - cycle: [ai_review, ai_fix] + - cycle: + - ai_review + - ai_fix threshold: 3 judge: persona: supervisor @@ -84,7 +64,6 @@ loop_monitors: next: ai_review - condition: Unproductive (no improvement) next: reviewers - movements: - name: plan edit: false @@ -111,7 +90,6 @@ movements: - {Question 1} - {Question 2} instruction: plan - - name: architect edit: false persona: architect-planner @@ -132,15 +110,17 @@ movements: - condition: Insufficient info, cannot proceed next: ABORT instruction: architect - - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - backend + - architecture report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -166,7 +146,6 @@ movements: requires_user_input: true interactive_only: true instruction: implement - - name: ai_review edit: false persona: ai-antipattern-reviewer @@ -186,15 +165,17 @@ movements: - condition: AI-specific issues found next: ai_fix instruction: ai-review - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - backend + - architecture allowed_tools: - Read - Glob @@ -213,7 +194,6 @@ movements: - condition: Cannot proceed, insufficient info next: ai_no_fix instruction: ai-fix - - name: ai_no_fix edit: false persona: architecture-reviewer @@ -228,13 +208,15 @@ movements: - condition: ai_fix's judgment is valid (no fix needed) next: reviewers instruction: arbitrate - - name: reviewers parallel: - name: arch-review edit: false persona: architecture-reviewer stance: review + knowledge: + - architecture + - backend report: name: 05-architect-review.md format: architecture-review @@ -248,7 +230,6 @@ movements: - condition: approved - condition: needs_fix instruction: review-arch - - name: qa-review edit: false persona: qa-reviewer @@ -271,14 +252,16 @@ movements: next: supervise - condition: any("needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - backend + - architecture allowed_tools: - Read - Glob @@ -295,7 +278,6 @@ movements: - condition: Cannot proceed, insufficient info next: plan instruction: fix - - name: supervise edit: false persona: supervisor diff --git a/builtins/en/pieces/default.yaml b/builtins/en/pieces/default.yaml index 60b7985..05fe66e 100644 --- a/builtins/en/pieces/default.yaml +++ b/builtins/en/pieces/default.yaml @@ -21,6 +21,7 @@ stances: testing: ../stances/testing.md knowledge: + backend: ../knowledge/backend.md architecture: ../knowledge/architecture.md personas: @@ -134,6 +135,7 @@ movements: - coding - testing session: refresh + knowledge: [backend, architecture] report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -187,6 +189,7 @@ movements: - coding - testing session: refresh + knowledge: [backend, architecture] allowed_tools: - Read - Glob @@ -227,7 +230,7 @@ movements: edit: false persona: architecture-reviewer stance: review - knowledge: architecture + knowledge: [architecture, backend] report: name: 05-architect-review.md format: architecture-review @@ -271,6 +274,7 @@ movements: stance: - coding - testing + knowledge: [backend, architecture] allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/expert-cqrs-hybrid-codex.yaml b/builtins/en/pieces/expert-cqrs-hybrid-codex.yaml index 8433774..f928e62 100644 --- a/builtins/en/pieces/expert-cqrs-hybrid-codex.yaml +++ b/builtins/en/pieces/expert-cqrs-hybrid-codex.yaml @@ -1,33 +1,19 @@ -# Expert CQRS Review Piece -# Review piece with CQRS+ES, Frontend, Security, and QA experts -# -# Flow: -# plan -> implement -> ai_review -> reviewers (parallel) -> supervise -> COMPLETE -# ↓ ├─ cqrs-es-review ↓ -# ai_fix ├─ frontend-review fix_supervisor -# ├─ security-review -# └─ qa-review -# any("needs_fix") → fix → reviewers -# -# Template Variables: -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request -# {previous_response} - Output from the previous movement -# {user_inputs} - Accumulated user inputs during piece -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from expert-cqrs.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: expert-cqrs-hybrid-codex description: CQRS+ES, Frontend, Security, QA Expert Review - max_iterations: 30 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md + cqrs-es: ../knowledge/cqrs-es.md + security: ../knowledge/security.md + architecture: ../knowledge/architecture.md personas: planner: ../personas/planner.md coder: ../personas/coder.md @@ -38,7 +24,6 @@ personas: security-reviewer: ../personas/security-reviewer.md qa-reviewer: ../personas/qa-reviewer.md expert-supervisor: ../personas/expert-supervisor.md - instructions: plan: ../instructions/plan.md implement: ../instructions/implement.md @@ -52,7 +37,6 @@ instructions: fix: ../instructions/fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: plan: ../report-formats/plan.md ai-review: ../report-formats/ai-review.md @@ -62,13 +46,8 @@ report_formats: qa-review: ../report-formats/qa-review.md validation: ../report-formats/validation.md summary: ../report-formats/summary.md - initial_movement: plan - movements: - # =========================================== - # Movement 0: Planning - # =========================================== - name: plan edit: false persona: planner @@ -88,18 +67,20 @@ movements: next: implement - condition: Requirements are unclear and planning cannot proceed next: ABORT - - # =========================================== - # Movement 1: Implementation - # =========================================== - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -124,10 +105,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - # =========================================== - # Movement 2: AI Review - # =========================================== - name: ai_review edit: false persona: ai-antipattern-reviewer @@ -147,15 +124,20 @@ movements: next: reviewers - condition: AI-specific issues detected next: ai_fix - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture allowed_tools: - Read - Glob @@ -173,7 +155,6 @@ movements: next: ai_no_fix - condition: Unable to proceed with fixes next: ai_no_fix - - name: ai_no_fix edit: false persona: architecture-reviewer @@ -188,16 +169,15 @@ movements: - condition: ai_fix's judgment is valid (no fix needed) next: reviewers instruction: arbitrate - - # =========================================== - # Movement 3: Expert Reviews (Parallel) - # =========================================== - name: reviewers parallel: - name: cqrs-es-review edit: false persona: cqrs-es-reviewer stance: review + knowledge: + - cqrs-es + - backend report: name: 04-cqrs-es-review.md format: cqrs-es-review @@ -205,18 +185,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-cqrs-es - - name: frontend-review edit: false persona: frontend-reviewer stance: review + knowledge: frontend report: name: 05-frontend-review.md format: frontend-review @@ -224,18 +203,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-frontend - - name: security-review edit: false persona: security-reviewer stance: review + knowledge: security report: name: 06-security-review.md format: security-review @@ -243,14 +221,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-security - - name: qa-review edit: false persona: qa-reviewer @@ -262,7 +238,6 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: @@ -274,14 +249,19 @@ movements: next: supervise - condition: any("needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture allowed_tools: - Read - Glob @@ -298,10 +278,6 @@ movements: - condition: Cannot proceed, insufficient info next: plan instruction: fix - - # =========================================== - # Movement 4: Supervision - # =========================================== - name: supervise edit: false persona: expert-supervisor @@ -321,14 +297,19 @@ movements: next: COMPLETE - condition: Issues detected during final review next: fix_supervisor - - name: fix_supervisor edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/expert-cqrs.yaml b/builtins/en/pieces/expert-cqrs.yaml index e79ae21..acb674c 100644 --- a/builtins/en/pieces/expert-cqrs.yaml +++ b/builtins/en/pieces/expert-cqrs.yaml @@ -30,8 +30,10 @@ stances: knowledge: frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md cqrs-es: ../knowledge/cqrs-es.md security: ../knowledge/security.md + architecture: ../knowledge/architecture.md personas: planner: ../personas/planner.md @@ -104,6 +106,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, cqrs-es, security, architecture] report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -159,6 +162,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, cqrs-es, security, architecture] allowed_tools: - Read - Glob @@ -201,7 +205,7 @@ movements: edit: false persona: cqrs-es-reviewer stance: review - knowledge: cqrs-es + knowledge: [cqrs-es, backend] report: name: 04-cqrs-es-review.md format: cqrs-es-review @@ -287,6 +291,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, cqrs-es, security, architecture] allowed_tools: - Read - Glob @@ -333,6 +338,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, cqrs-es, security, architecture] allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/expert-hybrid-codex.yaml b/builtins/en/pieces/expert-hybrid-codex.yaml index dc60c2e..3bedd04 100644 --- a/builtins/en/pieces/expert-hybrid-codex.yaml +++ b/builtins/en/pieces/expert-hybrid-codex.yaml @@ -1,45 +1,18 @@ -# Expert Review Piece -# Review piece with Architecture, Frontend, Security, and QA experts -# -# Flow: -# plan -> implement -> ai_review -> reviewers (parallel) -> supervise -> COMPLETE -# ↓ ├─ arch-review ↓ -# ai_fix ├─ frontend-review fix_supervisor -# ├─ security-review -# └─ qa-review -# any("needs_fix") → fix → reviewers -# -# AI review runs immediately after implementation to catch AI-specific issues early, -# before expert reviews begin. -# -# Boilerplate sections (Piece Context, User Request, Previous Response, -# Additional User Inputs, Instructions heading) are auto-injected by buildInstruction(). -# Only movement-specific content belongs in instruction_template. -# -# Template Variables (available in instruction_template): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {previous_response} - Output from the previous movement (only when pass_previous_response: true) -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") -# -# Movement-level Fields: -# report: - Report file(s) for the movement (auto-injected as Report File/Files in Piece Context) -# Single: report: 00-plan.md -# Multiple: report: -# - Scope: 01-coder-scope.md -# - Decisions: 02-coder-decisions.md +# Auto-generated from expert.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: expert-hybrid-codex description: Architecture, Frontend, Security, QA Expert Review - max_iterations: 30 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md + security: ../knowledge/security.md + architecture: ../knowledge/architecture.md personas: planner: ../personas/planner.md coder: ../personas/coder.md @@ -49,7 +22,6 @@ personas: security-reviewer: ../personas/security-reviewer.md qa-reviewer: ../personas/qa-reviewer.md expert-supervisor: ../personas/expert-supervisor.md - instructions: plan: ../instructions/plan.md implement: ../instructions/implement.md @@ -63,7 +35,6 @@ instructions: fix: ../instructions/fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: plan: ../report-formats/plan.md ai-review: ../report-formats/ai-review.md @@ -73,13 +44,8 @@ report_formats: qa-review: ../report-formats/qa-review.md validation: ../report-formats/validation.md summary: ../report-formats/summary.md - initial_movement: plan - movements: - # =========================================== - # Movement 0: Planning - # =========================================== - name: plan edit: false persona: planner @@ -99,18 +65,19 @@ movements: next: implement - condition: Requirements are unclear and planning cannot proceed next: ABORT - - # =========================================== - # Movement 1: Implementation - # =========================================== - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - security + - architecture report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -135,10 +102,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - # =========================================== - # Movement 2: AI Review - # =========================================== - name: ai_review edit: false persona: ai-antipattern-reviewer @@ -158,15 +121,19 @@ movements: next: reviewers - condition: AI-specific issues detected next: ai_fix - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - security + - architecture allowed_tools: - Read - Glob @@ -184,7 +151,6 @@ movements: next: ai_no_fix - condition: Unable to proceed with fixes next: ai_no_fix - - name: ai_no_fix edit: false persona: architecture-reviewer @@ -199,16 +165,15 @@ movements: - condition: ai_fix's judgment is valid (no fix needed) next: reviewers instruction: arbitrate - - # =========================================== - # Movement 3: Expert Reviews (Parallel) - # =========================================== - name: reviewers parallel: - name: arch-review edit: false persona: architecture-reviewer stance: review + knowledge: + - architecture + - backend report: name: 04-architect-review.md format: architecture-review @@ -216,18 +181,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-arch - - name: frontend-review edit: false persona: frontend-reviewer stance: review + knowledge: frontend report: name: 05-frontend-review.md format: frontend-review @@ -235,18 +199,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-frontend - - name: security-review edit: false persona: security-reviewer stance: review + knowledge: security report: name: 06-security-review.md format: security-review @@ -254,14 +217,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-security - - name: qa-review edit: false persona: qa-reviewer @@ -273,7 +234,6 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: @@ -285,14 +245,18 @@ movements: next: supervise - condition: any("needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - security + - architecture allowed_tools: - Read - Glob @@ -309,10 +273,6 @@ movements: - condition: Cannot proceed, insufficient info next: plan instruction: fix - - # =========================================== - # Movement 4: Supervision - # =========================================== - name: supervise edit: false persona: expert-supervisor @@ -332,14 +292,18 @@ movements: next: COMPLETE - condition: Issues detected during final review next: fix_supervisor - - name: fix_supervisor edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - security + - architecture allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/expert.yaml b/builtins/en/pieces/expert.yaml index 37c73a8..0a10849 100644 --- a/builtins/en/pieces/expert.yaml +++ b/builtins/en/pieces/expert.yaml @@ -30,6 +30,7 @@ stances: knowledge: frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md security: ../knowledge/security.md architecture: ../knowledge/architecture.md @@ -103,6 +104,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, security, architecture] report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -158,6 +160,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, security, architecture] allowed_tools: - Read - Glob @@ -200,7 +203,7 @@ movements: edit: false persona: architecture-reviewer stance: review - knowledge: architecture + knowledge: [architecture, backend] report: name: 04-architect-review.md format: architecture-review @@ -286,6 +289,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, security, architecture] allowed_tools: - Read - Glob @@ -332,6 +336,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, security, architecture] allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/minimal-hybrid-codex.yaml b/builtins/en/pieces/minimal-hybrid-codex.yaml index b10a81c..05f1273 100644 --- a/builtins/en/pieces/minimal-hybrid-codex.yaml +++ b/builtins/en/pieces/minimal-hybrid-codex.yaml @@ -1,51 +1,34 @@ -# Minimal TAKT Piece -# Implement -> Parallel Review (AI + Supervisor) -> Fix if needed -> Complete -# (Simplest configuration - no plan, no architect review) -# -# Template Variables (auto-injected): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request (auto-injected) -# {previous_response} - Output from the previous movement (auto-injected) -# {user_inputs} - Accumulated user inputs during piece (auto-injected) -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from minimal.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: minimal-hybrid-codex description: Minimal development piece (implement -> parallel review -> fix if needed -> complete) - max_iterations: 20 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - personas: coder: ../personas/coder.md ai-antipattern-reviewer: ../personas/ai-antipattern-reviewer.md supervisor: ../personas/supervisor.md - instructions: implement: ../instructions/implement.md review-ai: ../instructions/review-ai.md ai-fix: ../instructions/ai-fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: ai-review: ../report-formats/ai-review.md - initial_movement: implement - movements: - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -69,7 +52,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - name: reviewers parallel: - name: ai_review @@ -83,14 +65,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch instruction: review-ai rules: - condition: No AI-specific issues - condition: AI-specific issues found - - name: supervise edit: false persona: supervisor @@ -102,7 +82,6 @@ movements: - Read - Glob - Grep - - Bash - WebSearch - WebFetch @@ -110,7 +89,6 @@ movements: rules: - condition: All checks passed - condition: Requirements unmet, tests failing - rules: - condition: all("No AI-specific issues", "All checks passed") next: COMPLETE @@ -120,22 +98,20 @@ movements: next: ai_fix - condition: any("Requirements unmet, tests failing") next: supervise_fix - - name: fix_both parallel: - name: ai_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -145,20 +121,18 @@ movements: - condition: No fix needed (verified target files/spec) - condition: Cannot proceed, insufficient info instruction: ai-fix - - name: supervise_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -167,20 +141,18 @@ movements: - condition: Supervisor's issues fixed - condition: Cannot proceed, insufficient info instruction: fix-supervisor - rules: - condition: all("AI Reviewer's issues fixed", "Supervisor's issues fixed") next: reviewers - condition: any("No fix needed (verified target files/spec)", "Cannot proceed, insufficient info") next: implement - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob @@ -199,14 +171,13 @@ movements: - condition: Cannot proceed, insufficient info next: implement instruction: ai-fix - - name: supervise_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/passthrough-hybrid-codex.yaml b/builtins/en/pieces/passthrough-hybrid-codex.yaml index 41cb622..86a61d0 100644 --- a/builtins/en/pieces/passthrough-hybrid-codex.yaml +++ b/builtins/en/pieces/passthrough-hybrid-codex.yaml @@ -1,33 +1,23 @@ -# Passthrough TAKT Piece -# Thinnest wrapper. Pass task directly to coder as-is. -# -# Flow: -# execute (do the task) -# ↓ -# COMPLETE +# Auto-generated from passthrough.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: passthrough-hybrid-codex description: Single-agent thin wrapper. Pass task directly to coder as-is. - max_iterations: 10 - stances: coding: ../stances/coding.md testing: ../stances/testing.md - personas: coder: ../personas/coder.md - initial_movement: execute - movements: - name: execute edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex report: - Summary: summary.md allowed_tools: diff --git a/builtins/en/pieces/review-fix-minimal-hybrid-codex.yaml b/builtins/en/pieces/review-fix-minimal-hybrid-codex.yaml index 8b8eefa..fa84e1e 100644 --- a/builtins/en/pieces/review-fix-minimal-hybrid-codex.yaml +++ b/builtins/en/pieces/review-fix-minimal-hybrid-codex.yaml @@ -1,51 +1,34 @@ -# Review-Fix Minimal TAKT Piece -# Review -> Fix (if needed) -> Re-review -> Complete -# (Starts with review, no implementation movement) -# -# Template Variables (auto-injected): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request (auto-injected) -# {previous_response} - Output from the previous movement (auto-injected) -# {user_inputs} - Accumulated user inputs during piece (auto-injected) -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from review-fix-minimal.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: review-fix-minimal-hybrid-codex description: Review and fix piece for existing code (starts with review, no implementation) - max_iterations: 20 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - personas: coder: ../personas/coder.md ai-antipattern-reviewer: ../personas/ai-antipattern-reviewer.md supervisor: ../personas/supervisor.md - instructions: implement: ../instructions/implement.md review-ai: ../instructions/review-ai.md ai-fix: ../instructions/ai-fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: ai-review: ../report-formats/ai-review.md - initial_movement: reviewers - movements: - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -69,7 +52,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - name: reviewers parallel: - name: ai_review @@ -83,14 +65,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch instruction: review-ai rules: - condition: No AI-specific issues - condition: AI-specific issues found - - name: supervise edit: false persona: supervisor @@ -102,7 +82,6 @@ movements: - Read - Glob - Grep - - Bash - WebSearch - WebFetch @@ -110,7 +89,6 @@ movements: rules: - condition: All checks passed - condition: Requirements unmet, tests failing - rules: - condition: all("No AI-specific issues", "All checks passed") next: COMPLETE @@ -120,22 +98,20 @@ movements: next: ai_fix - condition: any("Requirements unmet, tests failing") next: supervise_fix - - name: fix_both parallel: - name: ai_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -145,20 +121,18 @@ movements: - condition: No fix needed (verified target files/spec) - condition: Cannot proceed, insufficient info instruction: ai-fix - - name: supervise_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -167,20 +141,18 @@ movements: - condition: Supervisor's issues fixed - condition: Cannot proceed, insufficient info instruction: fix-supervisor - rules: - condition: all("AI Reviewer's issues fixed", "Supervisor's issues fixed") next: reviewers - condition: any("No fix needed (verified target files/spec)", "Cannot proceed, insufficient info") next: implement - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob @@ -199,14 +171,13 @@ movements: - condition: Cannot proceed, insufficient info next: implement instruction: ai-fix - - name: supervise_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob diff --git a/builtins/en/pieces/review-only.yaml b/builtins/en/pieces/review-only.yaml index 785a8ac..7ce20a9 100644 --- a/builtins/en/pieces/review-only.yaml +++ b/builtins/en/pieces/review-only.yaml @@ -27,6 +27,10 @@ max_iterations: 10 stances: review: ../stances/review.md +knowledge: + architecture: ../knowledge/architecture.md + security: ../knowledge/security.md + personas: planner: ../personas/planner.md architecture-reviewer: ../personas/architecture-reviewer.md @@ -90,6 +94,7 @@ movements: edit: false persona: architecture-reviewer stance: review + knowledge: architecture report: name: 01-architect-review.md format: architecture-review @@ -109,6 +114,7 @@ movements: edit: false persona: security-reviewer stance: review + knowledge: security report: name: 02-security-review.md format: security-review diff --git a/builtins/ja/knowledge/backend.md b/builtins/ja/knowledge/backend.md new file mode 100644 index 0000000..3f0efea --- /dev/null +++ b/builtins/ja/knowledge/backend.md @@ -0,0 +1,485 @@ +# バックエンド専門知識 + +## ヘキサゴナルアーキテクチャ(ポートとアダプター) + +依存方向は外側から内側へ。逆方向の依存は禁止。 + +``` +adapter(外部) → application(ユースケース) → domain(ビジネスロジック) +``` + +ディレクトリ構成: + +``` +{domain-name}/ +├── domain/ # ドメイン層(フレームワーク非依存) +│ ├── model/ +│ │ └── aggregate/ # 集約ルート、値オブジェクト +│ └── service/ # ドメインサービス +├── application/ # アプリケーション層(ユースケース) +│ ├── usecase/ # オーケストレーション +│ └── query/ # クエリハンドラ +├── adapter/ # アダプター層(外部接続) +│ ├── inbound/ # 入力アダプター +│ │ └── rest/ # REST Controller, Request/Response DTO +│ └── outbound/ # 出力アダプター +│ └── persistence/ # Entity, Repository実装 +└── api/ # 公開インターフェース(他ドメインから参照可能) + └── events/ # ドメインイベント +``` + +各層の責務: + +| 層 | 責務 | 依存してよいもの | 依存してはいけないもの | +|----|------|----------------|---------------------| +| domain | ビジネスロジック、不変条件 | 標準ライブラリのみ | フレームワーク、DB、外部API | +| application | ユースケースのオーケストレーション | domain | adapter の具体実装 | +| adapter/inbound | HTTPリクエスト受信、DTO変換 | application, domain | outbound adapter | +| adapter/outbound | DB永続化、外部API呼び出し | domain(インターフェース) | application | + +```kotlin +// CORRECT - ドメイン層はフレームワーク非依存 +data class Order(val orderId: String, val status: OrderStatus) { + fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) + return OrderConfirmedEvent(orderId, confirmedBy) + } +} + +// WRONG - ドメイン層にSpringアノテーション +@Entity +data class Order( + @Id val orderId: String, + @Enumerated(EnumType.STRING) val status: OrderStatus +) { + fun confirm(confirmedBy: String) { ... } +} +``` + +| 基準 | 判定 | +|------|------| +| ドメイン層にフレームワーク依存(@Entity, @Component等) | REJECT | +| Controller から Repository を直接参照 | REJECT。UseCase層を経由 | +| ドメイン層から外向きの依存(DB, HTTP等) | REJECT | +| adapter 間の直接依存(inbound → outbound) | REJECT | + +## API層設計(Controller) + +Controller は薄く保つ。リクエスト受信 → UseCase委譲 → レスポンス返却のみ。 + +```kotlin +// CORRECT - Controller は薄い +@RestController +@RequestMapping("/api/orders") +class OrdersController( + private val placeOrderUseCase: PlaceOrderUseCase, + private val queryGateway: QueryGateway +) { + // Command: 状態変更 + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + fun post(@Valid @RequestBody request: OrderPostRequest): OrderPostResponse { + val output = placeOrderUseCase.execute(request.toInput()) + return OrderPostResponse(output.orderId) + } + + // Query: 参照 + @GetMapping("/{id}") + fun get(@PathVariable id: String): ResponseEntity { + val detail = queryGateway.query(FindOrderQuery(id), OrderDetail::class.java).join() + ?: return ResponseEntity.notFound().build() + return ResponseEntity.ok(OrderGetResponse.from(detail)) + } +} + +// WRONG - Controller にビジネスロジック +@PostMapping +fun post(@RequestBody request: OrderPostRequest): ResponseEntity { + // バリデーション、在庫チェック、計算... Controller に書いてはいけない + val stock = inventoryRepository.findByProductId(request.productId) + if (stock.quantity < request.quantity) { + return ResponseEntity.badRequest().body("在庫不足") + } + val total = request.quantity * request.unitPrice * 1.1 // 税計算 + orderRepository.save(OrderEntity(...)) + return ResponseEntity.ok(...) +} +``` + +### Request/Response DTO 設計 + +Request と Response は別の型として定義する。ドメインモデルをそのままAPIに露出しない。 + +```kotlin +// Request: バリデーションアノテーション + init ブロック +data class OrderPostRequest( + @field:NotBlank val customerId: String, + @field:NotNull val items: List +) { + init { + require(items.isNotEmpty()) { "注文には1つ以上の商品が必要です" } + } + + fun toInput() = PlaceOrderInput(customerId = customerId, items = items.map { it.toItem() }) +} + +// Response: ファクトリメソッド from() で変換 +data class OrderGetResponse( + val orderId: String, + val status: String, + val customerName: String +) { + companion object { + fun from(detail: OrderDetail) = OrderGetResponse( + orderId = detail.orderId, + status = detail.status.name, + customerName = detail.customerName + ) + } +} +``` + +| 基準 | 判定 | +|------|------| +| ドメインモデルをそのままレスポンスに返す | REJECT | +| Request DTOにビジネスロジック | REJECT。バリデーションのみ許容 | +| Response DTOにドメインロジック(計算等) | REJECT | +| Request/Responseが同一の型 | REJECT | + +### RESTful なアクション設計 + +状態遷移は動詞をサブリソースとして表現する。 + +``` +POST /api/orders → 注文作成 +GET /api/orders/{id} → 注文取得 +GET /api/orders → 注文一覧 +POST /api/orders/{id}/approve → 承認(状態遷移) +POST /api/orders/{id}/cancel → キャンセル(状態遷移) +``` + +| 基準 | 判定 | +|------|------| +| PUT/PATCH でドメイン操作(approve, cancel等) | REJECT。POST + 動詞サブリソース | +| 1つのエンドポイントで複数の操作を分岐 | REJECT。操作ごとにエンドポイントを分ける | +| DELETE で論理削除 | REJECT。POST + cancel 等の明示的操作 | + +## バリデーション戦略 + +バリデーションは層ごとに役割が異なる。すべてを1箇所に集めない。 + +| 層 | 責務 | 手段 | 例 | +|----|------|------|-----| +| API層 | 構造的バリデーション | `@NotBlank`, `init` ブロック | 必須項目、型、フォーマット | +| UseCase層 | ビジネスルール検証 | Read Modelへの問い合わせ | 重複チェック、前提条件の存在確認 | +| ドメイン層 | 状態遷移の不変条件 | `require` | 「PENDINGでないと承認できない」 | + +```kotlin +// API層: 「入力の形が正しいか」 +data class OrderPostRequest( + @field:NotBlank val customerId: String, + val from: LocalDateTime, + val to: LocalDateTime +) { + init { + require(!to.isBefore(from)) { "終了日時は開始日時以降でなければなりません" } + } +} + +// UseCase層: 「ビジネス的に許可されるか」(Read Model参照) +fun execute(input: PlaceOrderInput) { + customerRepository.findById(input.customerId) + ?: throw CustomerNotFoundException("顧客が存在しません") + validateNoOverlapping(input) // 重複チェック + commandGateway.send(buildCommand(input)) +} + +// ドメイン層: 「今の状態でこの操作は許されるか」 +fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) { "確定できる状態ではありません" } + return OrderConfirmedEvent(orderId, confirmedBy) +} +``` + +| 基準 | 判定 | +|------|------| +| ドメインの状態遷移ルールがAPI層にある | REJECT | +| ビジネスルール検証がControllerにある | REJECT。UseCase層に | +| 構造バリデーション(@NotBlank等)がドメインにある | REJECT。API層で | +| UseCase層のバリデーションがAggregate内にある | REJECT。Read Model参照はUseCase層 | + +## エラーハンドリング + +### 例外階層設計 + +ドメイン例外は sealed class で階層化する。HTTP ステータスコードへのマッピングは Controller 層で行う。 + +```kotlin +// ドメイン例外: sealed class で網羅性を保証 +sealed class OrderException(message: String) : RuntimeException(message) +class OrderNotFoundException(message: String) : OrderException(message) +class InvalidOrderStateException(message: String) : OrderException(message) +class InsufficientStockException(message: String) : OrderException(message) + +// Controller 層でHTTPステータスにマッピング +@RestControllerAdvice +class OrderExceptionHandler { + @ExceptionHandler(OrderNotFoundException::class) + fun handleNotFound(e: OrderNotFoundException) = + ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse(e.message)) + + @ExceptionHandler(InvalidOrderStateException::class) + fun handleInvalidState(e: InvalidOrderStateException) = + ResponseEntity.status(HttpStatus.CONFLICT).body(ErrorResponse(e.message)) + + @ExceptionHandler(InsufficientStockException::class) + fun handleInsufficientStock(e: InsufficientStockException) = + ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY).body(ErrorResponse(e.message)) +} +``` + +| 基準 | 判定 | +|------|------| +| ドメイン例外にHTTPステータスコードが含まれる | REJECT。ドメインはHTTPを知らない | +| 汎用的な Exception や RuntimeException を throw | REJECT。具体的な例外型を使う | +| try-catch の空 catch | REJECT | +| Controller 内で例外を握りつぶして 200 を返す | REJECT | + +## ドメインモデル設計 + +### イミュータブル + require + +ドメインモデルは `data class`(イミュータブル)で設計し、`init` ブロックと `require` で不変条件を保証する。 + +```kotlin +data class Order( + val orderId: String, + val status: OrderStatus = OrderStatus.PENDING +) { + // companion object の static メソッドで生成 + companion object { + fun place(orderId: String, customerId: String): OrderPlacedEvent { + require(customerId.isNotBlank()) { "Customer ID cannot be blank" } + return OrderPlacedEvent(orderId, customerId) + } + } + + // インスタンスメソッドで状態遷移 → イベント返却 + fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) { "確定できる状態ではありません" } + return OrderConfirmedEvent(orderId, confirmedBy, LocalDateTime.now()) + } + + // イミュータブルな状態更新 + fun apply(event: OrderEvent): Order = when (event) { + is OrderPlacedEvent -> Order(orderId = event.orderId) + is OrderConfirmedEvent -> copy(status = OrderStatus.CONFIRMED) + is OrderCancelledEvent -> copy(status = OrderStatus.CANCELLED) + } +} +``` + +| 基準 | 判定 | +|------|------| +| ドメインモデルに var フィールド | REJECT。`copy()` でイミュータブルに更新 | +| バリデーションなしのファクトリ | REJECT。`require` で不変条件を保証 | +| ドメインモデルが外部サービスを呼ぶ | REJECT。純粋な関数のみ | +| setter でフィールドを直接変更 | REJECT | + +### 値オブジェクト + +プリミティブ型(String, Int)をドメインの意味でラップする。 + +```kotlin +// ID系: 型で取り違えを防止 +data class OrderId(@get:JsonValue val value: String) { + init { require(value.isNotBlank()) { "Order ID cannot be blank" } } + override fun toString(): String = value +} + +// 範囲系: 複合的な不変条件を保証 +data class DateRange(val from: LocalDateTime, val to: LocalDateTime) { + init { require(!to.isBefore(from)) { "終了日は開始日以降でなければなりません" } } +} + +// メタ情報系: イベントペイロード内の付随情報 +data class ApprovalInfo(val approvedBy: String, val approvalTime: LocalDateTime) +``` + +| 基準 | 判定 | +|------|------| +| 同じ型のIDが取り違えられる(orderId と customerId が両方 String) | 値オブジェクト化を検討 | +| 同じフィールドの組み合わせ(from/to等)が複数箇所に | 値オブジェクトに抽出 | +| 値オブジェクトに init ブロックがない | REJECT。不変条件を保証する | + +## リポジトリパターン + +ドメイン層でインターフェースを定義し、adapter/outbound で実装する。 + +```kotlin +// domain/: インターフェース(ポート) +interface OrderRepository { + fun findById(orderId: String): Order? + fun save(order: Order) +} + +// adapter/outbound/persistence/: 実装(アダプター) +@Repository +class JpaOrderRepository( + private val jpaRepository: OrderJpaRepository +) : OrderRepository { + override fun findById(orderId: String): Order? { + return jpaRepository.findById(orderId).orElse(null)?.toDomain() + } + override fun save(order: Order) { + jpaRepository.save(OrderEntity.from(order)) + } +} +``` + +### Read Model Entity(JPA Entity) + +Read Model 用の JPA Entity はドメインモデルとは別に定義する。var(mutable)が許容される。 + +```kotlin +@Entity +@Table(name = "orders") +data class OrderEntity( + @Id val orderId: String, + var customerId: String, + @Enumerated(EnumType.STRING) var status: OrderStatus, + var metadata: String? = null +) +``` + +| 基準 | 判定 | +|------|------| +| ドメインモデルを JPA Entity として兼用 | REJECT。分離する | +| Entity に ビジネスロジック | REJECT。Entity はデータ構造のみ | +| Repository 実装がドメイン層にある | REJECT。adapter/outbound に | + +## 認証・認可の配置 + +認証・認可は横断的関心事として適切な層で処理する。 + +| 関心事 | 配置 | 手段 | +|-------|------|------| +| 認証(誰か) | Filter / Interceptor層 | JWT検証、セッション確認 | +| 認可(権限) | Controller層 | `@PreAuthorize("hasRole('ADMIN')")` | +| データアクセス制御(自分のデータのみ) | UseCase層 | ビジネスルールとして検証 | + +```kotlin +// Controller層: ロールベースの認可 +@PostMapping("/{id}/approve") +@PreAuthorize("hasRole('FACILITY_ADMIN')") +fun approve(@PathVariable id: String, @RequestBody request: ApproveRequest) { ... } + +// UseCase層: データアクセス制御 +fun execute(input: DeleteInput, currentUserId: String) { + val entity = repository.findById(input.id) + ?: throw NotFoundException("見つかりません") + require(entity.ownerId == currentUserId) { "他のユーザーのデータは操作できません" } + // ... +} +``` + +| 基準 | 判定 | +|------|------| +| 認可ロジックが UseCase 層やドメイン層にある | REJECT。Controller層で | +| データアクセス制御が Controller にある | REJECT。UseCase層で | +| 認証処理が Controller 内にある | REJECT。Filter/Interceptor で | + +## テスト戦略 + +### テストピラミッド + +``` + ┌─────────────┐ + │ E2E Test │ ← 少数: API全体フロー確認 + ├─────────────┤ + │ Integration │ ← Repository, Controller の統合確認 + ├─────────────┤ + │ Unit Test │ ← 多数: ドメインモデル、UseCase の独立テスト + └─────────────┘ +``` + +### ドメインモデルのテスト + +ドメインモデルはフレームワーク非依存なので、純粋なユニットテストが書ける。 + +```kotlin +class OrderTest { + // ヘルパー: 特定の状態の集約を構築 + private fun pendingOrder(): Order { + val event = Order.place("order-1", "customer-1") + return Order.from(event) + } + + @Nested + inner class Confirm { + @Test + fun `PENDING状態から確定できる`() { + val order = pendingOrder() + val event = order.confirm("admin-1") + assertEquals("order-1", event.orderId) + } + + @Test + fun `CONFIRMED状態からは確定できない`() { + val order = pendingOrder().let { it.apply(it.confirm("admin-1")) } + assertThrows { + order.confirm("admin-2") + } + } + } +} +``` + +テストのルール: +- 状態遷移をヘルパーメソッドで構築(テストごとに独立) +- `@Nested` で操作単位にグループ化 +- 正常系と異常系(不正な状態遷移)を両方テスト +- `assertThrows` で例外の型を検証 + +### UseCase のテスト + +UseCase はモックを使ってテスト。外部依存を注入する。 + +```kotlin +class PlaceOrderUseCaseTest { + private val commandGateway = mockk() + private val customerRepository = mockk() + private val useCase = PlaceOrderUseCase(commandGateway, customerRepository) + + @Test + fun `顧客が存在しない場合はエラー`() { + every { customerRepository.findById("unknown") } returns null + + assertThrows { + useCase.execute(PlaceOrderInput(customerId = "unknown", items = listOf(...))) + } + } +} +``` + +| 基準 | 判定 | +|------|------| +| ドメインモデルのテストにモックを使用 | REJECT。ドメインは純粋にテスト | +| UseCase テストで実DBに接続 | REJECT。モックを使う | +| テストがフレームワークの起動を必要とする | ユニットテストなら REJECT | +| 状態遷移の異常系テストがない | REJECT | + +## アンチパターン検出 + +以下を見つけたら REJECT: + +| アンチパターン | 問題 | +|---------------|------| +| Smart Controller | Controller にビジネスロジックが集中 | +| Anemic Domain Model | ドメインモデルが setter/getter だけのデータ構造 | +| God Service | 1つの Service クラスに全操作が集中 | +| Repository直叩き | Controller が Repository を直接参照 | +| ドメイン漏洩 | adapter 層にドメインロジックが漏れる | +| Entity兼用 | JPA Entity をドメインモデルとして使い回す | +| 例外握りつぶし | 空の catch ブロック | +| Magic String | ハードコードされたステータス文字列等 | diff --git a/builtins/ja/knowledge/cqrs-es.md b/builtins/ja/knowledge/cqrs-es.md index 2579666..6ea0837 100644 --- a/builtins/ja/knowledge/cqrs-es.md +++ b/builtins/ja/knowledge/cqrs-es.md @@ -60,6 +60,97 @@ data class Notification(val notificationId: String) { } ``` +### Adapterパターン(ドメインとフレームワークの分離) + +ドメインモデルにフレームワークのアノテーション(`@Aggregate`, `@CommandHandler`等)を直接付けない。Adapterクラスがフレームワーク統合を担当し、ドメインモデルはビジネスロジックに専念する。 + +```kotlin +// ドメインモデル: フレームワーク非依存。ビジネスロジックのみ +data class Order( + val orderId: String, + val status: OrderStatus = OrderStatus.PENDING +) { + companion object { + fun place(orderId: String, customerId: String): OrderPlacedEvent { + require(customerId.isNotBlank()) { "Customer ID cannot be blank" } + return OrderPlacedEvent(orderId, customerId) + } + + fun from(event: OrderPlacedEvent): Order { + return Order(orderId = event.orderId, status = OrderStatus.PENDING) + } + } + + fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) { "確定できる状態ではありません" } + return OrderConfirmedEvent(orderId, confirmedBy, LocalDateTime.now()) + } + + fun apply(event: OrderEvent): Order = when (event) { + is OrderPlacedEvent -> from(event) + is OrderConfirmedEvent -> copy(status = OrderStatus.CONFIRMED) + is OrderCancelledEvent -> copy(status = OrderStatus.CANCELLED) + } +} + +// Adapter: フレームワーク統合。ドメイン呼び出し → イベント発行の中継 +@Aggregate +class OrderAggregateAdapter() { + private var order: Order? = null + + @AggregateIdentifier + fun orderId(): String? = order?.orderId + + @CommandHandler + constructor(command: PlaceOrderCommand) : this() { + val event = Order.place(command.orderId, command.customerId) + AggregateLifecycle.apply(event) + } + + @CommandHandler + fun handle(command: ConfirmOrderCommand) { + val event = order!!.confirm(command.confirmedBy) + AggregateLifecycle.apply(event) + } + + @EventSourcingHandler + fun on(event: OrderEvent) { + this.order = when (event) { + is OrderPlacedEvent -> Order.from(event) + else -> order?.apply(event) + } + } +} +``` + +分離の利点: +- ドメインモデル単体でユニットテスト可能(フレームワーク不要) +- フレームワーク移行時にドメインモデルは変更不要 +- Adapterはコマンド受信 → ドメイン呼び出し → イベント発行の定型コード + +### apply/from パターン(イベント再生) + +ドメインモデルが自身の状態をイベントから再構築するパターン。 + +- `from(event)`: 生成イベントから初期状態を構築するファクトリ +- `apply(event)`: イベントを受けて新しい状態を返す(`copy()` でイミュータブルに更新) +- `when` 式 + sealed interface で全イベント型の網羅性をコンパイラが保証 + +```kotlin +fun apply(event: OrderEvent): Order = when (event) { + is OrderPlacedEvent -> from(event) + is OrderConfirmedEvent -> copy(status = OrderStatus.CONFIRMED) + is OrderShippedEvent -> copy(status = OrderStatus.SHIPPED) + // sealed interface なので、イベント型の追加漏れはコンパイルエラーになる +} +``` + +| 基準 | 判定 | +|------|------| +| apply 内にビジネスロジック(バリデーション等) | REJECT。applyは状態復元のみ | +| apply が副作用を持つ(DB操作、イベント発行等) | REJECT | +| apply が例外をスローする | REJECT。再生時の失敗は許容しない | + ## イベント設計 | 基準 | 判定 | @@ -79,6 +170,36 @@ OrderPlaced, PaymentReceived, ItemShipped OrderUpdated, OrderDeleted ``` +### sealed interface によるイベント型階層 + +集約のイベントは sealed interface で型階層化する。集約ルートIDを共通フィールドとして強制し、`when` 式の網羅性チェックを有効にする。 + +```kotlin +sealed interface OrderEvent { + val orderId: String // 全イベントに必須 +} + +data class OrderPlacedEvent( + override val orderId: String, + val customerId: String +) : OrderEvent + +data class OrderConfirmedEvent( + override val orderId: String, + val approvalInfo: ApprovalInfo +) : OrderEvent + +data class OrderCancelledEvent( + override val orderId: String, + val cancellationInfo: CancellationInfo +) : OrderEvent +``` + +利点: +- `when (event)` で全イベント型を列挙しないとコンパイルエラー(`apply` メソッドで特に重要) +- 集約ルートIDの存在をコンパイラが保証 +- 型ベースのイベントハンドラ分岐が安全 + イベント粒度: - 細かすぎ: `OrderFieldChanged` → ドメインの意図が不明 - 適切: `ShippingAddressChanged` → 意図が明確 @@ -101,6 +222,86 @@ OrderUpdated, OrderDeleted 4. 発行されたイベントを保存 ``` +### 多層バリデーション + +バリデーションは層ごとに役割が異なる。すべてを1箇所に集めない。 + +| 層 | 責務 | 手段 | 例 | +|----|------|------|-----| +| API層 | 構造的バリデーション | `@NotBlank`, `init` ブロック | 必須項目、型、フォーマット | +| UseCase層 | ビジネスルール検証 | Read Modelへの問い合わせ | 重複チェック、前提条件の存在確認 | +| ドメイン層 | 状態遷移の不変条件 | `require` | 「PENDINGでないと承認できない」 | + +```kotlin +// API層: 構造的バリデーション +data class OrderPostRequest( + @field:NotBlank val customerId: String, + @field:NotNull val items: List +) { + init { + require(items.isNotEmpty()) { "注文には1つ以上の商品が必要です" } + } +} + +// UseCase層: ビジネスルール検証(Read Model参照) +@Service +class PlaceOrderUseCase( + private val commandGateway: CommandGateway, + private val customerRepository: CustomerRepository, + private val inventoryRepository: InventoryRepository +) { + fun execute(input: PlaceOrderInput): Mono { + return Mono.fromCallable { + // 顧客の存在確認 + customerRepository.findById(input.customerId) + ?: throw CustomerNotFoundException("顧客が存在しません") + // 在庫の事前確認 + validateInventory(input.items) + // コマンド送信 + val orderId = UUID.randomUUID().toString() + commandGateway.send(PlaceOrderCommand(orderId, input.customerId, input.items)) + PlaceOrderOutput(orderId) + } + } +} + +// ドメイン層: 状態遷移の不変条件 +fun confirm(confirmedBy: String): OrderConfirmedEvent { + require(status == OrderStatus.PENDING) { "確定できる状態ではありません" } + return OrderConfirmedEvent(orderId, confirmedBy, LocalDateTime.now()) +} +``` + +| 基準 | 判定 | +|------|------| +| ドメイン層のバリデーションがAPI層にある | REJECT。状態遷移ルールはドメインに | +| UseCase層のバリデーションがController内にある | REJECT。UseCase層に分離 | +| API層のバリデーション(@NotBlank等)がドメインにある | REJECT。構造検証はAPI層で | + +## UseCase層(オーケストレーション) + +Controller と CommandGateway の間にUseCase層を置く。コマンド発行前に複数集約のRead Modelを参照してバリデーションし、必要な前処理を行う。 + +``` +Controller → UseCase → CommandGateway → Aggregate + ↓ + QueryGateway / Repository(Read Model参照) +``` + +UseCaseが必要なケース: +- コマンド発行前にRead Modelから他集約の状態を確認する +- 複数のバリデーションを直列に実行する +- コマンド送信後の結果整合性を待機する(ポーリング等) + +UseCaseが不要なケース: +- Controllerからコマンドを1つ送るだけで完結する単純な操作 + +| 基準 | 判定 | +|------|------| +| ControllerがRepository直接参照してバリデーション | UseCase層に分離 | +| UseCaseがHTTPリクエスト/レスポンスに依存 | REJECT。UseCaseはプロトコル非依存 | +| UseCaseがAggregate内部状態を直接変更 | REJECT。CommandGateway経由 | + ## プロジェクション設計 | 基準 | 判定 | @@ -115,6 +316,58 @@ OrderUpdated, OrderDeleted - イベントから冪等に再構築可能 - Writeモデルから完全に独立 +### Projection と EventHandler(サイドエフェクト)の区別 + +どちらも `@EventHandler` を使うが、責務が異なる。混同しない。 + +| 種類 | 責務 | やること | やらないこと | +|------|------|---------|-------------| +| Projection | Read Model 更新 | Entity の保存・更新 | コマンド送信、外部API呼び出し | +| EventHandler | サイドエフェクト | 他集約へのコマンド送信 | Read Model 更新 | + +```kotlin +// Projection: Read Model 更新のみ +@Component +class OrderProjection(private val orderRepository: OrderRepository) { + @EventHandler + fun on(event: OrderPlacedEvent) { + val entity = OrderEntity( + orderId = event.orderId, + customerId = event.customerId, + status = OrderStatus.PENDING + ) + orderRepository.save(entity) + } + + @EventHandler + fun on(event: OrderConfirmedEvent) { + orderRepository.findById(event.orderId).ifPresent { entity -> + entity.status = OrderStatus.CONFIRMED + orderRepository.save(entity) + } + } +} + +// EventHandler: サイドエフェクト(他集約へのコマンド送信) +@Component +class InventoryReleaseHandler(private val commandGateway: CommandGateway) { + @EventHandler + fun on(event: OrderCancelledEvent) { + val command = ReleaseInventoryCommand( + productId = event.productId, + quantity = event.quantity + ) + commandGateway.send(command) + } +} +``` + +| 基準 | 判定 | +|------|------| +| Projection 内で CommandGateway を使用 | REJECT。EventHandler に分離 | +| EventHandler 内で Repository に save | REJECT。Projection に分離 | +| 1クラスに Projection と EventHandler の責務が混在 | REJECT。クラスを分離 | + ## Query側の設計 ControllerはQueryGatewayを使う。Repositoryを直接使わない。 @@ -408,6 +661,66 @@ fun `注文詳細が取得できる`() { | Query側テストがCommand経由でデータを作っていない | 推奨 | | 統合テストでAxonの非同期処理を考慮している | 必須 | +## 値オブジェクト設計 + +Aggregate とイベントの構成要素として値オブジェクトを使う。プリミティブ型(String, Int)で済ませない。 + +```kotlin +// NG - プリミティブ型のまま +data class OrderPlacedEvent( + val orderId: String, + val categoryId: String, // ただの文字列 + val from: LocalDateTime, // 意味が不明確 + val to: LocalDateTime +) + +// OK - 値オブジェクトで意味と制約を表現 +data class OrderPlacedEvent( + val orderId: String, + val categoryId: CategoryId, + val period: OrderPeriod +) +``` + +値オブジェクトの設計ルール: +- `data class` で equals/hashCode を自動生成(同値性で比較) +- `init` ブロックで不変条件を保証(生成時に必ず検証) +- ドメインロジック(計算)は含まない(純粋なデータホルダー) +- `@JsonValue` でシリアライゼーションを制御 + +```kotlin +// ID系: 単一値ラッパー +data class CategoryId(@get:JsonValue val value: String) { + init { + require(value.isNotBlank()) { "Category ID cannot be blank" } + } + override fun toString(): String = value +} + +// 範囲系: 複数値の不変条件を保証 +data class OrderPeriod( + val from: LocalDateTime, + val to: LocalDateTime +) { + init { + require(!to.isBefore(from)) { "終了日は開始日以降でなければなりません" } + } +} + +// メタ情報系: イベントペイロード内の付随情報 +data class ApprovalInfo( + val approvedBy: String, + val approvalTime: LocalDateTime +) +``` + +| 基準 | 判定 | +|------|------| +| IDをStringのまま使い回す | 値オブジェクト化を検討 | +| 同じフィールドの組み合わせ(from/to等)が複数箇所に | 値オブジェクトに抽出 | +| 値オブジェクトにビジネスロジック(状態遷移等) | REJECT。Aggregateの責務 | +| init ブロックなしで不変条件が保証されない | REJECT | + ## インフラ層 確認事項: diff --git a/builtins/ja/knowledge/frontend.md b/builtins/ja/knowledge/frontend.md index f4f6958..1860932 100644 --- a/builtins/ja/knowledge/frontend.md +++ b/builtins/ja/knowledge/frontend.md @@ -1,5 +1,42 @@ # フロントエンド専門知識 +## フロントエンドの層構造 + +依存方向は一方向。逆方向の依存は禁止。 + +``` +app/routes/ → features/ → shared/ +``` + +| 層 | 責務 | ルール | +|---|------|--------| +| `app/routes/` | ルート定義のみ | UIロジックを持たない。feature の View を呼ぶだけ | +| `features/` | 機能単位の自己完結モジュール | 他の feature を直接参照しない | +| `shared/` | 全 feature 横断の共有コード | feature に依存しない | + +ルートファイルは薄いラッパーに徹する。 + +```tsx +// CORRECT - ルートは薄い +// app/routes/schedule-management.tsx +export default function ScheduleManagementRoute() { + return +} + +// WRONG - ルートにロジックを書く +export default function ScheduleManagementRoute() { + const [filter, setFilter] = useState('all') + const { data } = useListSchedules({ filter }) + return +} +``` + +View コンポーネント(`features/*/components/*-view.tsx`)がデータ取得・状態管理を担当する。 + +``` +ルート(route) → View(データ取得・状態管理) → 子コンポーネント(表示) +``` + ## コンポーネント設計 1ファイルにベタ書きしない。必ずコンポーネント分割する。 @@ -33,6 +70,37 @@ | Layout | 配置・構造 | `PageLayout`, `Grid` | | Utility | 共通機能 | `ErrorBoundary`, `Portal` | +### UIプリミティブの設計原則 + +shared/components/ui/ に配置するHTML要素ラッパーの設計ルール: + +- `forwardRef` で ref を転送する(外部からの制御を可能にする) +- `className` を受け取り、外からスタイル拡張可能にする +- ネイティブ props をスプレッドで透過する(`...props`) +- variants は別ファイルに分離する(`button.variants.ts`) + +```tsx +// CORRECT - プリミティブの設計 +export const Button = forwardRef( + ({ variant, size, className, children, ...props }, ref) => { + return ( + + ) + } +) + +// WRONG - refもclassNameも透過しない閉じたコンポーネント +export const Button = ({ label, onClick }: { label: string; onClick: () => void }) => { + return +} +``` + ディレクトリ構成: ``` features/{feature-name}/ @@ -151,12 +219,62 @@ const WeeklyCalendar = ({ facilityId }) => { | ケース | 理由 | |--------|------| +| 独立ウィジェット | どのページにも置ける自己完結型コンポーネント | | 無限スクロール | スクロール位置というUI内部状態に依存 | | 検索オートコンプリート | 入力値に依存したリアルタイム検索 | -| 独立したウィジェット | 通知バッジ、天気等。親のデータと完全に無関係 | | リアルタイム更新 | WebSocket/Pollingでの自動更新 | | モーダル内の詳細取得 | 開いたときだけ追加データを取得 | +### 独立ウィジェットパターン + +WordPress のサイドバーウィジェットのように、どのページにも「置くだけ」で動くコンポーネント。親のデータフローに参加しない自己完結型。 + +該当する例: +- 通知バッジ・通知ベル(未読数を自分で取得) +- ログインユーザー情報表示(ヘッダーのアバター等) +- お知らせバナー +- 天気・為替など外部データ表示 +- アクティビティフィード(サイドバー) + +```tsx +// OK - 独立ウィジェット。どのページに置いても自分で動く +const NotificationBell = () => { + const { data } = useNotificationCount({ refetchInterval: 30000 }) + return ( + + ) +} + +// OK - ヘッダーに常駐するユーザーメニュー +const UserMenu = () => { + const { data: user } = useCurrentUser() + return +} +``` + +ウィジェットと判定する条件(すべて満たすこと): +- 親のデータと**完全に無関係**(親から props でデータを受け取る必要がない) +- 親の状態に**影響を与えない**(結果を親にバブリングしない) +- **どのページに置いても同じ動作**をする(ページ固有のコンテキストに依存しない) + +1つでも満たさない場合は View でデータ取得し、props で渡す。 + +```tsx +// WRONG - ウィジェットに見えるが、orderId という親のコンテキストに依存 +const OrderStatusWidget = ({ orderId }: { orderId: string }) => { + const { data } = useGetOrder(orderId) + return +} + +// CORRECT - 親のデータフローに参加するならpropsで受け取る +const OrderStatusWidget = ({ status }: { status: OrderStatus }) => { + return +} +``` + 判断基準: 「親が管理する意味がない / 親に影響を与えない」ケースのみ許容。 | 基準 | 判定 | @@ -169,6 +287,33 @@ const WeeklyCalendar = ({ facilityId }) => { ## 共有コンポーネントと抽象化 +### カテゴリ分類 + +shared コンポーネントは責務別にサブディレクトリで分類する。 + +``` +shared/components/ +├── ui/ # HTMLプリミティブのラッパー(Button, Card, Badge, Dialog) +├── form/ # フォーム入力要素(TextInput, Select, Checkbox) +├── layout/ # ページ構造・ルート保護(Layout, ProtectedRoute) +├── navigation/ # ナビゲーション(Tabs, BackLink, SidebarItem) +├── data-display/ # データ表示(Table, DetailField, Calendar) +├── feedback/ # 状態フィードバック(LoadingState, ErrorState) +├── domain/ # ドメイン固有だが横断的(StatusBadge, CategoryBadge) +└── index.ts # barrel export +``` + +| カテゴリ | 配置基準 | +|---------|---------| +| ui/ | HTML要素を薄くラップ。ドメイン知識を持たない | +| form/ | ラベル・エラー・必須マークを統合したフォーム部品 | +| layout/ | ページ全体の骨格。認証・ロール制御を含む | +| domain/ | 特定ドメインに依存するが、複数 feature で共有 | + +ui/ と domain/ の判断基準: ドメイン用語がコンポーネント名やpropsに含まれるなら domain/。 + +### 共有化の基準 + 同じパターンのUIは共有コンポーネント化する。インラインスタイルのコピペは禁止。 ```tsx @@ -411,6 +556,62 @@ function TaskCard({ task }: { task: Task }) { - YES → バックエンドに配置(ドメインロジック) - NO → フロントエンドでもOK(表示ロジック) +## 横断的関心事の処理層 + +横断的関心事は適切な層で処理する。コンポーネント内に散在させない。 + +| 関心事 | 処理層 | パターン | +|-------|--------|---------| +| 認証トークン付与 | APIクライアント層 | リクエストインターセプタ | +| 認証エラー(401/403) | APIクライアント層 | レスポンスインターセプタ | +| ルート保護 | レイアウト層 | ProtectedRoute + Outlet | +| ロール別振り分け | レイアウト層 | ユーザー種別による分岐 | +| ローディング/エラー表示 | View(Container)層 | 早期リターン | + +```tsx +// CORRECT - 横断的関心事はインターセプタ層で処理 +// api/axios-instance.ts +instance.interceptors.request.use((config) => { + const token = localStorage.getItem('auth_token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config +}) + +// WRONG - 各コンポーネントで個別にトークンを付与 +const MyComponent = () => { + const token = localStorage.getItem('auth_token') + const { data } = useQuery({ + queryFn: () => fetch('/api/data', { + headers: { Authorization: `Bearer ${token}` }, + }), + }) +} +``` + +```tsx +// CORRECT - ルート保護はレイアウト層で +// shared/components/layout/protected-route.tsx +function ProtectedRoute() { + const { isAuthenticated } = useAuthStore() + if (!isAuthenticated) return + return +} + +// routes でラップ +}> + } /> + + +// WRONG - 各ページで個別に認証チェック +function DashboardView() { + const { isAuthenticated } = useAuthStore() + if (!isAuthenticated) return + return
...
+} +``` + ## パフォーマンス | 基準 | 判定 | diff --git a/builtins/ja/piece-categories.yaml b/builtins/ja/piece-categories.yaml index ced6285..77ccee2 100644 --- a/builtins/ja/piece-categories.yaml +++ b/builtins/ja/piece-categories.yaml @@ -1,46 +1,37 @@ piece_categories: - "🚀 クイックスタート": + 🚀 クイックスタート: pieces: - default - passthrough - coding - minimal - - "🔍 レビュー&修正": + 🔍 レビュー&修正: pieces: - review-fix-minimal - - "🎨 フロントエンド": - {} - - "⚙️ バックエンド": - {} - - "🔧 フルスタック": + 🎨 フロントエンド: {} + ⚙️ バックエンド: {} + 🔧 フルスタック: pieces: - expert - expert-cqrs - - "🔀 ハイブリッド (Codex Coding)": - "🚀 クイックスタート": + 🔀 ハイブリッド (Codex Coding): + 🚀 クイックスタート: pieces: - - default-hybrid-codex - - passthrough-hybrid-codex - coding-hybrid-codex + - default-hybrid-codex - minimal-hybrid-codex - "🔍 レビュー&修正": + - passthrough-hybrid-codex + 🔧 フルスタック: + pieces: + - expert-cqrs-hybrid-codex + - expert-hybrid-codex + 🔍 レビュー&修正: pieces: - review-fix-minimal-hybrid-codex - "🔧 フルスタック": - pieces: - - expert-hybrid-codex - - expert-cqrs-hybrid-codex - - "その他": + その他: pieces: - research - magi - review-only - show_others_category: true -others_category_name: "その他" +others_category_name: その他 diff --git a/builtins/ja/pieces/coding-hybrid-codex.yaml b/builtins/ja/pieces/coding-hybrid-codex.yaml index 22f4842..27dea84 100644 --- a/builtins/ja/pieces/coding-hybrid-codex.yaml +++ b/builtins/ja/pieces/coding-hybrid-codex.yaml @@ -1,62 +1,31 @@ -# Coding TAKT Piece -# Plan -> Implement -> Parallel Review (AI + Architecture) -> Fix if needed -# -# 計画と並列レビューを備えた軽量な開発ピース。 -# architect-plannerが要件を調査・整理し、不明点はコードを読んで自力で解決する。 -# 並列レビュー後、問題がなければ直接完了し、高速なフィードバックループを実現。 -# -# フロー: -# plan (要件調査・計画) -# ↓ -# implement (実装) -# ↓ -# reviewers (並列レビュー) -# ├─ ai_review (AI特有問題検出) -# └─ arch-review (設計準拠性確認) -# ↓ -# [判定] -# ├─ all(approved) → COMPLETE -# └─ any(needs_fix) → fix → reviewers (再レビュー) -# -# Template Variables (auto-injected by buildInstruction): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request -# {previous_response} - Output from the previous movement -# {user_inputs} - Accumulated user inputs during piece -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from coding.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: coding-hybrid-codex description: Lightweight development piece with planning and parallel reviews (plan -> implement -> parallel review -> complete) - max_iterations: 20 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + architecture: ../knowledge/architecture.md personas: architect-planner: ../personas/architect-planner.md coder: ../personas/coder.md ai-antipattern-reviewer: ../personas/ai-antipattern-reviewer.md architecture-reviewer: ../personas/architecture-reviewer.md - instructions: plan: ../instructions/plan.md implement: ../instructions/implement.md ai-review: ../instructions/ai-review.md review-arch: ../instructions/review-arch.md fix: ../instructions/fix.md - report_formats: plan: ../report-formats/plan.md ai-review: ../report-formats/ai-review.md architecture-review: ../report-formats/architecture-review.md - initial_movement: plan - movements: - name: plan edit: false @@ -79,15 +48,15 @@ movements: - condition: 要件が不明確、情報不足 next: ABORT instruction: plan - - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: architecture report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -113,7 +82,6 @@ movements: requires_user_input: true interactive_only: true instruction: implement - - name: reviewers parallel: - name: ai_review @@ -133,11 +101,11 @@ movements: - condition: AI特有の問題なし - condition: AI特有の問題あり instruction: ai-review - - name: arch-review edit: false persona: architecture-reviewer stance: review + knowledge: architecture report: name: 05-architect-review.md format: architecture-review @@ -151,20 +119,19 @@ movements: - condition: approved - condition: needs_fix instruction: review-arch - rules: - condition: all("AI特有の問題なし", "approved") next: COMPLETE - condition: any("AI特有の問題あり", "needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: architecture allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/coding.yaml b/builtins/ja/pieces/coding.yaml index 7262cea..3cefae4 100644 --- a/builtins/ja/pieces/coding.yaml +++ b/builtins/ja/pieces/coding.yaml @@ -37,6 +37,9 @@ stances: review: ../stances/review.md testing: ../stances/testing.md +knowledge: + architecture: ../knowledge/architecture.md + personas: architect-planner: ../personas/architect-planner.md coder: ../personas/coder.md @@ -87,6 +90,7 @@ movements: - coding - testing session: refresh + knowledge: architecture report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -137,6 +141,7 @@ movements: edit: false persona: architecture-reviewer stance: review + knowledge: architecture report: name: 05-architect-review.md format: architecture-review @@ -163,6 +168,7 @@ movements: stance: - coding - testing + knowledge: architecture allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/default-hybrid-codex.yaml b/builtins/ja/pieces/default-hybrid-codex.yaml index 53ebd4c..0b03f1f 100644 --- a/builtins/ja/pieces/default-hybrid-codex.yaml +++ b/builtins/ja/pieces/default-hybrid-codex.yaml @@ -1,25 +1,16 @@ -# Default TAKT Piece -# Plan -> Architect -> Implement -> AI Review -> Reviewers (parallel: Architect + QA) -> Supervisor Approval -# -# Template Variables (auto-injected by buildInstruction): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request -# {previous_response} - Output from the previous movement -# {user_inputs} - Accumulated user inputs during piece -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from default.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: default-hybrid-codex description: Standard development piece with planning and specialized reviews - max_iterations: 30 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + architecture: ../knowledge/architecture.md + backend: ../knowledge/backend.md personas: planner: ../personas/planner.md architect-planner: ../personas/architect-planner.md @@ -28,7 +19,6 @@ personas: architecture-reviewer: ../personas/architecture-reviewer.md qa-reviewer: ../personas/qa-reviewer.md supervisor: ../personas/supervisor.md - instructions: plan: ../instructions/plan.md architect: ../instructions/architect.md @@ -40,7 +30,6 @@ instructions: review-qa: ../instructions/review-qa.md fix: ../instructions/fix.md supervise: ../instructions/supervise.md - report_formats: plan: ../report-formats/plan.md architecture-design: ../report-formats/architecture-design.md @@ -49,11 +38,11 @@ report_formats: qa-review: ../report-formats/qa-review.md validation: ../report-formats/validation.md summary: ../report-formats/summary.md - initial_movement: plan - loop_monitors: - - cycle: [ai_review, ai_fix] + - cycle: + - ai_review + - ai_fix threshold: 3 judge: persona: supervisor @@ -75,7 +64,6 @@ loop_monitors: next: ai_review - condition: 非生産的(改善なし) next: reviewers - movements: - name: plan edit: false @@ -102,7 +90,6 @@ movements: - {質問1} - {質問2} instruction: plan - - name: architect edit: false persona: architect-planner @@ -123,15 +110,17 @@ movements: - condition: 情報不足、判断できない next: ABORT instruction: architect - - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - backend + - architecture report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -157,7 +146,6 @@ movements: requires_user_input: true interactive_only: true instruction: implement - - name: ai_review edit: false persona: ai-antipattern-reviewer @@ -177,15 +165,17 @@ movements: - condition: AI特有の問題あり next: ai_fix instruction: ai-review - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - backend + - architecture allowed_tools: - Read - Glob @@ -204,7 +194,6 @@ movements: - condition: 判断できない、情報不足 next: ai_no_fix instruction: ai-fix - - name: ai_no_fix edit: false persona: architecture-reviewer @@ -219,13 +208,15 @@ movements: - condition: ai_fixの判断が妥当(修正不要) next: reviewers instruction: arbitrate - - name: reviewers parallel: - name: arch-review edit: false persona: architecture-reviewer stance: review + knowledge: + - architecture + - backend report: name: 05-architect-review.md format: architecture-review @@ -239,7 +230,6 @@ movements: - condition: approved - condition: needs_fix instruction: review-arch - - name: qa-review edit: false persona: qa-reviewer @@ -262,14 +252,16 @@ movements: next: supervise - condition: any("needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - backend + - architecture allowed_tools: - Read - Glob @@ -286,7 +278,6 @@ movements: - condition: 判断できない、情報不足 next: plan instruction: fix - - name: supervise edit: false persona: supervisor diff --git a/builtins/ja/pieces/default.yaml b/builtins/ja/pieces/default.yaml index b3efe6a..d6379ae 100644 --- a/builtins/ja/pieces/default.yaml +++ b/builtins/ja/pieces/default.yaml @@ -22,6 +22,7 @@ stances: knowledge: architecture: ../knowledge/architecture.md + backend: ../knowledge/backend.md personas: planner: ../personas/planner.md @@ -134,6 +135,7 @@ movements: - coding - testing session: refresh + knowledge: [backend, architecture] report: - Scope: 02-coder-scope.md - Decisions: 03-coder-decisions.md @@ -187,6 +189,7 @@ movements: - coding - testing session: refresh + knowledge: [backend, architecture] allowed_tools: - Read - Glob @@ -227,7 +230,7 @@ movements: edit: false persona: architecture-reviewer stance: review - knowledge: architecture + knowledge: [architecture, backend] report: name: 05-architect-review.md format: architecture-review @@ -271,6 +274,7 @@ movements: stance: - coding - testing + knowledge: [backend, architecture] allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/expert-cqrs-hybrid-codex.yaml b/builtins/ja/pieces/expert-cqrs-hybrid-codex.yaml index a66be3e..8e2b6aa 100644 --- a/builtins/ja/pieces/expert-cqrs-hybrid-codex.yaml +++ b/builtins/ja/pieces/expert-cqrs-hybrid-codex.yaml @@ -1,42 +1,19 @@ -# Expert Review Piece -# CQRS+ES、フロントエンド、セキュリティ、QAの専門家によるレビューピース -# -# フロー: -# plan -> implement -> ai_review -> reviewers (parallel) -> supervise -> COMPLETE -# ↓ ├─ cqrs-es-review ↓ -# ai_fix ├─ frontend-review fix_supervisor -# ├─ security-review -# └─ qa-review -# any("needs_fix") → fix → reviewers -# -# ボイラープレートセクション(Piece Context, User Request, Previous Response, -# Additional User Inputs, Instructions heading)はbuildInstruction()が自動挿入。 -# instruction_templateにはムーブメント固有の内容のみ記述。 -# -# テンプレート変数(instruction_template内で使用可能): -# {iteration} - ピース全体のターン数(全エージェントで実行されたムーブメントの合計) -# {max_iterations} - ピースの最大イテレーション数 -# {movement_iteration} - ムーブメントごとのイテレーション数(このムーブメントが何回実行されたか) -# {previous_response} - 前のムーブメントの出力(pass_previous_response: true の場合のみ) -# {report_dir} - レポートディレクトリ名(例: "20250126-143052-task-summary") -# -# ムーブメントレベルフィールド: -# report: - ムーブメントのレポートファイル(Piece ContextにReport File/Filesとして自動挿入) -# 単一: report: 00-plan.md -# 複数: report: -# - Scope: 01-coder-scope.md -# - Decisions: 02-coder-decisions.md +# Auto-generated from expert-cqrs.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: expert-cqrs-hybrid-codex description: CQRS+ES・フロントエンド・セキュリティ・QA専門家レビュー - max_iterations: 30 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md + cqrs-es: ../knowledge/cqrs-es.md + security: ../knowledge/security.md + architecture: ../knowledge/architecture.md personas: planner: ../personas/planner.md coder: ../personas/coder.md @@ -47,7 +24,6 @@ personas: security-reviewer: ../personas/security-reviewer.md qa-reviewer: ../personas/qa-reviewer.md expert-supervisor: ../personas/expert-supervisor.md - instructions: plan: ../instructions/plan.md implement: ../instructions/implement.md @@ -61,7 +37,6 @@ instructions: fix: ../instructions/fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: plan: ../report-formats/plan.md ai-review: ../report-formats/ai-review.md @@ -71,13 +46,8 @@ report_formats: qa-review: ../report-formats/qa-review.md validation: ../report-formats/validation.md summary: ../report-formats/summary.md - initial_movement: plan - movements: - # =========================================== - # Movement 0: Planning - # =========================================== - name: plan edit: false persona: planner @@ -97,18 +67,20 @@ movements: next: implement - condition: 要件が不明確で計画を立てられない next: ABORT - - # =========================================== - # Movement 1: Implementation - # =========================================== - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -133,10 +105,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - # =========================================== - # Movement 2: AI Review - # =========================================== - name: ai_review edit: false persona: ai-antipattern-reviewer @@ -156,15 +124,20 @@ movements: next: reviewers - condition: AI特有の問題が検出された next: ai_fix - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture allowed_tools: - Read - Glob @@ -182,7 +155,6 @@ movements: next: ai_no_fix - condition: 修正を進行できない next: ai_no_fix - - name: ai_no_fix edit: false persona: architecture-reviewer @@ -197,16 +169,15 @@ movements: - condition: ai_fixの判断が妥当(修正不要) next: reviewers instruction: arbitrate - - # =========================================== - # Movement 3: Expert Reviews (Parallel) - # =========================================== - name: reviewers parallel: - name: cqrs-es-review edit: false persona: cqrs-es-reviewer stance: review + knowledge: + - cqrs-es + - backend report: name: 04-cqrs-es-review.md format: cqrs-es-review @@ -214,18 +185,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-cqrs-es - - name: frontend-review edit: false persona: frontend-reviewer stance: review + knowledge: frontend report: name: 05-frontend-review.md format: frontend-review @@ -233,18 +203,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-frontend - - name: security-review edit: false persona: security-reviewer stance: review + knowledge: security report: name: 06-security-review.md format: security-review @@ -252,14 +221,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-security - - name: qa-review edit: false persona: qa-reviewer @@ -271,7 +238,6 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: @@ -283,14 +249,19 @@ movements: next: supervise - condition: any("needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture allowed_tools: - Read - Glob @@ -307,10 +278,6 @@ movements: - condition: 修正を進行できない next: plan instruction: fix - - # =========================================== - # Movement 4: Supervision - # =========================================== - name: supervise edit: false persona: expert-supervisor @@ -330,14 +297,19 @@ movements: next: COMPLETE - condition: 問題が検出された next: fix_supervisor - - name: fix_supervisor edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - cqrs-es + - security + - architecture allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/expert-cqrs.yaml b/builtins/ja/pieces/expert-cqrs.yaml index 3b39625..029f1ce 100644 --- a/builtins/ja/pieces/expert-cqrs.yaml +++ b/builtins/ja/pieces/expert-cqrs.yaml @@ -39,8 +39,10 @@ stances: knowledge: frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md cqrs-es: ../knowledge/cqrs-es.md security: ../knowledge/security.md + architecture: ../knowledge/architecture.md personas: planner: ../personas/planner.md @@ -113,6 +115,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, cqrs-es, security, architecture] report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -168,6 +171,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, cqrs-es, security, architecture] allowed_tools: - Read - Glob @@ -210,7 +214,7 @@ movements: edit: false persona: cqrs-es-reviewer stance: review - knowledge: cqrs-es + knowledge: [cqrs-es, backend] report: name: 04-cqrs-es-review.md format: cqrs-es-review @@ -296,6 +300,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, cqrs-es, security, architecture] allowed_tools: - Read - Glob @@ -342,6 +347,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, cqrs-es, security, architecture] allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/expert-hybrid-codex.yaml b/builtins/ja/pieces/expert-hybrid-codex.yaml index ad68d80..b4e772e 100644 --- a/builtins/ja/pieces/expert-hybrid-codex.yaml +++ b/builtins/ja/pieces/expert-hybrid-codex.yaml @@ -1,33 +1,18 @@ -# Expert Review Piece -# アーキテクチャ、フロントエンド、セキュリティ、QAの専門家によるレビューピース -# -# フロー: -# plan -> implement -> ai_review -> reviewers (parallel) -> supervise -> COMPLETE -# ↓ ├─ arch-review ↓ -# ai_fix ├─ frontend-review fix_supervisor -# ├─ security-review -# └─ qa-review -# any("needs_fix") → fix → reviewers -# -# テンプレート変数: -# {iteration} - ピース全体のターン数(全エージェントで実行されたムーブメントの合計) -# {max_iterations} - ピースの最大イテレーション数 -# {movement_iteration} - ムーブメントごとのイテレーション数(このムーブメントが何回実行されたか) -# {task} - 元のユーザー要求 -# {previous_response} - 前のムーブメントの出力 -# {user_inputs} - ピース中に蓄積されたユーザー入力 -# {report_dir} - レポートディレクトリ名(例: "20250126-143052-task-summary") +# Auto-generated from expert.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: expert-hybrid-codex description: アーキテクチャ・フロントエンド・セキュリティ・QA専門家レビュー - max_iterations: 30 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - +knowledge: + frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md + security: ../knowledge/security.md + architecture: ../knowledge/architecture.md personas: planner: ../personas/planner.md coder: ../personas/coder.md @@ -37,7 +22,6 @@ personas: security-reviewer: ../personas/security-reviewer.md qa-reviewer: ../personas/qa-reviewer.md expert-supervisor: ../personas/expert-supervisor.md - instructions: plan: ../instructions/plan.md implement: ../instructions/implement.md @@ -51,7 +35,6 @@ instructions: fix: ../instructions/fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: plan: ../report-formats/plan.md ai-review: ../report-formats/ai-review.md @@ -61,13 +44,8 @@ report_formats: qa-review: ../report-formats/qa-review.md validation: ../report-formats/validation.md summary: ../report-formats/summary.md - initial_movement: plan - movements: - # =========================================== - # Movement 0: Planning - # =========================================== - name: plan edit: false persona: planner @@ -87,18 +65,19 @@ movements: next: implement - condition: 要件が不明確で計画を立てられない next: ABORT - - # =========================================== - # Movement 1: Implementation - # =========================================== - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - security + - architecture report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -123,10 +102,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - # =========================================== - # Movement 2: AI Review - # =========================================== - name: ai_review edit: false persona: ai-antipattern-reviewer @@ -146,15 +121,19 @@ movements: next: reviewers - condition: AI特有の問題が検出された next: ai_fix - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex session: refresh + knowledge: + - frontend + - backend + - security + - architecture allowed_tools: - Read - Glob @@ -172,7 +151,6 @@ movements: next: ai_no_fix - condition: 修正を進行できない next: ai_no_fix - - name: ai_no_fix edit: false persona: architecture-reviewer @@ -187,16 +165,15 @@ movements: - condition: ai_fixの判断が妥当(修正不要) next: reviewers instruction: arbitrate - - # =========================================== - # Movement 3: Expert Reviews (Parallel) - # =========================================== - name: reviewers parallel: - name: arch-review edit: false persona: architecture-reviewer stance: review + knowledge: + - architecture + - backend report: name: 04-architect-review.md format: architecture-review @@ -204,18 +181,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-arch - - name: frontend-review edit: false persona: frontend-reviewer stance: review + knowledge: frontend report: name: 05-frontend-review.md format: frontend-review @@ -223,18 +199,17 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-frontend - - name: security-review edit: false persona: security-reviewer stance: review + knowledge: security report: name: 06-security-review.md format: security-review @@ -242,14 +217,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: - condition: approved - condition: needs_fix instruction: review-security - - name: qa-review edit: false persona: qa-reviewer @@ -261,7 +234,6 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch rules: @@ -273,14 +245,18 @@ movements: next: supervise - condition: any("needs_fix") next: fix - - name: fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - security + - architecture allowed_tools: - Read - Glob @@ -297,10 +273,6 @@ movements: - condition: 修正を進行できない next: plan instruction: fix - - # =========================================== - # Movement 4: Supervision - # =========================================== - name: supervise edit: false persona: expert-supervisor @@ -320,14 +292,18 @@ movements: next: COMPLETE - condition: 問題が検出された next: fix_supervisor - - name: fix_supervisor edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex + knowledge: + - frontend + - backend + - security + - architecture allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/expert.yaml b/builtins/ja/pieces/expert.yaml index 145d3e5..12274b1 100644 --- a/builtins/ja/pieces/expert.yaml +++ b/builtins/ja/pieces/expert.yaml @@ -30,6 +30,7 @@ stances: knowledge: frontend: ../knowledge/frontend.md + backend: ../knowledge/backend.md security: ../knowledge/security.md architecture: ../knowledge/architecture.md @@ -103,6 +104,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, security, architecture] report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -158,6 +160,7 @@ movements: - coding - testing session: refresh + knowledge: [frontend, backend, security, architecture] allowed_tools: - Read - Glob @@ -200,7 +203,7 @@ movements: edit: false persona: architecture-reviewer stance: review - knowledge: architecture + knowledge: [architecture, backend] report: name: 04-architect-review.md format: architecture-review @@ -286,6 +289,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, security, architecture] allowed_tools: - Read - Glob @@ -332,6 +336,7 @@ movements: stance: - coding - testing + knowledge: [frontend, backend, security, architecture] allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/minimal-hybrid-codex.yaml b/builtins/ja/pieces/minimal-hybrid-codex.yaml index a2836b1..ed8ad62 100644 --- a/builtins/ja/pieces/minimal-hybrid-codex.yaml +++ b/builtins/ja/pieces/minimal-hybrid-codex.yaml @@ -1,51 +1,34 @@ -# Simple TAKT Piece -# Implement -> AI Review -> Supervisor Approval -# (最もシンプルな構成 - plan, architect review, fix ムーブメントなし) -# -# Template Variables (auto-injected): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request (auto-injected) -# {previous_response} - Output from the previous movement (auto-injected) -# {user_inputs} - Accumulated user inputs during piece (auto-injected) -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from minimal.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: minimal-hybrid-codex description: Minimal development piece (implement -> parallel review -> fix if needed -> complete) - max_iterations: 20 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - personas: coder: ../personas/coder.md ai-antipattern-reviewer: ../personas/ai-antipattern-reviewer.md supervisor: ../personas/supervisor.md - instructions: implement: ../instructions/implement.md review-ai: ../instructions/review-ai.md ai-fix: ../instructions/ai-fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: ai-review: ../report-formats/ai-review.md - initial_movement: implement - movements: - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -69,7 +52,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - name: reviewers parallel: - name: ai_review @@ -83,14 +65,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch instruction: review-ai rules: - - condition: "AI特有の問題なし" - - condition: "AI特有の問題あり" - + - condition: AI特有の問題なし + - condition: AI特有の問題あり - name: supervise edit: false persona: supervisor @@ -102,15 +82,13 @@ movements: - Read - Glob - Grep - - Bash - WebSearch - WebFetch instruction: supervise rules: - - condition: "すべて問題なし" - - condition: "要求未達成、テスト失敗、ビルドエラー" - + - condition: すべて問題なし + - condition: 要求未達成、テスト失敗、ビルドエラー rules: - condition: all("AI特有の問題なし", "すべて問題なし") next: COMPLETE @@ -120,22 +98,20 @@ movements: next: ai_fix - condition: any("要求未達成、テスト失敗、ビルドエラー") next: supervise_fix - - name: fix_both parallel: - name: ai_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -145,20 +121,18 @@ movements: - condition: 修正不要(指摘対象ファイル/仕様の確認済み) - condition: 判断できない、情報不足 instruction: ai-fix - - name: supervise_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -167,20 +141,18 @@ movements: - condition: 監督者の指摘に対する修正が完了した - condition: 修正を進行できない instruction: fix-supervisor - rules: - condition: all("AI問題の修正完了", "監督者の指摘に対する修正が完了した") next: reviewers - condition: any("修正不要(指摘対象ファイル/仕様の確認済み)", "判断できない、情報不足", "修正を進行できない") next: implement - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob @@ -199,14 +171,13 @@ movements: - condition: 判断できない、情報不足 next: implement instruction: ai-fix - - name: supervise_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/passthrough-hybrid-codex.yaml b/builtins/ja/pieces/passthrough-hybrid-codex.yaml index 4abff71..b122f5c 100644 --- a/builtins/ja/pieces/passthrough-hybrid-codex.yaml +++ b/builtins/ja/pieces/passthrough-hybrid-codex.yaml @@ -1,33 +1,23 @@ -# Passthrough TAKT Piece -# タスクをそのままエージェントに渡す最薄ラッパー。 -# -# フロー: -# execute (タスク実行) -# ↓ -# COMPLETE +# Auto-generated from passthrough.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: passthrough-hybrid-codex description: Single-agent thin wrapper. Pass task directly to coder as-is. - max_iterations: 10 - stances: coding: ../stances/coding.md testing: ../stances/testing.md - personas: coder: ../personas/coder.md - initial_movement: execute - movements: - name: execute edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex report: - Summary: summary.md allowed_tools: diff --git a/builtins/ja/pieces/review-fix-minimal-hybrid-codex.yaml b/builtins/ja/pieces/review-fix-minimal-hybrid-codex.yaml index af9f411..ecc8f42 100644 --- a/builtins/ja/pieces/review-fix-minimal-hybrid-codex.yaml +++ b/builtins/ja/pieces/review-fix-minimal-hybrid-codex.yaml @@ -1,51 +1,34 @@ -# Review-Fix Minimal TAKT Piece -# Review -> Fix (if needed) -> Re-review -> Complete -# (レビューから開始、実装ムーブメントなし) -# -# Template Variables (auto-injected): -# {iteration} - Piece-wide turn count (total movements executed across all agents) -# {max_iterations} - Maximum iterations allowed for the piece -# {movement_iteration} - Per-movement iteration count (how many times THIS movement has been executed) -# {task} - Original user request (auto-injected) -# {previous_response} - Output from the previous movement (auto-injected) -# {user_inputs} - Accumulated user inputs during piece (auto-injected) -# {report_dir} - Report directory name (e.g., "20250126-143052-task-summary") +# Auto-generated from review-fix-minimal.yaml by tools/generate-hybrid-codex.mjs +# Do not edit manually. Edit the source piece and re-run the generator. name: review-fix-minimal-hybrid-codex description: 既存コードのレビューと修正ピース(レビュー開始、実装なし) - max_iterations: 20 - stances: coding: ../stances/coding.md review: ../stances/review.md testing: ../stances/testing.md - personas: coder: ../personas/coder.md ai-antipattern-reviewer: ../personas/ai-antipattern-reviewer.md supervisor: ../personas/supervisor.md - instructions: implement: ../instructions/implement.md review-ai: ../instructions/review-ai.md ai-fix: ../instructions/ai-fix.md supervise: ../instructions/supervise.md fix-supervisor: ../instructions/fix-supervisor.md - report_formats: ai-review: ../report-formats/ai-review.md - initial_movement: reviewers - movements: - name: implement edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex report: - Scope: 01-coder-scope.md - Decisions: 02-coder-decisions.md @@ -69,7 +52,6 @@ movements: next: implement requires_user_input: true interactive_only: true - - name: reviewers parallel: - name: ai_review @@ -83,14 +65,12 @@ movements: - Read - Glob - Grep - - WebSearch - WebFetch instruction: review-ai rules: - - condition: "AI特有の問題なし" - - condition: "AI特有の問題あり" - + - condition: AI特有の問題なし + - condition: AI特有の問題あり - name: supervise edit: false persona: supervisor @@ -102,15 +82,13 @@ movements: - Read - Glob - Grep - - Bash - WebSearch - WebFetch instruction: supervise rules: - - condition: "すべて問題なし" - - condition: "要求未達成、テスト失敗、ビルドエラー" - + - condition: すべて問題なし + - condition: 要求未達成、テスト失敗、ビルドエラー rules: - condition: all("AI特有の問題なし", "すべて問題なし") next: COMPLETE @@ -120,22 +98,20 @@ movements: next: ai_fix - condition: any("要求未達成、テスト失敗、ビルドエラー") next: supervise_fix - - name: fix_both parallel: - name: ai_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -145,20 +121,18 @@ movements: - condition: 修正不要(指摘対象ファイル/仕様の確認済み) - condition: 判断できない、情報不足 instruction: ai-fix - - name: supervise_fix_parallel edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob - Grep - Edit - - Bash - WebSearch - WebFetch @@ -167,20 +141,18 @@ movements: - condition: 監督者の指摘に対する修正が完了した - condition: 修正を進行できない instruction: fix-supervisor - rules: - condition: all("AI問題の修正完了", "監督者の指摘に対する修正が完了した") next: reviewers - condition: any("修正不要(指摘対象ファイル/仕様の確認済み)", "判断できない、情報不足", "修正を進行できない") next: implement - - name: ai_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob @@ -199,14 +171,13 @@ movements: - condition: 判断できない、情報不足 next: implement instruction: ai-fix - - name: supervise_fix edit: true persona: coder + provider: codex stance: - coding - testing - provider: codex allowed_tools: - Read - Glob diff --git a/builtins/ja/pieces/review-only.yaml b/builtins/ja/pieces/review-only.yaml index 08a1ad6..96498c0 100644 --- a/builtins/ja/pieces/review-only.yaml +++ b/builtins/ja/pieces/review-only.yaml @@ -27,6 +27,10 @@ max_iterations: 10 stances: review: ../stances/review.md +knowledge: + architecture: ../knowledge/architecture.md + security: ../knowledge/security.md + personas: planner: ../personas/planner.md architecture-reviewer: ../personas/architecture-reviewer.md @@ -90,6 +94,7 @@ movements: edit: false persona: architecture-reviewer stance: review + knowledge: architecture report: name: 01-architect-review.md format: architecture-review @@ -109,6 +114,7 @@ movements: edit: false persona: security-reviewer stance: review + knowledge: security report: name: 02-security-review.md format: security-review diff --git a/builtins/skill/SKILL.md b/builtins/skill/SKILL.md index e2226e7..e385438 100644 --- a/builtins/skill/SKILL.md +++ b/builtins/skill/SKILL.md @@ -1,10 +1,39 @@ --- -name: takt-engine -description: TAKT ピースエンジン。Agent Team を使ったマルチエージェントオーケストレーション。/takt コマンドから使用される。 +name: takt +description: TAKT ピースエンジン。Agent Team を使ったマルチエージェントオーケストレーション。ピースYAMLワークフローに従ってマルチエージェントを実行する。 +user-invocable: true --- # TAKT Piece Engine +## 引数の解析 + +$ARGUMENTS を以下のように解析する: + +``` +/takt {piece} [permission] {task...} +``` + +- **第1トークン**: ピース名またはYAMLファイルパス(必須) +- **第2トークン**: 権限モード(任意)。以下のキーワードの場合は権限モードとして解釈する: + - `--permit-full` — 全権限付与(mode: "bypassPermissions") + - `--permit-edit` — 編集許可(mode: "acceptEdits") + - 上記以外 → タスク内容の一部として扱う +- **残りのトークン**: タスク内容(省略時は AskUserQuestion でユーザーに入力を求める) +- **権限モード省略時のデフォルト**: `"default"`(権限確認あり) + +例: +- `/takt coding FizzBuzzを作って` → coding ピース、default 権限 +- `/takt coding --permit-full FizzBuzzを作って` → coding ピース、bypassPermissions +- `/takt /path/to/custom.yaml 実装して` → カスタムYAML、default 権限 + +## 事前準備: リファレンスの読み込み + +手順を開始する前に、以下の2ファイルを **Read tool で読み込む**: + +1. `~/.claude/skills/takt/references/engine.md` - プロンプト構築、レポート管理、ループ検出の詳細 +2. `~/.claude/skills/takt/references/yaml-schema.md` - ピースYAMLの構造定義 + ## あなたの役割: Team Lead あなたは **Team Lead(オーケストレーター)** である。 @@ -30,11 +59,11 @@ description: TAKT ピースエンジン。Agent Team を使ったマルチエー | やること | 使うツール | 説明 | |---------|-----------|------| -| チーム作成 | **Teammate** tool (operation: "spawnTeam") | 最初に1回だけ呼ぶ | -| チーム解散 | **Teammate** tool (operation: "cleanup") | 最後に1回だけ呼ぶ | +| チーム作成 | **TeamCreate** tool | 最初に1回だけ呼ぶ | +| チーム解散 | **TeamDelete** tool | 最後に1回だけ呼ぶ | | チームメイト起動 | **Task** tool (team_name 付き) | movement ごとに呼ぶ。**結果は同期的に返る** | -**Teammate tool でチームメイトを個別に起動することはできない。** チームメイトの起動は必ず Task tool を使う。 +**TeamCreate / TeamDelete でチームメイトを個別に起動することはできない。** チームメイトの起動は必ず Task tool を使う。 **Task tool は同期的に結果を返す。** TaskOutput やポーリングは不要。呼べば結果が返ってくる。 ## 手順(この順序で厳密に実行せよ) @@ -55,24 +84,24 @@ description: TAKT ピースエンジン。Agent Team を使ったマルチエー YAMLから以下を抽出する(→ references/yaml-schema.md 参照): - `name`, `max_iterations`, `initial_movement`, `movements` 配列 +- セクションマップ: `personas`, `stances`, `instructions`, `report_formats`, `knowledge` -### 手順 2: エージェント .md の事前読み込み +### 手順 2: セクションリソースの事前読み込み -全 movement(parallel のサブステップ含む)から `agent:` パスを収集する。 +ピースYAMLのセクションマップ(`personas:`, `stances:`, `instructions:`, `report_formats:`, `knowledge:`)から全ファイルパスを収集する。 パスは **ピースYAMLファイルのディレクトリからの相対パス** で解決する。 -例: ピースが `~/.claude/skills/takt/pieces/coding.yaml` にあり、`agent: ../agents/default/coder.md` の場合 -→ 絶対パスは `~/.claude/skills/takt/agents/default/coder.md` +例: ピースが `~/.claude/skills/takt/pieces/default.yaml` にあり、`personas:` に `coder: ../personas/coder.md` がある場合 +→ 絶対パスは `~/.claude/skills/takt/personas/coder.md` -重複を除いて Read で全て読み込む。読み込んだ内容はチームメイトへのプロンプトに使う。 +重複を除いて Read で全て読み込む。読み込んだ内容はチームメイトへのプロンプト構築に使う。 ### 手順 3: Agent Team 作成 -**今すぐ** Teammate tool を呼べ: +**今すぐ** TeamCreate tool を呼べ: ``` -Teammate tool を呼ぶ: - operation: "spawnTeam" +TeamCreate tool を呼ぶ: team_name: "takt" description: "TAKT {piece_name} ワークフロー" ``` @@ -84,7 +113,7 @@ Teammate tool を呼ぶ: - `iteration = 1` - `current_movement = initial_movement の movement 定義` - `previous_response = ""` -- `permission_mode = コマンドで解析された権限モード("bypassPermissions" または "default")` +- `permission_mode = コマンドで解析された権限モード("bypassPermissions" / "acceptEdits" / "default")` - `movement_history = []`(遷移履歴。Loop Monitor 用) **レポートディレクトリ**: いずれかの movement に `report` フィールドがある場合、`.takt/reports/{YYYYMMDD-HHmmss}-{slug}/` を作成し、パスを `report_dir` 変数に保持する。 @@ -97,6 +126,14 @@ Teammate tool を呼ぶ: current_movement のプロンプトを構築する(→ references/engine.md のプロンプト構築を参照)。 +プロンプト構築の要素: +1. **ペルソナ**: `persona:` キー → `personas:` セクション → .md ファイル内容 +2. **スタンス**: `stance:` キー → `stances:` セクション → .md ファイル内容(複数可、末尾にリマインダー再掲) +3. **実行コンテキスト**: cwd, ピース名, movement名, イテレーション情報 +4. **ナレッジ**: `knowledge:` キー → `knowledge:` セクション → .md ファイル内容 +5. **インストラクション**: `instruction:` キー → `instructions:` セクション → .md ファイル内容(テンプレート変数展開済み) +6. **タスク/前回出力/レポート指示/タグ指示**: 自動注入 + **通常 movement の場合(parallel フィールドなし):** Task tool を1つ呼ぶ。**Task tool は同期的に結果を返す。待機やポーリングは不要。** @@ -183,10 +220,9 @@ matched_rule の `next` を確認する: ### 手順 8: 終了 -1. Teammate tool を呼ぶ: +1. TeamDelete tool を呼ぶ: ``` -Teammate tool を呼ぶ: - operation: "cleanup" +TeamDelete tool を呼ぶ ``` 2. ユーザーに結果を報告する: diff --git a/builtins/skill/references/engine.md b/builtins/skill/references/engine.md index ef6e95a..3777678 100644 --- a/builtins/skill/references/engine.md +++ b/builtins/skill/references/engine.md @@ -20,7 +20,8 @@ Task tool: ### permission_mode コマンド引数で解析された `permission_mode` をそのまま Task tool の `mode` に渡す。 -- `/takt coding yolo タスク` → `permission_mode = "bypassPermissions"`(確認なし) +- `/takt coding --permit-full タスク` → `permission_mode = "bypassPermissions"`(確認なし) +- `/takt coding --permit-edit タスク` → `permission_mode = "acceptEdits"`(編集は自動許可) - `/takt coding タスク` → `permission_mode = "default"`(権限確認あり) ## 通常 Movement の実行 @@ -42,7 +43,7 @@ Task tool: 2. **全ての Task tool を1つのメッセージで並列に呼び出す**(依存関係がないため) 3. 全チームメイトの完了を待つ 4. 各サブステップの出力を収集する -5. 各サブステップの出力に対して、そのサブステップの `rules` で条件マッチを判定する +5. 各サブステップの出力に対して、そのサブステップの `rules` で条件マッチを判定 6. 親 movement の `rules` で aggregate 評価(all()/any())を行う ### サブステップの条件マッチ判定 @@ -55,6 +56,21 @@ Task tool: マッチした condition 文字列を記録する(次の aggregate 評価で使う)。 +## セクションマップの解決 + +ピースYAMLのトップレベルにある `personas:`, `stances:`, `instructions:`, `report_formats:`, `knowledge:` はキーとファイルパスの対応表。movement 内ではキー名で参照する。 + +### 解決手順 + +1. ピースYAMLを読み込む +2. 各セクションマップのパスを、**ピースYAMLファイルのディレクトリ**を基準に絶対パスに変換する +3. movement の `persona: coder` → `personas:` セクションの `coder` キー → ファイルパス → Read で内容を取得 + +例: ピースが `~/.claude/skills/takt/pieces/default.yaml` の場合 +- `personas.coder: ../personas/coder.md` → `~/.claude/skills/takt/personas/coder.md` +- `stances.coding: ../stances/coding.md` → `~/.claude/skills/takt/stances/coding.md` +- `instructions.plan: ../instructions/plan.md` → `~/.claude/skills/takt/instructions/plan.md` + ## プロンプト構築 各チームメイト起動時、以下を結合してプロンプトを組み立てる。 @@ -62,16 +78,54 @@ Task tool: ### 構成要素(上から順に結合) ``` -1. エージェントプロンプト(agent: で参照される .md の全内容) +1. ペルソナプロンプト(persona: で参照される .md の全内容) 2. ---(区切り線) -3. 実行コンテキスト情報 -4. instruction_template の内容(テンプレート変数を展開済み) -5. ユーザーのタスク({task} が template に含まれない場合、末尾に自動追加) -6. 前の movement の出力(pass_previous_response: true の場合、自動追加) -7. レポート出力指示(report フィールドがある場合、自動追加) -8. ステータスタグ出力指示(rules がある場合、自動追加) +3. スタンス(stance: で参照される .md の内容。複数ある場合は結合) +4. ---(区切り線) +5. 実行コンテキスト情報 +6. ナレッジ(knowledge: で参照される .md の内容) +7. インストラクション内容(instruction: で参照される .md、または instruction_template のインライン内容) +8. ユーザーのタスク({task} が template に含まれない場合、末尾に自動追加) +9. 前の movement の出力(pass_previous_response: true の場合、自動追加) +10. レポート出力指示(report フィールドがある場合、自動追加) +11. ステータスタグ出力指示(rules がある場合、自動追加) +12. スタンスリマインダー(スタンスがある場合、末尾に再掲) ``` +### ペルソナプロンプト + +movement の `persona:` キーからセクションマップを経由して .md ファイルを解決し、その全内容をプロンプトの冒頭に配置する。ペルソナはドメイン知識と行動原則のみを含む(ピース固有の手順は含まない)。 + +### スタンス注入 + +movement の `stance:` キー(単一または配列)からスタンスファイルを解決し、内容を結合する。スタンスは行動ルール(コーディング規約、レビュー基準等)を定義する。 + +**Lost in the Middle 対策**: スタンスはプロンプトの前半に配置し、末尾にリマインダーとして再掲する。 + +``` +(プロンプト冒頭付近) +## スタンス(行動ルール) +{スタンスの内容} + +(プロンプト末尾) +--- +**リマインダー**: 以下のスタンスに従ってください。 +{スタンスの内容(再掲)} +``` + +### ナレッジ注入 + +movement の `knowledge:` キーからナレッジファイルを解決し、ドメイン固有の参考情報としてプロンプトに含める。 + +``` +## ナレッジ +{ナレッジの内容} +``` + +### インストラクション + +movement の `instruction:` キーから指示テンプレートファイルを解決する。または `instruction_template:` でインライン記述。テンプレート変数({task}, {previous_response} 等)を展開した上でプロンプトに含める。 + ### 実行コンテキスト情報 ``` @@ -85,7 +139,7 @@ Task tool: ### テンプレート変数の展開 -`instruction_template` 内の以下のプレースホルダーを置換する: +インストラクション内の以下のプレースホルダーを置換する: | 変数 | 値 | |-----|-----| @@ -99,31 +153,28 @@ Task tool: ### {report:ファイル名} の処理 -`instruction_template` 内に `{report:04-ai-review.md}` のような記法がある場合: +インストラクション内に `{report:04-ai-review.md}` のような記法がある場合: 1. レポートディレクトリ内に対応するレポートファイルがあれば Read で読む 2. 読み込んだ内容をプレースホルダーに展開する 3. ファイルが存在しない場合は「(レポート未作成)」に置換する -### agent フィールドがない場合 +### persona フィールドがない場合 -`agent:` が指定されていない movement の場合、エージェントプロンプト部分を省略し、`instruction_template` の内容のみでプロンプトを構成する。 +`persona:` が指定されていない movement の場合、ペルソナプロンプト部分を省略し、インストラクションの内容のみでプロンプトを構成する。 ## レポート出力指示の自動注入 movement に `report` フィールドがある場合、プロンプト末尾にレポート出力指示を自動追加する。 -### 形式1: name + format +### 形式1: name + format(キー参照) ```yaml report: name: 01-plan.md - format: | - # タスク計画 - ## 元の要求 - ... + format: plan # report_formats セクションのキー ``` -→ プロンプトに追加する指示: +→ `report_formats:` セクションの `plan` キーから .md ファイルを解決し、Read で読んだ内容をフォーマット指示に使う: ``` --- @@ -133,9 +184,7 @@ report: ファイル名: 01-plan.md フォーマット: -# タスク計画 -## 元の要求 -... +{report_formats の plan キーの .md ファイル内容} ``` ### 形式2: 配列(複数レポート) @@ -288,7 +337,7 @@ loop_monitors: - cycle: [ai_review, ai_fix] threshold: 3 judge: - agent: ../agents/default/supervisor.md + persona: supervisor instruction_template: | サイクルが {cycle_count} 回繰り返されました... rules: @@ -303,7 +352,7 @@ loop_monitors: 1. movement 遷移履歴を記録する(例: `[plan, implement, ai_review, ai_fix, ai_review, ai_fix, ...]`) 2. 各 loop_monitor の `cycle` パターンが履歴の末尾に `threshold` 回以上連続で出現するかチェックする 3. 閾値に達した場合: - a. judge の `agent` を Read で読み込む + a. judge の `persona` キーからペルソナファイルを Read で読み込む b. `instruction_template` の `{cycle_count}` を実際のサイクル回数に置換する c. Task tool でチームメイト(judge)を起動する d. judge の出力を judge の `rules` で評価する @@ -332,16 +381,16 @@ loop_monitors: ### レポートの参照 -後続の movement の `instruction_template` 内で `{report:ファイル名}` として参照すると、そのレポートファイルを Read して内容をプレースホルダーに展開する。 +後続の movement のインストラクション内で `{report:ファイル名}` として参照すると、そのレポートファイルを Read して内容をプレースホルダーに展開する。 ## 状態遷移の全体像 ``` [開始] ↓ -ピースYAML読み込み + エージェント .md 読み込み +ピースYAML読み込み + セクションマップ解決(personas, stances, instructions, report_formats, knowledge) ↓ -Teammate(spawnTeam) でチーム作成 +TeamCreate でチーム作成 ↓ レポートディレクトリ作成 ↓ @@ -349,8 +398,9 @@ initial_movement を取得 ↓ ┌─→ Task tool でチームメイト起動 │ ├── 通常: 1つの Task tool 呼び出し -│ │ prompt = agent.md + context + instruction + task -│ │ + previous_response + レポート指示 + タグ指示 +│ │ prompt = persona + stance + context + knowledge +│ │ + instruction + task + previous_response +│ │ + レポート指示 + タグ指示 + スタンスリマインダー │ └── parallel: 複数の Task tool を1メッセージで並列呼び出し │ 各サブステップを別々のチームメイトとして起動 │ ↓ @@ -366,8 +416,8 @@ initial_movement を取得 │ ├── parallel: サブステップ条件 → aggregate(all/any) │ ↓ │ next を決定 -│ ├── COMPLETE → Teammate(cleanup) → ユーザーに結果報告 -│ ├── ABORT → Teammate(cleanup) → ユーザーにエラー報告 +│ ├── COMPLETE → TeamDelete → ユーザーに結果報告 +│ ├── ABORT → TeamDelete → ユーザーにエラー報告 │ └── movement名 → ループ検出チェック → 次の movement │ ↓ └──────────────────────────────────────────────┘ diff --git a/builtins/skill/references/yaml-schema.md b/builtins/skill/references/yaml-schema.md index edd455f..86e36ee 100644 --- a/builtins/skill/references/yaml-schema.md +++ b/builtins/skill/references/yaml-schema.md @@ -9,58 +9,92 @@ name: piece-name # ピース名(必須) description: 説明テキスト # ピースの説明(任意) max_iterations: 10 # 最大イテレーション数(必須) initial_movement: plan # 最初に実行する movement 名(必須) + +# セクションマップ(キー → ファイルパスの対応表) +stances: # スタンス定義(任意) + coding: ../stances/coding.md + review: ../stances/review.md +personas: # ペルソナ定義(任意) + coder: ../personas/coder.md + reviewer: ../personas/architecture-reviewer.md +instructions: # 指示テンプレート定義(任意) + plan: ../instructions/plan.md + implement: ../instructions/implement.md +report_formats: # レポートフォーマット定義(任意) + plan: ../report-formats/plan.md + review: ../report-formats/architecture-review.md +knowledge: # ナレッジ定義(任意) + architecture: ../knowledge/architecture.md + movements: [...] # movement 定義の配列(必須) loop_monitors: [...] # ループ監視設定(任意) ``` +### セクションマップの解決 + +各セクションマップのパスは **ピースYAMLファイルのディレクトリからの相対パス** で解決する。 +movement 内では**キー名**で参照する(パスを直接書かない)。 + +例: ピースが `~/.claude/skills/takt/pieces/coding.yaml` にあり、`personas:` セクションに `coder: ../personas/coder.md` がある場合 +→ 絶対パスは `~/.claude/skills/takt/personas/coder.md` +→ movement では `persona: coder` で参照 + ## Movement 定義 ### 通常 Movement ```yaml - name: movement-name # movement 名(必須、一意) - agent: ../agents/path.md # エージェントプロンプトへの相対パス(任意) - agent_name: coder # 表示名(任意) + persona: coder # ペルソナキー(personas マップを参照、任意) + stance: coding # スタンスキー(stances マップを参照、任意) + stance: [coding, testing] # 複数指定も可(配列) + instruction: implement # 指示テンプレートキー(instructions マップを参照、任意) + knowledge: architecture # ナレッジキー(knowledge マップを参照、任意) edit: true # ファイル編集可否(必須) permission_mode: edit # 権限モード: edit / readonly / full(任意) session: refresh # セッション管理(任意) pass_previous_response: true # 前の出力を渡すか(デフォルト: true) allowed_tools: [...] # 許可ツール一覧(任意、参考情報) - instruction_template: | # ステップ固有の指示テンプレート(任意) + instruction_template: | # インライン指示テンプレート(instruction キーの代替、任意) 指示内容... report: ... # レポート設定(任意) rules: [...] # 遷移ルール(必須) ``` +**`instruction` vs `instruction_template`**: `instruction` はトップレベル `instructions:` セクションのキー参照。`instruction_template` はインラインで指示を記述。どちらか一方を使用する。 + ### Parallel Movement ```yaml - name: reviewers # 親 movement 名(必須) parallel: # 並列サブステップ配列(これがあると parallel movement) - - name: sub-step-1 # サブステップ名 - agent: ../agents/a.md + - name: arch-review + persona: architecture-reviewer + stance: review + knowledge: architecture edit: false - instruction_template: | - ... - rules: # サブステップの rules(condition のみ、next は無視される) + instruction: review-arch + report: + name: 05-architect-review.md + format: architecture-review + rules: - condition: "approved" - condition: "needs_fix" - # report, allowed_tools 等も指定可能 - - name: sub-step-2 - agent: ../agents/b.md + - name: qa-review + persona: qa-reviewer + stance: review edit: false - instruction_template: | - ... + instruction: review-qa rules: - - condition: "passed" - - condition: "failed" + - condition: "approved" + - condition: "needs_fix" rules: # 親の rules(aggregate 条件で遷移先を決定) - - condition: all("approved", "passed") - next: complete-step - - condition: any("needs_fix", "failed") - next: fix-step + - condition: all("approved") + next: supervise + - condition: any("needs_fix") + next: fix ``` **重要**: サブステップの `rules` は結果分類のための condition 定義のみ。`next` は無視される(親の rules が遷移先を決定)。 @@ -97,21 +131,27 @@ rules: ## Report 定義 -### 形式1: 単一レポート(name + format) +### 形式1: 単一レポート(name + format キー参照) ```yaml report: name: 01-plan.md - format: | - ```markdown + format: plan # report_formats マップのキーを参照 +``` + +`format` がキー文字列の場合、トップレベル `report_formats:` セクションから対応する .md ファイルを読み込み、フォーマット指示として使用する。 + +### 形式1b: 単一レポート(name + format インライン) + +```yaml +report: + name: 01-plan.md + format: | # インラインでフォーマットを記述 # レポートタイトル ## セクション {内容} - ``` ``` -`format` はエージェントへの出力フォーマット指示。レポート抽出時の参考情報。 - ### 形式2: 複数レポート(配列) ```yaml @@ -125,7 +165,7 @@ report: ## テンプレート変数 -`instruction_template` 内で使用可能な変数: +`instruction_template`(またはインストラクションファイル)内で使用可能な変数: | 変数 | 説明 | |-----|------| @@ -146,7 +186,7 @@ loop_monitors: - cycle: [movement_a, movement_b] # 監視対象の movement サイクル threshold: 3 # 発動閾値(サイクル回数) judge: - agent: ../agents/supervisor.md # 判定エージェント + persona: supervisor # ペルソナキー参照 instruction_template: | # 判定用指示 サイクルが {cycle_count} 回繰り返されました。 健全性を判断してください。 @@ -157,7 +197,7 @@ loop_monitors: next: alternative_movement ``` -特定の movement 間のサイクルが閾値に達した場合、judge エージェントが介入して遷移先を判断する。 +特定の movement 間のサイクルが閾値に達した場合、judge が介入して遷移先を判断する。 ## allowed_tools について diff --git a/builtins/skill/takt-command.md b/builtins/skill/takt-command.md deleted file mode 100644 index d3d4641..0000000 --- a/builtins/skill/takt-command.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: takt -description: TAKT ピースランナー。ピースYAMLワークフローに従ってマルチエージェントを実行する。 ---- - -TAKT ピースランナーを実行する。 - -## 引数 - -$ARGUMENTS を以下のように解析する: - -``` -/takt {piece} [permission] {task...} -``` - -- **第1トークン**: ピース名またはYAMLファイルパス(必須) -- **第2トークン**: 権限モード(任意)。以下のキーワードの場合は権限モードとして解釈する: - - `yolo` — 全権限付与(mode: "bypassPermissions") - - 上記以外 → タスク内容の一部として扱う -- **残りのトークン**: タスク内容(省略時は AskUserQuestion でユーザーに入力を求める) -- **権限モード省略時のデフォルト**: `"default"`(権限確認あり) - -例: -- `/takt coding FizzBuzzを作って` → coding ピース、default 権限 -- `/takt coding yolo FizzBuzzを作って` → coding ピース、bypassPermissions -- `/takt passthrough yolo 全テストを実行` → passthrough ピース、bypassPermissions -- `/takt /path/to/custom.yaml 実装して` → カスタムYAML、default 権限 - -## 実行手順 - -以下のファイルを **Read tool で読み込み**、記載された手順に従って実行する: - -1. `~/.claude/skills/takt/SKILL.md` - エンジン概要とピース解決 -2. `~/.claude/skills/takt/references/engine.md` - 実行エンジンの詳細ロジック -3. `~/.claude/skills/takt/references/yaml-schema.md` - ピースYAML構造リファレンス - -**重要**: これら3ファイルを最初に全て読み込んでから、SKILL.md の「手順」に従って処理を開始する。 diff --git a/docs/README.ja.md b/docs/README.ja.md index 654e4e5..9ae88dd 100644 --- a/docs/README.ja.md +++ b/docs/README.ja.md @@ -783,6 +783,7 @@ export TAKT_OPENAI_API_KEY=sk-... ## ドキュメント +- [Faceted Prompting](./prompt-composition.ja.md) - AIプロンプトへの関心の分離(Persona, Stance, Instruction, Knowledge, Report Format) - [Piece Guide](./pieces.md) - ピースの作成とカスタマイズ - [Agent Guide](./agents.md) - カスタムエージェントの設定 - [Changelog](../CHANGELOG.md) - バージョン履歴 diff --git a/docs/prompt-composition.ja.md b/docs/prompt-composition.ja.md new file mode 100644 index 0000000..f21285c --- /dev/null +++ b/docs/prompt-composition.ja.md @@ -0,0 +1,149 @@ +# Faceted Prompting: AIプロンプトへの関心の分離 + +## 問題 + +マルチエージェントシステムが複雑になるにつれ、プロンプトはモノリシックになる。1つのプロンプトファイルにエージェントの役割、行動規範、タスク固有の指示、ドメイン知識、出力形式がすべて混在する。これは3つの問題を生む。 + +1. **再利用できない** — 2つのステップが同じレビュアーのペルソナを必要としつつ指示が異なる場合、プロンプト全体を複製するしかない +2. **暗黙的な結合** — コーディング規約を変更すると、それを参照するすべてのプロンプトを編集する必要がある +3. **責任の不明確さ** — プロンプトのどの部分がエージェントの「役割」を定義し、どの部分が「やるべきこと」を定義しているのか区別がつかない + +## アイデア + +ソフトウェア工学の基本原則である**関心の分離(Separation of Concerns)**をプロンプト設計に適用する。 + +エージェントごとに1つのモノリシックなプロンプトを書く代わりに、「何の関心を扱っているか」で独立した再利用可能なファイルに分解する。そしてワークフローのステップごとに宣言的に合成する。 + +## 5つの関心 + +Faceted Promptingはプロンプトを5つの直交する関心に分解する。 + +| 関心 | 答える問い | 例 | +|------|-----------|-----| +| **Persona** | エージェントは*誰*か? | 役割定義、専門性、判断基準 | +| **Stance** | *どう*振る舞うべきか? | コーディング規約、レビュー基準、行動規範 | +| **Instruction** | *何を*すべきか? | ステップ固有の手順、変数付きテンプレート | +| **Knowledge** | *何を*知っているか? | アーキテクチャ文書、API仕様、例示、参照資料 | +| **Report Format** | *何を*出力すべきか? | 出力構造、レポートテンプレート | + +各関心はそれぞれのディレクトリに独立したファイル(Markdownまたはテンプレート)として格納される。 + +``` +workflows/ # ワークフロー定義 +personas/ # WHO — 役割定義 +stances/ # HOW — 行動規範 +instructions/ # WHAT — ステップ手順 +knowledge/ # CONTEXT — ドメイン情報 +report-formats/ # OUTPUT — レポートテンプレート +``` + +### なぜこの5つか? + +**Persona** と **Instruction** は最低限必要なもの — エージェントが誰で、何をすべきかを定義する必要がある。しかし実際には、さらに3つの関心が独立した軸として現れる。 + +- **Stance** はタスクをまたがって適用される行動規範を捉える。「コーディングスタンス」(命名規則、エラーハンドリングのルール、テスト要件)は、機能実装でもバグ修正でも同じように適用される。スタンスは「横断的関心事」であり、作業内容に関係なく作業の*やり方*を規定する。 + +- **Knowledge** は複数のエージェントが必要とするドメイン知識を捉える。アーキテクチャ文書はプランナーにもレビュアーにも関係がある。ナレッジをインストラクションから分離することで重複を防ぎ、インストラクションを手順に集中させる。 + +- **Report Format** は作業そのものとは独立した出力構造を捉える。同じレビューフォーマットをアーキテクチャレビュアーとセキュリティレビュアーの両方で使える。出力形式の変更がエージェントの振る舞いに影響しない。 + +## 宣言的な合成 + +Faceted Promptingの中核メカニズムは**宣言的な合成**である。ワークフロー定義が各ステップでプロンプトの内容を直接埋め込むのではなく、*どの*関心を組み合わせるかを宣言する。 + +主要な特性は次の通り。 + +- **各ファイルは1つの関心だけを持つ。** ペルソナファイルには役割と専門性のみを記述し、ステップ固有の手順は書かない。 +- **合成は宣言的。** ワークフローは*どの*関心を組み合わせるかを記述し、プロンプトを*どう*組み立てるかは記述しない。 +- **自由に組み合わせ可能。** 同じ `coder` ペルソナを異なるスタンスとインストラクションで異なるステップに使える。 +- **ファイルが再利用の単位。** 同じファイルを指すことでスタンスをワークフロー間で共有する。 + +### TAKTでの実装例 + +[TAKT](https://github.com/nrslib/takt) はFaceted PromptingをYAMLベースのワークフロー定義(「ピース」と呼ぶ)で実装している。各関心はセクションマップで短いキーにマッピングされ、各ステップ(TAKTでは「ムーブメント」と呼ぶ)からキーで参照される。 + +```yaml +name: my-workflow +max_iterations: 10 +initial_movement: plan + +# セクションマップ — キー: ファイルパス(このYAMLからの相対パス) +personas: + coder: ../personas/coder.md + reviewer: ../personas/architecture-reviewer.md + +stances: + coding: ../stances/coding.md + review: ../stances/review.md + +instructions: + plan: ../instructions/plan.md + implement: ../instructions/implement.md + +knowledge: + architecture: ../knowledge/architecture.md + +report_formats: + review: ../report-formats/review.md + +movements: + - name: implement + persona: coder # WHO — personas.coder を参照 + stance: coding # HOW — stances.coding を参照 + instruction: implement # WHAT — instructions.implement を参照 + knowledge: architecture # CONTEXT — knowledge.architecture を参照 + edit: true + rules: + - condition: Implementation complete + next: review + + - name: review + persona: reviewer # 異なる WHO + stance: review # 異なる HOW + instruction: review # 異なる WHAT(共有も可能) + knowledge: architecture # 同じ CONTEXT — 再利用 + report: + name: review.md + format: review # OUTPUT — report_formats.review を参照 + edit: false + rules: + - condition: Approved + next: COMPLETE + - condition: Needs fix + next: implement +``` + +エンジンは各キーをファイルに解決し、内容を読み込み、実行時に最終的なプロンプトを組み立てる。ワークフローの作者がモノリシックなプロンプトを書くことはない — どのファセットを組み合わせるかを選択するだけである。 + +## 既存手法との違い + +| 手法 | 内容 | 本手法との違い | +|------|------|--------------| +| **Decomposed Prompting** (Khot et al.) | *タスク*をサブタスクに分解して異なるLLMに委任 | 分解するのはタスクではなく*プロンプトの構造* | +| **Modular Prompting** | XML/HTMLタグを使った単一プロンプト内のセクション分け | 関心を*独立ファイル*に分離し、宣言的に合成する | +| **Prompt Layering** (Airia) | エンタープライズ向けのスタック可能なプロンプトセグメント | 管理ツールであり、プロンプト設計のデザインパターンではない | +| **PDL** (IBM) | データパイプライン向けのYAMLベースプロンプトプログラミング言語 | 制御フロー(if/for/model呼び出し)が焦点で、関心の分離ではない | +| **Role/Persona Prompting** | 役割を割り当ててレスポンスを方向付ける | ペルソナは5つの関心の1つにすぎない — スタンス、インストラクション、ナレッジ、出力形式も分離する | + +核心的な違いは次の点にある。既存手法は*タスク*(何をするか)を分解するか、*プロンプトの構造*(どう書式化するか)を整理する。Faceted Promptingは*プロンプトの関心*(各部分がなぜ存在するか)を独立した再利用可能な単位に分解する。 + +## 実用上の利点 + +**ワークフロー作者にとって:** +- コーディング規約を1つのスタンスファイルで変更すれば、それを使うすべてのワークフローに反映される +- 既存のペルソナ、スタンス、インストラクションを組み合わせて新しいワークフローを作れる +- 各ファイルを単一の責務に集中させられる + +**チームにとって:** +- プロンプトを複製せずにプロジェクト間で振る舞い(スタンス)を標準化できる +- ドメイン専門家がナレッジファイルを管理し、ワークフロー設計者がインストラクションを管理する分業ができる +- 個々の関心を独立してレビューできる + +**エンジンにとって:** +- プロンプト組み立ては決定的 — 同じワークフロー定義とファイルからは同じプロンプトが構築される +- スタンスの配置を最適化できる(例: 「Lost in the Middle」効果に対抗するためプロンプトの先頭と末尾に配置) +- 各関心を他の部分に影響を与えずにステップごとに注入・省略・上書きできる + +## まとめ + +Faceted Promptingは、関心の分離(Separation of Concerns)をAIプロンプト工学に適用するデザインパターンである。プロンプトを5つの独立した関心 — Persona、Stance、Instruction、Knowledge、Report Format — に分解し、宣言的に合成することで、再利用可能で保守しやすく透明なマルチエージェントワークフローを実現する。 diff --git a/docs/prompt-composition.md b/docs/prompt-composition.md new file mode 100644 index 0000000..ee30ac3 --- /dev/null +++ b/docs/prompt-composition.md @@ -0,0 +1,149 @@ +# Faceted Prompting: Separation of Concerns for AI Prompts + +## The Problem + +As multi-agent systems grow complex, prompts become monolithic. A single prompt file contains the agent's role, behavioral rules, task-specific instructions, domain knowledge, and output format — all tangled together. This creates three problems: + +1. **No reuse** — When two steps need the same reviewer persona but different instructions, you duplicate the entire prompt +2. **Hidden coupling** — Changing a coding standard means editing every prompt that references it +3. **Unclear ownership** — It's impossible to tell which part of a prompt defines *who* the agent is versus *what* it should do + +## The Idea + +Apply **Separation of Concerns** — a foundational software engineering principle — to prompt design. + +Instead of one monolithic prompt per agent, decompose it into independent, reusable files organized by *what concern they address*. Then compose them declaratively per workflow step. + +## Five Concerns + +Faceted Prompting decomposes prompts into five orthogonal concerns: + +| Concern | Question it answers | Example | +|---------|-------------------|---------| +| **Persona** | *Who* is the agent? | Role definition, expertise, judgment criteria | +| **Stance** | *How* should it behave? | Coding standards, review criteria, behavioral rules | +| **Instruction** | *What* should it do? | Step-specific procedures, templates with variables | +| **Knowledge** | *What* does it know? | Architecture docs, API specs, examples, references | +| **Report Format** | *What* should it output? | Output structure, report templates | + +Each concern is a standalone file (Markdown or template) stored in its own directory: + +``` +workflows/ # Workflow definitions +personas/ # WHO — role definitions +stances/ # HOW — behavioral rules +instructions/ # WHAT — step procedures +knowledge/ # CONTEXT — domain information +report-formats/ # OUTPUT — report templates +``` + +### Why These Five? + +**Persona** and **Instruction** are the minimum — you need to define who the agent is and what it should do. But in practice, three more concerns emerge as independent axes: + +- **Stance** captures behavioral rules that apply across different tasks. A "coding stance" (naming conventions, error handling rules, test requirements) applies whether the agent is implementing a feature or fixing a bug. Stances are *cross-cutting concerns* — they modify how work is done regardless of what the work is. + +- **Knowledge** captures domain context that multiple agents may need. An architecture document is relevant to both the planner and the reviewer. Separating knowledge from instructions prevents duplication and keeps instructions focused on procedures. + +- **Report Format** captures output structure independently of the work itself. The same review format can be used by an architecture reviewer and a security reviewer. Separating it allows format changes without touching agent behavior. + +## Declarative Composition + +The core mechanism of Faceted Prompting is **declarative composition**: a workflow definition declares *which* concerns to combine for each step, rather than embedding prompt content directly. + +Key properties: + +- **Each file has one concern.** A persona file contains only role and expertise — never step-specific procedures. +- **Composition is declarative.** The workflow says *which* concerns to combine, not *how* to assemble the prompt. +- **Mix and match.** The same `coder` persona can appear with different stances and instructions in different steps. +- **Files are the unit of reuse.** Share a stance across workflows by pointing to the same file. + +### Implementation Example: TAKT + +[TAKT](https://github.com/nrslib/takt) implements Faceted Prompting using YAML-based workflow definitions called "pieces." Concerns are mapped to short keys via section maps, then referenced by key in each step (called "movement" in TAKT): + +```yaml +name: my-workflow +max_iterations: 10 +initial_movement: plan + +# Section maps — key: file path (relative to this YAML) +personas: + coder: ../personas/coder.md + reviewer: ../personas/architecture-reviewer.md + +stances: + coding: ../stances/coding.md + review: ../stances/review.md + +instructions: + plan: ../instructions/plan.md + implement: ../instructions/implement.md + +knowledge: + architecture: ../knowledge/architecture.md + +report_formats: + review: ../report-formats/review.md + +movements: + - name: implement + persona: coder # WHO — references personas.coder + stance: coding # HOW — references stances.coding + instruction: implement # WHAT — references instructions.implement + knowledge: architecture # CONTEXT — references knowledge.architecture + edit: true + rules: + - condition: Implementation complete + next: review + + - name: review + persona: reviewer # Different WHO + stance: review # Different HOW + instruction: review # Different WHAT (but could share) + knowledge: architecture # Same CONTEXT — reused + report: + name: review.md + format: review # OUTPUT — references report_formats.review + edit: false + rules: + - condition: Approved + next: COMPLETE + - condition: Needs fix + next: implement +``` + +The engine resolves each key to its file, reads the content, and assembles the final prompt at runtime. The workflow author never writes a monolithic prompt — only selects which facets to combine. + +## How It Differs from Existing Approaches + +| Approach | What it does | How this differs | +|----------|-------------|-----------------| +| **Decomposed Prompting** (Khot et al.) | Breaks *tasks* into sub-tasks delegated to different LLMs | We decompose the *prompt structure*, not the task | +| **Modular Prompting** | Sections within a single prompt using XML/HTML tags | We separate concerns into *independent files* with declarative composition | +| **Prompt Layering** (Airia) | Stackable prompt segments for enterprise management | A management tool, not a design pattern for prompt architecture | +| **PDL** (IBM) | YAML-based prompt programming language for data pipelines | Focuses on control flow (if/for/model calls), not concern separation | +| **Role/Persona Prompting** | Assigns a role to shape responses | Persona is one of five concerns — we also separate stance, instruction, knowledge, and output format | + +The key distinction: existing approaches either decompose *tasks* (what to do) or *structure prompts* (how to format). Faceted Prompting decomposes *prompt concerns* (why each part exists) into independent, reusable units. + +## Practical Benefits + +**For workflow authors:** +- Change a coding standard in one stance file; every workflow using it gets the update +- Create a new workflow by combining existing personas, stances, and instructions +- Focus each file on a single responsibility + +**For teams:** +- Standardize behavior (stances) across projects without duplicating prompts +- Domain experts maintain knowledge files; workflow designers maintain instructions +- Review individual concerns independently + +**For the engine:** +- Prompt assembly is deterministic — given the same workflow definition and files, the same prompt is built +- Stance placement can be optimized (e.g., placed at top and bottom to counter the "Lost in the Middle" effect) +- Concerns can be injected, omitted, or overridden per step without touching other parts + +## Summary + +Faceted Prompting is a design pattern that applies Separation of Concerns to AI prompt engineering. By decomposing prompts into five independent concerns — Persona, Stance, Instruction, Knowledge, and Report Format — and composing them declaratively, it enables reusable, maintainable, and transparent multi-agent workflows. diff --git a/src/features/config/deploySkill.ts b/src/features/config/deploySkill.ts index cb3aa6a..a6eb9f4 100644 --- a/src/features/config/deploySkill.ts +++ b/src/features/config/deploySkill.ts @@ -1,17 +1,16 @@ /** * takt export-cc — Deploy takt skill files to Claude Code. * - * Copies the following to ~/.claude/: - * commands/takt.md — /takt command entry point - * skills/takt/SKILL.md — Engine overview - * skills/takt/references/ — Engine logic + YAML schema - * skills/takt/pieces/ — Builtin piece YAML files - * skills/takt/personas/ — Builtin persona .md files - * skills/takt/stances/ — Builtin stance files - * skills/takt/instructions/ — Builtin instruction files - * skills/takt/knowledge/ — Builtin knowledge files - * skills/takt/report-formats/ — Builtin report format files - * skills/takt/templates/ — Builtin template files + * Copies the following to ~/.claude/skills/takt/: + * SKILL.md — Engine overview (user-invocable as /takt) + * references/ — Engine logic + YAML schema + * pieces/ — Builtin piece YAML files + * personas/ — Builtin persona .md files + * stances/ — Builtin stance files + * instructions/ — Builtin instruction files + * knowledge/ — Builtin knowledge files + * report-formats/ — Builtin report format files + * templates/ — Builtin template files * * Piece YAML persona paths (../personas/...) work as-is because * the directory structure is mirrored. @@ -34,10 +33,6 @@ function getSkillDir(): string { return join(homedir(), '.claude', 'skills', 'takt'); } -function getCommandDir(): string { - return join(homedir(), '.claude', 'commands'); -} - /** Directories within builtins/{lang}/ to copy as resource types */ const RESOURCE_DIRS = [ 'pieces', @@ -59,7 +54,6 @@ export async function deploySkill(): Promise { const skillResourcesDir = join(getResourcesDir(), 'skill'); const langResourcesDir = getLanguageResourcesDir(lang); const skillDir = getSkillDir(); - const commandDir = getCommandDir(); // Verify source directories exist if (!existsSync(skillResourcesDir)) { @@ -84,23 +78,18 @@ export async function deploySkill(): Promise { const copiedFiles: string[] = []; - // 1. Deploy command file: ~/.claude/commands/takt.md - const commandSrc = join(skillResourcesDir, 'takt-command.md'); - const commandDest = join(commandDir, 'takt.md'); - copyFile(commandSrc, commandDest, copiedFiles); - - // 2. Deploy SKILL.md + // 1. Deploy SKILL.md const skillSrc = join(skillResourcesDir, 'SKILL.md'); const skillDest = join(skillDir, 'SKILL.md'); copyFile(skillSrc, skillDest, copiedFiles); - // 3. Deploy references/ (engine.md, yaml-schema.md) + // 2. Deploy references/ (engine.md, yaml-schema.md) const refsSrcDir = join(skillResourcesDir, 'references'); const refsDestDir = join(skillDir, 'references'); cleanDir(refsDestDir); copyDirRecursive(refsSrcDir, refsDestDir, copiedFiles); - // 4. Deploy all resource directories from builtins/{lang}/ + // 3. Deploy all resource directories from builtins/{lang}/ for (const resourceDir of RESOURCE_DIRS) { const srcDir = join(langResourcesDir, resourceDir); const destDir = join(skillDir, resourceDir); @@ -116,7 +105,6 @@ export async function deploySkill(): Promise { // Show summary by category const skillBase = join(homedir(), '.claude'); - const commandFiles = copiedFiles.filter((f) => f.startsWith(commandDir)); const skillFiles = copiedFiles.filter( (f) => f.startsWith(skillDir) && @@ -130,12 +118,6 @@ export async function deploySkill(): Promise { const reportFormatFiles = copiedFiles.filter((f) => f.includes('/report-formats/')); const templateFiles = copiedFiles.filter((f) => f.includes('/templates/')); - if (commandFiles.length > 0) { - info(` コマンド: ${commandFiles.length} ファイル`); - for (const f of commandFiles) { - info(` ${relative(skillBase, f)}`); - } - } if (skillFiles.length > 0) { info(` スキル: ${skillFiles.length} ファイル`); for (const f of skillFiles) { diff --git a/tools/generate-hybrid-codex.mjs b/tools/generate-hybrid-codex.mjs new file mode 100644 index 0000000..326f612 --- /dev/null +++ b/tools/generate-hybrid-codex.mjs @@ -0,0 +1,260 @@ +#!/usr/bin/env node +/** + * Generate hybrid-codex piece variants from standard pieces. + * + * For each standard piece (not already -hybrid-codex, not in skip list): + * 1. Parse the YAML + * 2. Add `provider: codex` to all coder movements (including parallel sub-movements) + * 3. Change name to {name}-hybrid-codex + * 4. Write the hybrid-codex YAML file + * 5. Update piece-categories.yaml to include generated hybrids + * + * Usage: + * node tools/generate-hybrid-codex.mjs # Generate all + * node tools/generate-hybrid-codex.mjs --dry-run # Preview only + */ + +import { readFileSync, writeFileSync, readdirSync } from 'node:fs'; +import { join, basename, dirname } from 'node:path'; +import { parse, stringify } from 'yaml'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT = join(__dirname, '..'); +const BUILTINS = join(ROOT, 'builtins'); +const LANGUAGES = ['en', 'ja']; + +/** Pieces that should NOT get hybrid variants (no coder involvement or special purpose) */ +const SKIP_PIECES = new Set(['magi', 'research', 'review-only']); + +const CODER_PERSONA = 'coder'; +const dryRun = process.argv.includes('--dry-run'); + +// ───────────────────────────────────────── +// Movement transformation +// ───────────────────────────────────────── + +function hasCoderPersona(movement) { + if (movement.persona === CODER_PERSONA) return true; + if (movement.parallel) return movement.parallel.some(sub => sub.persona === CODER_PERSONA); + return false; +} + +/** + * Insert a field into an object after specified anchor fields (preserves key order). + * If anchor not found, appends at end. + */ +function insertFieldAfter(obj, key, value, anchorFields) { + if (obj[key] === value) return obj; + const result = {}; + let inserted = false; + for (const [k, v] of Object.entries(obj)) { + if (k === key) continue; // Remove existing (will re-insert) + result[k] = v; + if (!inserted && anchorFields.includes(k)) { + result[key] = value; + inserted = true; + } + } + if (!inserted) result[key] = value; + return result; +} + +/** + * Add `provider: codex` to all coder movements (recursively handles parallel). + */ +function addCodexToCoders(movements) { + return movements.map(m => { + if (m.parallel) { + return { ...m, parallel: addCodexToCoders(m.parallel) }; + } + if (m.persona === CODER_PERSONA) { + return insertFieldAfter(m, 'provider', 'codex', ['knowledge', 'stance', 'persona']); + } + return m; + }); +} + +// ───────────────────────────────────────── +// Hybrid piece builder +// ───────────────────────────────────────── + +/** Top-level field order for readable output */ +const TOP_FIELD_ORDER = [ + 'name', 'description', 'max_iterations', + 'stances', 'knowledge', 'personas', 'instructions', 'report_formats', + 'initial_movement', 'loop_monitors', 'answer_agent', 'movements', +]; + +function buildHybrid(parsed) { + const hybrid = {}; + for (const field of TOP_FIELD_ORDER) { + if (field === 'name') { + hybrid.name = `${parsed.name}-hybrid-codex`; + } else if (field === 'movements') { + hybrid.movements = addCodexToCoders(parsed.movements); + } else if (parsed[field] != null) { + hybrid[field] = parsed[field]; + } + } + // Carry over any extra top-level fields not in the order list + for (const key of Object.keys(parsed)) { + if (!(key in hybrid) && key !== 'name') { + hybrid[key] = parsed[key]; + } + } + return hybrid; +} + +function generateHeader(sourceFile) { + return [ + `# Auto-generated from ${sourceFile} by tools/generate-hybrid-codex.mjs`, + '# Do not edit manually. Edit the source piece and re-run the generator.', + '', + '', + ].join('\n'); +} + +// ───────────────────────────────────────── +// Category handling +// ───────────────────────────────────────── + +/** Recursively collect all piece names from a category tree */ +function collectPieces(obj) { + const pieces = []; + if (!obj || typeof obj !== 'object') return pieces; + if (Array.isArray(obj.pieces)) pieces.push(...obj.pieces); + for (const [key, val] of Object.entries(obj)) { + if (key === 'pieces') continue; + if (typeof val === 'object' && val !== null && !Array.isArray(val)) { + pieces.push(...collectPieces(val)); + } + } + return pieces; +} + +/** Find the key for the hybrid top-level category */ +function findHybridTopKey(categories) { + for (const key of Object.keys(categories)) { + if (key.includes('Hybrid') || key.includes('ハイブリッド')) return key; + } + return null; +} + +/** + * Build mapping: standard piece name → top-level category key. + * Excludes the hybrid category and "Others" category. + */ +function getTopLevelMapping(categories, hybridKey, othersKey) { + const map = new Map(); + for (const [key, val] of Object.entries(categories)) { + if (key === hybridKey) continue; + if (othersKey && key === othersKey) continue; + if (typeof val !== 'object' || val === null) continue; + const pieces = collectPieces(val); + for (const p of pieces) map.set(p, key); + } + return map; +} + +/** + * Build the hybrid category section by mirroring standard categories. + */ +function buildHybridCategories(generatedNames, topMap) { + // Group hybrids by their source piece's top-level category + const grouped = new Map(); + for (const hybridName of generatedNames) { + const sourceName = hybridName.replace('-hybrid-codex', ''); + const topCat = topMap.get(sourceName); + if (!topCat) continue; + if (!grouped.has(topCat)) grouped.set(topCat, []); + grouped.get(topCat).push(hybridName); + } + + const section = {}; + for (const [catKey, hybrids] of grouped) { + section[catKey] = { pieces: hybrids.sort() }; + } + return section; +} + +// ───────────────────────────────────────── +// Main +// ───────────────────────────────────────── + +console.log('=== Generating hybrid-codex pieces ===\n'); + +for (const lang of LANGUAGES) { + console.log(`[${lang}]`); + const generatedNames = []; + + const piecesDir = join(BUILTINS, lang, 'pieces'); + const files = readdirSync(piecesDir) + .filter(f => f.endsWith('.yaml') && !f.includes('-hybrid-codex')) + .sort(); + + for (const file of files) { + const name = basename(file, '.yaml'); + if (SKIP_PIECES.has(name)) { + console.log(` Skip: ${name} (in skip list)`); + continue; + } + + const content = readFileSync(join(piecesDir, file), 'utf-8'); + const parsed = parse(content); + + if (!parsed.movements?.some(hasCoderPersona)) { + console.log(` Skip: ${name} (no coder movements)`); + continue; + } + + const hybrid = buildHybrid(parsed); + const header = generateHeader(file); + const yamlOutput = stringify(hybrid, { lineWidth: 120, indent: 2 }); + const outputPath = join(piecesDir, `${name}-hybrid-codex.yaml`); + + if (dryRun) { + console.log(` Would generate: ${name}-hybrid-codex.yaml`); + } else { + writeFileSync(outputPath, header + yamlOutput, 'utf-8'); + console.log(` Generated: ${name}-hybrid-codex.yaml`); + } + + generatedNames.push(`${name}-hybrid-codex`); + } + + // ─── Update piece-categories.yaml ─── + const catPath = join(BUILTINS, lang, 'piece-categories.yaml'); + const catRaw = readFileSync(catPath, 'utf-8'); + const catParsed = parse(catRaw); + const cats = catParsed.piece_categories; + + if (cats) { + const hybridKey = findHybridTopKey(cats); + const othersKey = Object.keys(cats).find(k => + k === 'Others' || k === 'その他' + ); + + if (hybridKey) { + const topMap = getTopLevelMapping(cats, hybridKey, othersKey); + const newSection = buildHybridCategories(generatedNames, topMap); + cats[hybridKey] = newSection; + + if (dryRun) { + console.log(` Would update: piece-categories.yaml`); + console.log(` Hybrid pieces: ${generatedNames.join(', ')}`); + } else { + const catOut = stringify(catParsed, { lineWidth: 120, indent: 2 }); + writeFileSync(catPath, catOut, 'utf-8'); + console.log(` Updated: piece-categories.yaml`); + } + } else { + console.log(` Warning: No hybrid category found in piece-categories.yaml`); + } + } + + console.log(); +} + +console.log('Done!'); +if (dryRun) console.log('(dry-run mode, no files were written)');