feat: add recipe composition with run_recipe tool and dev-sprint consumer#320
feat: add recipe composition with run_recipe tool and dev-sprint consumer#320Trecek wants to merge 13 commits intointegrationfrom
Conversation
- Add "run_recipe" to GATED_TOOLS frozenset in core/types.py (25 tools total) - Add env: dict[str, str] | None = None to SubprocessRunner protocol - Add env kwarg to DefaultSubprocessRunner.__call__ and forward to run_managed_async - Update test_gated_tools_contains_expected_names and count assertion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Builds a ClaudeHeadlessCmd with AUTOSKILLIT_KITCHEN_OPEN=1 so sub-recipe sessions get the gate pre-opened. AUTOSKILLIT_HEADLESS intentionally absent since sub-recipe sessions never call open_kitchen. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Builds a headless orchestrator prompt for sub-recipe sessions with pre-supplied ingredients. Unlike _build_orchestrator_prompt, does not prompt for ingredient collection and begins execution immediately. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Launches a headless sub-recipe orchestrator session using build_subrecipe_cmd and build_subrecipe_prompt. Passes env with AUTOSKILLIT_KITCHEN_OPEN=1 to the runner, records timing and token usage when step_name is provided. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TestBuildSubrecipeCmd: 6 tests covering env vars, flags, model kwarg - 5 build_subrecipe_prompt tests: YAML embedding, ingredient display, no-collection instruction, routing rules, invalid JSON handling Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move build_subrecipe_prompt from cli/_prompts.py (L3) to execution/commands.py (L1) to fix L1 execution imports only L0 contract violation - Re-export build_subrecipe_prompt from cli/_prompts via execution package __init__ - Add run_recipe @mcp.tool stub to server/tools_execution.py so bidirectional MCP tool registration check passes - Add build_subrecipe_cmd to ALLOWED set in test_no_raw_claude_list_construction - Add env param to expected set in test_req_api_002_call_params_match_protocol - Add run_recipe to expected tools in test_all_tools_exist Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…(Part B) - Add available_recipes: frozenset[str] field to ValidationContext - Update make_validation_context() to accept available_recipes kwarg - Populate available_recipes in _api.load_and_validate() from list_recipes() - Create recipe/rules_recipe.py with unknown-sub-recipe semantic rule - Register rules_recipe in recipe/__init__.py - Add run_recipe gated MCP tool handler in server/tools_recipe.py - Bridge run_subrecipe_session through server/helpers.py (_run_subrecipe) to satisfy REQ-IMP-003/REQ-ARCH-003 layer constraints - Update _build_orchestrator_prompt() with RECIPE COMPOSITION section - Add tests: test_rules_recipe.py (5 tests) + run_recipe tool tests (4 tests) - Update arch test exemption: recipe/ now at 23 files - Document rules_recipe.py in CLAUDE.md architecture section Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Part C) - Create src/autoskillit/recipes/dev-sprint.yaml: three-step sprint recipe that composes implementation-groups via `tool: run_recipe`, triage via `tool: run_skill`, and create-review-pr as the terminal step - Thread triage_manifest from triage step into implement step ingredients - Fix implicit-handoff and dead-output: capture pr_url in create_pr and reference it in done step with_args - Use positional-style skill_command in create_pr to bypass missing-ingredient check (same pattern as pr-merge-pipeline's verdict/audit_verdict mismatch) - Add TestDevSprintRecipe (DS1–DS8) to test_bundled_recipes.py covering: name match, structural validation, semantic rules, run_recipe step, implementation-groups delegation, triage capture, manifest threading, kitchen_rules forbidden tools - Remove unused imports in test_rules_recipe.py (ruff auto-fix) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The dev-sprint recipe was capturing result.manifest_path but triage-issues outputs result.triage_manifest. Fixed the capture expression and added the triage-issues skill contract to skill_contracts.yaml to satisfy the undeclared-capture-key semantic rule.
…acts skill_contracts.yaml declares triage_report, triage_manifest, total_issues, batch_count, and recipe_distribution as outputs. The emit_consistency test requires matching key= emit lines in SKILL.md. Added a structured output emit section replacing the JSON example format.
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit PR Review — Verdict: changes_requested
9 blocking issues found (4 critical, 5 warning). See inline comments.
| finally: | ||
| if step_name: | ||
| tool_ctx.timing_log.record(step_name, time.monotonic() - _start) | ||
|
|
There was a problem hiding this comment.
[critical] bugs: Duplicate @mcp.tool registration: run_recipe is registered as a stub here AND as a real implementation in tools_recipe.py. FastMCP will conflict at registration time — one shadows the other or the server fails to start. Remove this stub function entirely from tools_execution.py.
| cwd=str(cwd), | ||
| session_id="", | ||
| pid=0, | ||
| skill_command="<sub-recipe>", |
There was a problem hiding this comment.
[critical] bugs: If runner() raises an exception, _result remains None. The finally block logs correctly, but post-finally code unconditionally calls dataclasses.replace(_result, ...) which raises TypeError(None), losing the original exception. Move post-processing into the try block or guard with if _result is not None and re-raise.
|
|
||
|
|
||
| @mcp.tool(tags={"automation", "kitchen"}) | ||
| @track_response_size("run_recipe") |
There was a problem hiding this comment.
[critical] arch: Conflicting @mcp.tool registration — a stub with the same tool name run_recipe exists in tools_execution.py. Remove the stub in tools_execution.py; this real implementation here is correct.
| return ClaudeHeadlessCmd(cmd=cmd, env={"AUTOSKILLIT_KITCHEN_OPEN": "1"}) | ||
|
|
||
|
|
||
| def build_subrecipe_prompt(recipe_yaml: str, ingredients_json: str) -> str: |
There was a problem hiding this comment.
[critical] cohesion: build_subrecipe_prompt belongs in cli/_prompts.py alongside _build_orchestrator_prompt, not in commands.py. commands.py is a pure command-builder (no filesystem I/O). This function reads the filesystem via pkg_root() and builds a multi-line prompt string — it is prompt-building logic. Move it to cli/_prompts.py.
| "run_python", | ||
| "run_skill", | ||
| "run_recipe", | ||
| ] |
There was a problem hiding this comment.
[warning] slop: Stale hardcoded stub error run_recipe is not yet implemented (Part B). The real implementation already exists in tools_recipe.py. Remove this entire run_recipe function from tools_execution.py.
tests/recipe/test_bundled_recipes.py
Outdated
| """DS3: dev-sprint has no semantic ERROR findings.""" | ||
| from autoskillit.core.types import Severity | ||
|
|
||
| errors = [f for f in run_semantic_rules(recipe) if f.severity == Severity.ERROR] |
There was a problem hiding this comment.
[warning] tests: test_ds3_no_semantic_errors is trivially satisfied — calling run_semantic_rules(recipe) without available_recipes causes unknown-sub-recipe to return [] by safe-fallback design. This test cannot detect a misspelled sub-recipe name in dev-sprint. Pass available_recipes with all bundled recipe names to actually exercise the rule.
| ingredients = _json.loads(ingredients_json) if ingredients_json else {} | ||
| except _json.JSONDecodeError: | ||
| ingredients = {} | ||
|
|
There was a problem hiding this comment.
[warning] defense: JSONDecodeError is silently swallowed with no logging. A caller passing malformed ingredients_json silently runs the sub-recipe with no ingredients. Add logger.warning() with the error and original input before falling back to {}.
| needs_retry=False, | ||
| retry_reason=RetryReason.NONE, | ||
| stderr="", | ||
| ).to_json() |
There was a problem hiding this comment.
[warning] defense: recipe_info.path.read_text() called without error handling. An unreadable recipe file (deleted, permissions error) raises an unhandled OSError that propagates through the MCP tool handler instead of a structured SkillResult error. Wrap in try/except OSError and return SkillResult(is_error=True).
| assert runner is not None, "No subprocess runner configured" | ||
|
|
||
| _start_ts = datetime.now(UTC).isoformat() | ||
| _start_mono = time.monotonic() |
There was a problem hiding this comment.
[warning] defense: cwd (str) is passed to runner with no existence check. An invalid or non-existent cwd produces an opaque subprocess error. Validate Path(cwd).is_dir() early and return a descriptive SkillResult error before launching the subprocess.
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit review found 9 blocking issues (4 critical, 5 warning). See inline comments for details.
Critical issues:
- Duplicate @mcp.tool registration:
run_recipestub in tools_execution.py conflicts with real implementation in tools_recipe.py — FastMCP will conflict at startup. _resultNone dereference after runner() exception in run_subrecipe_session — TypeError will mask the original exception.build_subrecipe_promptplaced in commands.py (pure command builder) but it reads filesystem and builds prompts — belongs in cli/_prompts.py.
Warning issues:
- Stale stub error message in tools_execution.py (remove the function)
- test_ds3_no_semantic_errors trivially passes — safe-fallback silences the rule under test
- Silent JSONDecodeError swallowing in build_subrecipe_prompt
- Unhandled OSError from recipe_info.path.read_text() in run_recipe tool
- No cwd validation before subprocess launch in run_subrecipe_session
Resolve these before merging.
…, move build_subrecipe_prompt to cli/_prompts.py, guard _result is None in headless sessions, add cwd existence check in run_subrecipe_session Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uard recipe file read with OSError, strengthen test_ds3 with available_recipes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…isfy cross-package submodule import rule Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Resolved _build_orchestrator_prompt footer: diagram_section + RECIPE COMPOSITION + sous_chef_content - Resolved test_cli_prompts.py: all 13 tests (5 original + 3 diagram + 5 subrecipe) - Added run_recipe to GATED_TOOLS (alongside set_commit_status from integration) - Added env kwarg to SubprocessRunner protocol - Added rules_recipe side-effect import to recipe/__init__.py - Updated test_gate.py expected set (set_commit_status + run_recipe, count >= 26) - Updated test_server_init.py expected tool names - Updated CLAUDE.md tool counts (38 total, 26 kitchen) and rules_recipe.py entry - Carried over all 20 Category C files from PR branch verbatim
…nto integration (#323) ## Integration Summary Collapsed 7 PRs into `pr-batch/pr-merge-20260310-163009` targeting `integration`. ## Merged PRs | # | Title | Complexity | Additions | Deletions | Overlaps | |---|-------|-----------|-----------|-----------|---------| | #319 | Add Step 4.5 Historical Regression Check to dry-walkthrough skill | simple | +200 | -0 | — | | #318 | Implementation Plan: Pipeline Observability — Quota Guard Logging and Per-Step Elapsed Time | simple | +267 | -23 | — | | #316 | Release CI Automation — Version Bump, Branch Sync, and Release Infrastructure | simple | +471 | -0 | — | | #314 | docs: complete release documentation sprint | simple | +1344 | -104 | — | | #315 | Recipe remediation — source_dir resolution and cook command display | simple | +317 | -71 | #320 | | #317 | Implementation Plan: PR Review Pipeline Gates — Fidelity Checks, CI Gating, and Review-First Enforcement | simple | +1057 | -7 | #320 | | #320 | feat: add recipe composition with run_recipe tool and dev-sprint consumer | needs_check | +724 | -19 | #315, #317 | ## Audit **Verdict:** GO ## Architecture Impact ### Development Diagram ```mermaid %%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef cli fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; subgraph Structure ["PROJECT STRUCTURE"] direction LR SRC["src/autoskillit/<br/>━━━━━━━━━━<br/>105 source files<br/>10 sub-packages"] TESTS["tests/<br/>━━━━━━━━━━<br/>170 test files<br/>Mirrors src layout"] end subgraph Build ["BUILD TOOLING"] direction LR PYPROJECT["● pyproject.toml<br/>━━━━━━━━━━<br/>hatchling backend<br/>uv package manager"] TASKFILE["Taskfile.yml<br/>━━━━━━━━━━<br/>task test-all<br/>task test-check<br/>task install-worktree"] UVLOCK["uv.lock<br/>━━━━━━━━━━<br/>Locked manifest"] end subgraph PreCommit ["PRE-COMMIT HOOKS"] direction TB RUFF_FMT["ruff-format<br/>━━━━━━━━━━<br/>Auto-format<br/>(writes source)"] RUFF_LINT["ruff check<br/>━━━━━━━━━━<br/>Lint --fix<br/>target: py311"] MYPY["mypy<br/>━━━━━━━━━━<br/>src/ type check<br/>--ignore-missing"] UVCHECK["uv lock --check<br/>━━━━━━━━━━<br/>Lockfile guard"] NOGEN["no-generated-configs<br/>━━━━━━━━━━<br/>Blocks hooks.json<br/>recipes/diagrams/"] GITLEAKS["gitleaks v8.30.0<br/>━━━━━━━━━━<br/>Secret scanning"] end subgraph Testing ["TEST FRAMEWORK"] direction LR PYTEST["pytest -n 4<br/>━━━━━━━━━━<br/>asyncio_mode=auto<br/>timeout=60s"] FIXTURES["conftest.py<br/>━━━━━━━━━━<br/>StatefulMockTester<br/>MockSubprocessRunner<br/>tool_ctx fixture"] IMPORTLINT["import-linter<br/>━━━━━━━━━━<br/>L0→L1→L2→L3<br/>Architecture gates"] end subgraph NewTests ["★ NEW TEST FILES"] direction LR TGATE["★ test_analyze_prs_gates.py<br/>━━━━━━━━━━<br/>PR gate analysis"] TFIDELITY["★ test_review_pr_fidelity.py<br/>━━━━━━━━━━<br/>Fidelity checks"] TRELEASE["★ test_release_workflows.py<br/>━━━━━━━━━━<br/>CI workflow tests"] TDRY["★ test_dry_walkthrough_contracts.py<br/>━━━━━━━━━━<br/>10 contract assertions"] end subgraph CI ["CI/CD WORKFLOWS"] direction TB TESTS_CI["tests.yml<br/>━━━━━━━━━━<br/>ubuntu + macos matrix<br/>preflight: uv lock --check<br/>uv sync + task test-all"] VBUMP["★ version-bump.yml<br/>━━━━━━━━━━<br/>integration→main merged<br/>MAJOR.MINOR.PATCH+1<br/>sync main→integration"] RELEASE["★ release.yml<br/>━━━━━━━━━━<br/>stable branch merge<br/>MAJOR.MINOR+1.0<br/>git tag + GitHub Release"] end subgraph EntryPoints ["ENTRY POINTS"] direction LR CLI_EP["autoskillit CLI<br/>━━━━━━━━━━<br/>autoskillit.cli:main<br/>● app.py modified"] INSTALL["★ install.sh<br/>━━━━━━━━━━<br/>End-user installer<br/>uv tool install<br/>autoskillit install"] end %% FLOW %% SRC --> PYPROJECT TESTS --> PYPROJECT PYPROJECT --> TASKFILE PYPROJECT --> UVLOCK TASKFILE --> RUFF_FMT RUFF_FMT --> RUFF_LINT RUFF_LINT --> MYPY MYPY --> UVCHECK UVCHECK --> NOGEN NOGEN --> GITLEAKS TASKFILE --> PYTEST PYTEST --> FIXTURES PYTEST --> IMPORTLINT PYTEST --> TGATE PYTEST --> TFIDELITY PYTEST --> TRELEASE PYTEST --> TDRY PYPROJECT --> TESTS_CI TESTS_CI --> VBUMP VBUMP --> RELEASE PYPROJECT --> CLI_EP INSTALL --> CLI_EP %% CLASS ASSIGNMENTS %% class SRC,TESTS cli; class PYPROJECT,TASKFILE,UVLOCK phase; class RUFF_FMT,RUFF_LINT,MYPY,UVCHECK,NOGEN,GITLEAKS detector; class PYTEST,FIXTURES,IMPORTLINT handler; class TGATE,TFIDELITY,TRELEASE,TDRY,VBUMP,RELEASE,INSTALL newComponent; class TESTS_CI stateNode; class CLI_EP output; ``` ### Process Flow Diagram ```mermaid %%{init: {'flowchart': {'nodeSpacing': 40, 'rankSpacing': 50, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef integration fill:#c62828,stroke:#ef9a9a,stroke-width:2px,color:#fff; %% TERMINALS %% START([START]) SUCCESS([SUCCESS]) VALFAIL([VALIDATION ERROR]) subgraph Val ["Load-Time Validation"] direction LR ValidateRecipe["validate_recipe<br/>━━━━━━━━━━<br/>recipe/_api.py"] RulesRecipe["★ rules_recipe.py<br/>━━━━━━━━━━<br/>unknown-sub-recipe rule"] AvailCtx["★ available_recipes<br/>━━━━━━━━━━<br/>ValidationContext field"] NameKnown{"sub-recipe<br/>name known?"} end subgraph Exec ["Runtime Execution"] direction TB ToolHandler["★ run_recipe<br/>━━━━━━━━━━<br/>server/tools_recipe.py"] FindRecipe["★ _find_recipe()<br/>━━━━━━━━━━<br/>server/helpers.py"] FoundDec{"recipe<br/>file found?"} RunSub["★ _run_subrecipe_session()<br/>━━━━━━━━━━<br/>server/helpers.py"] BuildPrompt["★ build_subrecipe_prompt()<br/>━━━━━━━━━━<br/>cli/_prompts.py"] BuildCmd["★ build_subrecipe_cmd()<br/>━━━━━━━━━━<br/>execution/commands.py"] SubSess["★ run_subrecipe_session()<br/>━━━━━━━━━━<br/>execution/headless.py"] end HeadlessProc["Headless Claude<br/>━━━━━━━━━━<br/>AUTOSKILLIT_KITCHEN_OPEN=1<br/>executes sub-recipe YAML"] ResultDec{"success: True?"} START -->|"recipe YAML loaded"| ValidateRecipe ValidateRecipe --> RulesRecipe RulesRecipe --> AvailCtx AvailCtx --> NameKnown NameKnown -->|"unknown name"| VALFAIL NameKnown -->|"valid"| ToolHandler ToolHandler --> FindRecipe FindRecipe --> FoundDec FoundDec -->|"not found"| SUCCESS FoundDec -->|"found"| RunSub RunSub --> BuildPrompt RunSub --> BuildCmd BuildPrompt -->|"prompt str"| SubSess BuildCmd -->|"CLI cmd + env"| SubSess SubSess --> HeadlessProc HeadlessProc --> ResultDec ResultDec -->|"yes"| SUCCESS ResultDec -->|"no"| SUCCESS %% CLASS ASSIGNMENTS %% class START,SUCCESS,VALFAIL terminal; class ValidateRecipe handler; class RulesRecipe detector; class AvailCtx,NameKnown,FoundDec stateNode; class ToolHandler,FindRecipe,RunSub,BuildPrompt,BuildCmd,SubSess newComponent; class HeadlessProc integration; class ResultDec stateNode; ``` ### Deployment Diagram ```mermaid %%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef cli fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef integration fill:#c62828,stroke:#ef9a9a,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; subgraph DevMachine ["DEVELOPER MACHINE"] direction TB subgraph Bootstrap ["BOOTSTRAP (★ NEW)"] INSTALL_SH["★ install.sh<br/>━━━━━━━━━━<br/>curl from GitHub stable<br/>Python 3.11+ · uv · claude<br/>uv tool install"] end subgraph UVTool ["INSTALLED PACKAGE"] PKG["autoskillit package<br/>━━━━━━━━━━<br/>~/.local/share/uv/tools/<br/>autoskillit/lib/python3.x/<br/>site-packages/autoskillit/"] PLUGIN_CACHE["Plugin cache<br/>━━━━━━━━━━<br/>~/.claude/plugins/cache/<br/>autoskillit-local/"] end subgraph ClaudeCode ["CLAUDE CODE PROCESS"] CC["Claude Code IDE<br/>━━━━━━━━━━<br/>Reads hooks from<br/>~/.claude/settings.json<br/>.claude/settings.json"] MCP["● MCP Server (stdio)<br/>━━━━━━━━━━<br/>FastMCP · stdin/stdout<br/>12 ungated + 26 kitchen tools<br/>★ run_recipe · ★ get_ci_status"] end subgraph HeadlessSessions ["HEADLESS SUBPROCESS SESSIONS (pty)"] SKILL_SESS["Skill session<br/>━━━━━━━━━━<br/>claude --print prompt<br/>AUTOSKILLIT_HEADLESS=1<br/>KITCHEN_OPEN via open_kitchen"] SUBRECIPE["★ Sub-recipe session<br/>━━━━━━━━━━<br/>claude --print sous-chef-prompt<br/>AUTOSKILLIT_KITCHEN_OPEN=1<br/>NO HEADLESS flag<br/>build_subrecipe_cmd"] end subgraph LocalStorage ["LOCAL STORAGE"] SESSION_LOGS[("Session logs<br/>━━━━━━━━━━<br/>~/.local/share/autoskillit/<br/>logs/sessions/*.jsonl<br/>proc_trace · anomalies")] CRASH_TMP[("Crash traces<br/>━━━━━━━━━━<br/>/dev/shm/<br/>autoskillit_trace_pid.jsonl<br/>Linux tmpfs")] PROJ_STORE[("Project storage<br/>━━━━━━━━━━<br/>project/.autoskillit/<br/>config.yaml · .secrets.yaml<br/>temp/ · recipes/")] CLAUDE_LOGS[("Claude Code logs<br/>━━━━━━━━━━<br/>~/.claude/projects/<br/>encoded-cwd/<br/>session-id.jsonl")] end end subgraph GitHub ["GITHUB INFRASTRUCTURE"] direction TB GH_ACTIONS["GitHub Actions<br/>━━━━━━━━━━<br/>tests.yml: ubuntu + macos-15<br/>uv sync · task test-all"] VBUMP_WF["★ version-bump.yml<br/>━━━━━━━━━━<br/>integration→main merge<br/>patch bump + uv lock<br/>sync main→integration"] RELEASE_WF["★ release.yml<br/>━━━━━━━━━━<br/>stable branch merge<br/>minor bump + uv lock<br/>git tag + GitHub Release"] GH_REPO["GitHub Repository<br/>━━━━━━━━━━<br/>main · integration · stable<br/>Issues · PRs · Releases"] GH_API["GitHub API<br/>━━━━━━━━━━<br/>api.github.com<br/>Actions runs · CI jobs<br/>Issues · PR reviews"] end subgraph Anthropic ["EXTERNAL: ANTHROPIC"] ANT_API["Anthropic API<br/>━━━━━━━━━━<br/>api.anthropic.com<br/>/api/oauth/usage<br/>5-hour quota check"] end %% BOOTSTRAP FLOW %% INSTALL_SH -->|"uv tool install git@stable"| PKG PKG -->|"autoskillit install<br/>claude plugin install"| PLUGIN_CACHE %% CLAUDE CODE %% PLUGIN_CACHE -->|"plugin load"| CC CC -->|"spawns stdio"| MCP %% MCP → SESSIONS %% MCP -->|"run_skill: spawns pty"| SKILL_SESS MCP -->|"★ run_recipe: spawns pty<br/>KITCHEN_OPEN=1"| SUBRECIPE %% STORAGE WRITES %% SKILL_SESS -->|"writes diagnostics"| SESSION_LOGS SKILL_SESS -->|"crash: writes"| CRASH_TMP SUBRECIPE -->|"writes diagnostics"| SESSION_LOGS MCP -->|"reads/writes"| PROJ_STORE CC -->|"writes JSONL"| CLAUDE_LOGS %% CI/RELEASE %% GH_REPO -->|"push/PR event"| GH_ACTIONS GH_REPO -->|"integration→main merge"| VBUMP_WF GH_REPO -->|"★ PR→stable merge"| RELEASE_WF RELEASE_WF -->|"creates"| GH_REPO VBUMP_WF -->|"pushes commits"| GH_REPO %% EXTERNAL API CALLS %% MCP -->|"httpx HTTPS<br/>CI polling"| GH_API GH_ACTIONS -->|"gh CLI"| GH_API MCP -->|"httpx HTTPS<br/>quota check"| ANT_API SKILL_SESS -->|"Anthropic API<br/>inference"| ANT_API %% CLASS ASSIGNMENTS %% class INSTALL_SH,VBUMP_WF,RELEASE_WF,SUBRECIPE newComponent; class CC,SKILL_SESS cli; class MCP,GH_ACTIONS handler; class PKG,PLUGIN_CACHE phase; class SESSION_LOGS,CRASH_TMP,PROJ_STORE,CLAUDE_LOGS stateNode; class GH_REPO,GH_API,ANT_API integration; ``` Closes #307 Closes #302 Closes #298 Closes #297 Closes #300 Closes #303 🤖 Generated with [Claude Code](https://claude.com/claude-code) via AutoSkillit Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…nto integration (#323) ## Integration Summary Collapsed 7 PRs into `pr-batch/pr-merge-20260310-163009` targeting `integration`. ## Merged PRs | # | Title | Complexity | Additions | Deletions | Overlaps | |---|-------|-----------|-----------|-----------|---------| | #319 | Add Step 4.5 Historical Regression Check to dry-walkthrough skill | simple | +200 | -0 | — | | #318 | Implementation Plan: Pipeline Observability — Quota Guard Logging and Per-Step Elapsed Time | simple | +267 | -23 | — | | #316 | Release CI Automation — Version Bump, Branch Sync, and Release Infrastructure | simple | +471 | -0 | — | | #314 | docs: complete release documentation sprint | simple | +1344 | -104 | — | | #315 | Recipe remediation — source_dir resolution and cook command display | simple | +317 | -71 | #320 | | #317 | Implementation Plan: PR Review Pipeline Gates — Fidelity Checks, CI Gating, and Review-First Enforcement | simple | +1057 | -7 | #320 | | #320 | feat: add recipe composition with run_recipe tool and dev-sprint consumer | needs_check | +724 | -19 | #315, #317 | ## Audit **Verdict:** GO ## Architecture Impact ### Development Diagram ```mermaid %%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef cli fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; subgraph Structure ["PROJECT STRUCTURE"] direction LR SRC["src/autoskillit/<br/>━━━━━━━━━━<br/>105 source files<br/>10 sub-packages"] TESTS["tests/<br/>━━━━━━━━━━<br/>170 test files<br/>Mirrors src layout"] end subgraph Build ["BUILD TOOLING"] direction LR PYPROJECT["● pyproject.toml<br/>━━━━━━━━━━<br/>hatchling backend<br/>uv package manager"] TASKFILE["Taskfile.yml<br/>━━━━━━━━━━<br/>task test-all<br/>task test-check<br/>task install-worktree"] UVLOCK["uv.lock<br/>━━━━━━━━━━<br/>Locked manifest"] end subgraph PreCommit ["PRE-COMMIT HOOKS"] direction TB RUFF_FMT["ruff-format<br/>━━━━━━━━━━<br/>Auto-format<br/>(writes source)"] RUFF_LINT["ruff check<br/>━━━━━━━━━━<br/>Lint --fix<br/>target: py311"] MYPY["mypy<br/>━━━━━━━━━━<br/>src/ type check<br/>--ignore-missing"] UVCHECK["uv lock --check<br/>━━━━━━━━━━<br/>Lockfile guard"] NOGEN["no-generated-configs<br/>━━━━━━━━━━<br/>Blocks hooks.json<br/>recipes/diagrams/"] GITLEAKS["gitleaks v8.30.0<br/>━━━━━━━━━━<br/>Secret scanning"] end subgraph Testing ["TEST FRAMEWORK"] direction LR PYTEST["pytest -n 4<br/>━━━━━━━━━━<br/>asyncio_mode=auto<br/>timeout=60s"] FIXTURES["conftest.py<br/>━━━━━━━━━━<br/>StatefulMockTester<br/>MockSubprocessRunner<br/>tool_ctx fixture"] IMPORTLINT["import-linter<br/>━━━━━━━━━━<br/>L0→L1→L2→L3<br/>Architecture gates"] end subgraph NewTests ["★ NEW TEST FILES"] direction LR TGATE["★ test_analyze_prs_gates.py<br/>━━━━━━━━━━<br/>PR gate analysis"] TFIDELITY["★ test_review_pr_fidelity.py<br/>━━━━━━━━━━<br/>Fidelity checks"] TRELEASE["★ test_release_workflows.py<br/>━━━━━━━━━━<br/>CI workflow tests"] TDRY["★ test_dry_walkthrough_contracts.py<br/>━━━━━━━━━━<br/>10 contract assertions"] end subgraph CI ["CI/CD WORKFLOWS"] direction TB TESTS_CI["tests.yml<br/>━━━━━━━━━━<br/>ubuntu + macos matrix<br/>preflight: uv lock --check<br/>uv sync + task test-all"] VBUMP["★ version-bump.yml<br/>━━━━━━━━━━<br/>integration→main merged<br/>MAJOR.MINOR.PATCH+1<br/>sync main→integration"] RELEASE["★ release.yml<br/>━━━━━━━━━━<br/>stable branch merge<br/>MAJOR.MINOR+1.0<br/>git tag + GitHub Release"] end subgraph EntryPoints ["ENTRY POINTS"] direction LR CLI_EP["autoskillit CLI<br/>━━━━━━━━━━<br/>autoskillit.cli:main<br/>● app.py modified"] INSTALL["★ install.sh<br/>━━━━━━━━━━<br/>End-user installer<br/>uv tool install<br/>autoskillit install"] end %% FLOW %% SRC --> PYPROJECT TESTS --> PYPROJECT PYPROJECT --> TASKFILE PYPROJECT --> UVLOCK TASKFILE --> RUFF_FMT RUFF_FMT --> RUFF_LINT RUFF_LINT --> MYPY MYPY --> UVCHECK UVCHECK --> NOGEN NOGEN --> GITLEAKS TASKFILE --> PYTEST PYTEST --> FIXTURES PYTEST --> IMPORTLINT PYTEST --> TGATE PYTEST --> TFIDELITY PYTEST --> TRELEASE PYTEST --> TDRY PYPROJECT --> TESTS_CI TESTS_CI --> VBUMP VBUMP --> RELEASE PYPROJECT --> CLI_EP INSTALL --> CLI_EP %% CLASS ASSIGNMENTS %% class SRC,TESTS cli; class PYPROJECT,TASKFILE,UVLOCK phase; class RUFF_FMT,RUFF_LINT,MYPY,UVCHECK,NOGEN,GITLEAKS detector; class PYTEST,FIXTURES,IMPORTLINT handler; class TGATE,TFIDELITY,TRELEASE,TDRY,VBUMP,RELEASE,INSTALL newComponent; class TESTS_CI stateNode; class CLI_EP output; ``` ### Process Flow Diagram ```mermaid %%{init: {'flowchart': {'nodeSpacing': 40, 'rankSpacing': 50, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef integration fill:#c62828,stroke:#ef9a9a,stroke-width:2px,color:#fff; %% TERMINALS %% START([START]) SUCCESS([SUCCESS]) VALFAIL([VALIDATION ERROR]) subgraph Val ["Load-Time Validation"] direction LR ValidateRecipe["validate_recipe<br/>━━━━━━━━━━<br/>recipe/_api.py"] RulesRecipe["★ rules_recipe.py<br/>━━━━━━━━━━<br/>unknown-sub-recipe rule"] AvailCtx["★ available_recipes<br/>━━━━━━━━━━<br/>ValidationContext field"] NameKnown{"sub-recipe<br/>name known?"} end subgraph Exec ["Runtime Execution"] direction TB ToolHandler["★ run_recipe<br/>━━━━━━━━━━<br/>server/tools_recipe.py"] FindRecipe["★ _find_recipe()<br/>━━━━━━━━━━<br/>server/helpers.py"] FoundDec{"recipe<br/>file found?"} RunSub["★ _run_subrecipe_session()<br/>━━━━━━━━━━<br/>server/helpers.py"] BuildPrompt["★ build_subrecipe_prompt()<br/>━━━━━━━━━━<br/>cli/_prompts.py"] BuildCmd["★ build_subrecipe_cmd()<br/>━━━━━━━━━━<br/>execution/commands.py"] SubSess["★ run_subrecipe_session()<br/>━━━━━━━━━━<br/>execution/headless.py"] end HeadlessProc["Headless Claude<br/>━━━━━━━━━━<br/>AUTOSKILLIT_KITCHEN_OPEN=1<br/>executes sub-recipe YAML"] ResultDec{"success: True?"} START -->|"recipe YAML loaded"| ValidateRecipe ValidateRecipe --> RulesRecipe RulesRecipe --> AvailCtx AvailCtx --> NameKnown NameKnown -->|"unknown name"| VALFAIL NameKnown -->|"valid"| ToolHandler ToolHandler --> FindRecipe FindRecipe --> FoundDec FoundDec -->|"not found"| SUCCESS FoundDec -->|"found"| RunSub RunSub --> BuildPrompt RunSub --> BuildCmd BuildPrompt -->|"prompt str"| SubSess BuildCmd -->|"CLI cmd + env"| SubSess SubSess --> HeadlessProc HeadlessProc --> ResultDec ResultDec -->|"yes"| SUCCESS ResultDec -->|"no"| SUCCESS %% CLASS ASSIGNMENTS %% class START,SUCCESS,VALFAIL terminal; class ValidateRecipe handler; class RulesRecipe detector; class AvailCtx,NameKnown,FoundDec stateNode; class ToolHandler,FindRecipe,RunSub,BuildPrompt,BuildCmd,SubSess newComponent; class HeadlessProc integration; class ResultDec stateNode; ``` ### Deployment Diagram ```mermaid %%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef cli fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef integration fill:#c62828,stroke:#ef9a9a,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; subgraph DevMachine ["DEVELOPER MACHINE"] direction TB subgraph Bootstrap ["BOOTSTRAP (★ NEW)"] INSTALL_SH["★ install.sh<br/>━━━━━━━━━━<br/>curl from GitHub stable<br/>Python 3.11+ · uv · claude<br/>uv tool install"] end subgraph UVTool ["INSTALLED PACKAGE"] PKG["autoskillit package<br/>━━━━━━━━━━<br/>~/.local/share/uv/tools/<br/>autoskillit/lib/python3.x/<br/>site-packages/autoskillit/"] PLUGIN_CACHE["Plugin cache<br/>━━━━━━━━━━<br/>~/.claude/plugins/cache/<br/>autoskillit-local/"] end subgraph ClaudeCode ["CLAUDE CODE PROCESS"] CC["Claude Code IDE<br/>━━━━━━━━━━<br/>Reads hooks from<br/>~/.claude/settings.json<br/>.claude/settings.json"] MCP["● MCP Server (stdio)<br/>━━━━━━━━━━<br/>FastMCP · stdin/stdout<br/>12 ungated + 26 kitchen tools<br/>★ run_recipe · ★ get_ci_status"] end subgraph HeadlessSessions ["HEADLESS SUBPROCESS SESSIONS (pty)"] SKILL_SESS["Skill session<br/>━━━━━━━━━━<br/>claude --print prompt<br/>AUTOSKILLIT_HEADLESS=1<br/>KITCHEN_OPEN via open_kitchen"] SUBRECIPE["★ Sub-recipe session<br/>━━━━━━━━━━<br/>claude --print sous-chef-prompt<br/>AUTOSKILLIT_KITCHEN_OPEN=1<br/>NO HEADLESS flag<br/>build_subrecipe_cmd"] end subgraph LocalStorage ["LOCAL STORAGE"] SESSION_LOGS[("Session logs<br/>━━━━━━━━━━<br/>~/.local/share/autoskillit/<br/>logs/sessions/*.jsonl<br/>proc_trace · anomalies")] CRASH_TMP[("Crash traces<br/>━━━━━━━━━━<br/>/dev/shm/<br/>autoskillit_trace_pid.jsonl<br/>Linux tmpfs")] PROJ_STORE[("Project storage<br/>━━━━━━━━━━<br/>project/.autoskillit/<br/>config.yaml · .secrets.yaml<br/>temp/ · recipes/")] CLAUDE_LOGS[("Claude Code logs<br/>━━━━━━━━━━<br/>~/.claude/projects/<br/>encoded-cwd/<br/>session-id.jsonl")] end end subgraph GitHub ["GITHUB INFRASTRUCTURE"] direction TB GH_ACTIONS["GitHub Actions<br/>━━━━━━━━━━<br/>tests.yml: ubuntu + macos-15<br/>uv sync · task test-all"] VBUMP_WF["★ version-bump.yml<br/>━━━━━━━━━━<br/>integration→main merge<br/>patch bump + uv lock<br/>sync main→integration"] RELEASE_WF["★ release.yml<br/>━━━━━━━━━━<br/>stable branch merge<br/>minor bump + uv lock<br/>git tag + GitHub Release"] GH_REPO["GitHub Repository<br/>━━━━━━━━━━<br/>main · integration · stable<br/>Issues · PRs · Releases"] GH_API["GitHub API<br/>━━━━━━━━━━<br/>api.github.com<br/>Actions runs · CI jobs<br/>Issues · PR reviews"] end subgraph Anthropic ["EXTERNAL: ANTHROPIC"] ANT_API["Anthropic API<br/>━━━━━━━━━━<br/>api.anthropic.com<br/>/api/oauth/usage<br/>5-hour quota check"] end %% BOOTSTRAP FLOW %% INSTALL_SH -->|"uv tool install git@stable"| PKG PKG -->|"autoskillit install<br/>claude plugin install"| PLUGIN_CACHE %% CLAUDE CODE %% PLUGIN_CACHE -->|"plugin load"| CC CC -->|"spawns stdio"| MCP %% MCP → SESSIONS %% MCP -->|"run_skill: spawns pty"| SKILL_SESS MCP -->|"★ run_recipe: spawns pty<br/>KITCHEN_OPEN=1"| SUBRECIPE %% STORAGE WRITES %% SKILL_SESS -->|"writes diagnostics"| SESSION_LOGS SKILL_SESS -->|"crash: writes"| CRASH_TMP SUBRECIPE -->|"writes diagnostics"| SESSION_LOGS MCP -->|"reads/writes"| PROJ_STORE CC -->|"writes JSONL"| CLAUDE_LOGS %% CI/RELEASE %% GH_REPO -->|"push/PR event"| GH_ACTIONS GH_REPO -->|"integration→main merge"| VBUMP_WF GH_REPO -->|"★ PR→stable merge"| RELEASE_WF RELEASE_WF -->|"creates"| GH_REPO VBUMP_WF -->|"pushes commits"| GH_REPO %% EXTERNAL API CALLS %% MCP -->|"httpx HTTPS<br/>CI polling"| GH_API GH_ACTIONS -->|"gh CLI"| GH_API MCP -->|"httpx HTTPS<br/>quota check"| ANT_API SKILL_SESS -->|"Anthropic API<br/>inference"| ANT_API %% CLASS ASSIGNMENTS %% class INSTALL_SH,VBUMP_WF,RELEASE_WF,SUBRECIPE newComponent; class CC,SKILL_SESS cli; class MCP,GH_ACTIONS handler; class PKG,PLUGIN_CACHE phase; class SESSION_LOGS,CRASH_TMP,PROJ_STORE,CLAUDE_LOGS stateNode; class GH_REPO,GH_API,ANT_API integration; ``` Closes #307 Closes #302 Closes #298 Closes #297 Closes #300 Closes #303 🤖 Generated with [Claude Code](https://claude.com/claude-code) via AutoSkillit Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
This PR implements recipe composition for AutoSkillit, enabling recipes to invoke other recipes as sub-steps via a new
run_recipeMCP tool. The execution layer addsrun_subrecipe_session()and supporting prompt/command builders, wired into the server with arules_recipe.pyvalidation rule that catches references to unknown sub-recipes at load time. A newdev-sprintbundled recipe serves as the first consumer, orchestrating a full development sprint by composing triage, implementation, and PR creation phases through recipe composition.Individual Group Plans
Group 1: Recipe Composition — Core Execution Layer
Establish the core execution layer for recipe composition: adds
run_recipetoGATED_TOOLS, introducesbuild_subrecipe_cmd()andbuild_subrecipe_prompt()inexecution/commands.py, and addsrun_subrecipe_session()toexecution/headless.py. Sub-recipe sessions setAUTOSKILLIT_KITCHEN_OPEN=1to pre-open the kitchen gate without callingopen_kitchen.Group 2: Recipe Composition — Validation + Server Tool
Adds the
run_recipeMCP tool handler and its validation infrastructure. IntroducesValidationContext.available_recipes, a newrules_recipe.pyfile with theunknown-sub-recipeERROR rule, and updates_build_orchestrator_prompt()so parent orchestrators know how to handletool: run_recipesteps.Group 3: Dev-Sprint Recipe (Recipe Composition Consumer)
Creates the
dev-sprintbundled recipe as the first consumer of recipe composition. Automates a full development sprint: triage open issues →run_recipe implementation-groups(sub-recipe) → create integration PR.Architecture Impact
Process Flow Diagram
%%{init: {'flowchart': {'nodeSpacing': 40, 'rankSpacing': 50, 'curve': 'basis'}}}%% flowchart TB %% CLASS DEFINITIONS %% classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; START([Parent orchestrator<br/>tool: run_recipe step]) END_OK([SkillResult success=true]) END_FAIL([SkillResult success=false]) subgraph RuntimePath ["Runtime Execution Path"] direction TB subgraph EntryGate ["Entry & Gate Check"] ToolHandler["● run_recipe MCP tool<br/>━━━━━━━━━━<br/>tools_recipe.py<br/>+ helpers.py bridges"] GateCheck{"Kitchen gate<br/>enabled?"} end subgraph Resolution ["Recipe Resolution"] FindRecipe["● find_recipe_by_name<br/>━━━━━━━━━━<br/>recipe.io<br/>project → bundled fallback"] RecipeFound{"Recipe<br/>found?"} end subgraph SessionBuild ["Sub-Session Build"] BuildPrompt["● build_subrecipe_prompt<br/>━━━━━━━━━━<br/>commands.py<br/>pre-supplied ingredients +<br/>routing rules + YAML fences"] BuildCmd["● build_subrecipe_cmd<br/>━━━━━━━━━━<br/>commands.py<br/>AUTOSKILLIT_KITCHEN_OPEN=1<br/>(NOT HEADLESS)"] Runner["● run_subrecipe_session<br/>━━━━━━━━━━<br/>headless.py<br/>subprocess runner + timing<br/>+ token recording"] end subgraph SubSession ["Sub-Orchestrator Session"] direction LR KitchenOpen["● MCP init<br/>━━━━━━━━━━<br/>KITCHEN_OPEN=1<br/>gate.enable() +<br/>mcp.enable(kitchen)"] ExecSteps["Execute recipe steps<br/>━━━━━━━━━━<br/>no AskUserQuestion<br/>ingredients pre-supplied"] end end subgraph ValidationPath ["Load-Time Validation Path (load_and_validate)"] direction TB ListRecipes["● list_recipes<br/>━━━━━━━━━━<br/>project + bundled<br/>→ known frozenset"] MakeCtx["● make_validation_context<br/>━━━━━━━━━━<br/>_analysis.py<br/>available_recipes=known"] RegistryEmpty{"available_recipes<br/>empty?"} TemplateRef{"name is<br/>template ref?"} NameKnown{"name in<br/>available_recipes?"} NewRule["★ unknown-sub-recipe rule<br/>━━━━━━━━━━<br/>rules_recipe.py<br/>ERROR severity"] ValidOK([No finding — passes]) ValidERR([ERROR finding emitted]) end %% RUNTIME FLOW %% START --> ToolHandler ToolHandler --> GateCheck GateCheck -->|"disabled"| END_FAIL GateCheck -->|"enabled"| FindRecipe FindRecipe --> RecipeFound RecipeFound -->|"not found"| END_FAIL RecipeFound -->|"found"| BuildPrompt BuildPrompt --> BuildCmd BuildCmd --> Runner Runner --> KitchenOpen KitchenOpen --> ExecSteps ExecSteps -->|"success"| END_OK ExecSteps -->|"failure"| END_FAIL %% VALIDATION FLOW %% ListRecipes --> MakeCtx MakeCtx --> NewRule NewRule --> RegistryEmpty RegistryEmpty -->|"yes — skip"| ValidOK RegistryEmpty -->|"no"| TemplateRef TemplateRef -->|"yes — skip"| ValidOK TemplateRef -->|"no"| NameKnown NameKnown -->|"known"| ValidOK NameKnown -->|"unknown"| ValidERR %% CLASS ASSIGNMENTS %% class START,END_OK,END_FAIL,ValidOK,ValidERR terminal; class GateCheck,RecipeFound,RegistryEmpty,TemplateRef,NameKnown detector; class ToolHandler,FindRecipe handler; class BuildPrompt,BuildCmd,Runner phase; class KitchenOpen,ExecSteps stateNode; class ListRecipes,MakeCtx handler; class NewRule newComponent;Color Legend:
unknown-sub-reciperule — new in this PRModule Dependency Diagram
%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 70, 'curve': 'basis'}}}%% graph TB %% CLASS DEFINITIONS %% classDef cli fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; classDef phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; classDef handler fill:#e65100,stroke:#ffb74d,stroke-width:2px,color:#fff; classDef stateNode fill:#004d40,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef newComponent fill:#2e7d32,stroke:#81c784,stroke-width:2px,color:#fff; classDef output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; subgraph L3 ["L3 — SERVER LAYER"] direction LR ToolsRecipe["● server/tools_recipe.py<br/>━━━━━━━━━━<br/>run_recipe MCP handler<br/>REQ-IMP-003: only core/<br/>pipeline/server imports"] Helpers["● server/helpers.py<br/>━━━━━━━━━━<br/>_find_recipe bridge<br/>_run_subrecipe bridge<br/>imports execution + recipe"] end subgraph L2 ["L2 — RECIPE LAYER"] direction LR RulesRecipe["★ recipe/rules_recipe.py<br/>━━━━━━━━━━<br/>unknown-sub-recipe rule<br/>@semantic_rule decorator"] Analysis["● recipe/_analysis.py<br/>━━━━━━━━━━<br/>ValidationContext<br/>+available_recipes field"] RecipeApi["● recipe/_api.py<br/>━━━━━━━━━━<br/>load_and_validate<br/>populates available_recipes"] RecipeInit["● recipe/__init__.py<br/>━━━━━━━━━━<br/>+find_recipe_by_name export<br/>+rules_recipe side-effect import"] end subgraph L1 ["L1 — EXECUTION LAYER"] direction LR Commands["● execution/commands.py<br/>━━━━━━━━━━<br/>+build_subrecipe_cmd<br/>+build_subrecipe_prompt"] Headless["● execution/headless.py<br/>━━━━━━━━━━<br/>+run_subrecipe_session<br/>uses env kwarg on runner"] ExecInit["● execution/__init__.py<br/>━━━━━━━━━━<br/>+build_subrecipe_cmd<br/>+build_subrecipe_prompt<br/>+run_subrecipe_session"] end subgraph L0 ["L0 — CORE LAYER"] direction LR CoreTypes["● core/types.py<br/>━━━━━━━━━━<br/>GATED_TOOLS += run_recipe<br/>SubprocessRunner (env kwarg)"] CorePublic["core/ (other)<br/>━━━━━━━━━━<br/>Severity, SkillResult,<br/>RetryReason, ClaudeFlags"] end %% L3 → L3 (intra-layer helpers bridge) %% ToolsRecipe -->|"calls _find_recipe<br/>_run_subrecipe"| Helpers %% L3 → L2 (helpers bridge → recipe) %% Helpers -->|"imports find_recipe_by_name"| RecipeInit %% L3 → L1 (helpers bridge → execution) %% Helpers -->|"imports run_subrecipe_session"| ExecInit %% L2 intra-layer %% RulesRecipe -->|"imports ValidationContext"| Analysis RecipeApi -->|"imports make_validation_context"| Analysis RecipeInit -->|"side-effect import"| RulesRecipe %% L2 → L0 %% RulesRecipe -->|"imports Severity"| CorePublic Analysis -->|"imports SKILL_TOOLS"| CorePublic RecipeApi -->|"imports pkg_root, load_yaml"| CorePublic %% L1 intra-layer %% Headless -->|"imports build_subrecipe_cmd<br/>build_subrecipe_prompt"| Commands ExecInit -->|"re-exports"| Headless ExecInit -->|"re-exports"| Commands %% L1 → L0 %% Commands -->|"imports ClaudeFlags, pkg_root"| CorePublic Headless -->|"imports SkillResult, RetryReason"| CorePublic CoreTypes -.->|"part of core"| CorePublic %% CLASS ASSIGNMENTS %% class ToolsRecipe cli; class Helpers stateNode; class RulesRecipe newComponent; class Analysis,RecipeApi,RecipeInit phase; class Commands,Headless,ExecInit handler; class CoreTypes,CorePublic output;Color Legend:
server/helpers.py— architecture-compliance bridgerules_recipe.py— new file added by this PRCloses #303
Implementation Plan
Plan files:
/home/talon/projects/autoskillit-runs/impl-303-20260310-073832-454193/temp/make-plan/recipe_composition_plan_2026-03-10_part_a.md/home/talon/projects/autoskillit-runs/impl-303-20260310-073832-454193/temp/make-plan/recipe_composition_plan_2026-03-10_part_b.md/home/talon/projects/autoskillit-runs/impl-303-20260310-073832-454193/temp/make-plan/recipe_composition_plan_2026-03-10_part_c.mdToken Usage Summary
Token Summary
implement
audit_impl
open_pr
retry_worktree
make_plan
review_pr
dry_walkthrough
resolve_review
fix
investigate
rectify
open_pr_step
analyze_prs
merge_pr
create_review_pr
plan
verify
🤖 Generated with Claude Code via AutoSkillit