Skip to content

feat: add recipe composition with run_recipe tool and dev-sprint consumer#320

Closed
Trecek wants to merge 13 commits intointegrationfrom
combined-recipe-composition-recipe-calls-recipe-infrastructu/303
Closed

feat: add recipe composition with run_recipe tool and dev-sprint consumer#320
Trecek wants to merge 13 commits intointegrationfrom
combined-recipe-composition-recipe-calls-recipe-infrastructu/303

Conversation

@Trecek
Copy link
Collaborator

@Trecek Trecek commented Mar 10, 2026

Summary

This PR implements recipe composition for AutoSkillit, enabling recipes to invoke other recipes as sub-steps via a new run_recipe MCP tool. The execution layer adds run_subrecipe_session() and supporting prompt/command builders, wired into the server with a rules_recipe.py validation rule that catches references to unknown sub-recipes at load time. A new dev-sprint bundled 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_recipe to GATED_TOOLS, introduces build_subrecipe_cmd() and build_subrecipe_prompt() in execution/commands.py, and adds run_subrecipe_session() to execution/headless.py. Sub-recipe sessions set AUTOSKILLIT_KITCHEN_OPEN=1 to pre-open the kitchen gate without calling open_kitchen.

Group 2: Recipe Composition — Validation + Server Tool

Adds the run_recipe MCP tool handler and its validation infrastructure. Introduces ValidationContext.available_recipes, a new rules_recipe.py file with the unknown-sub-recipe ERROR rule, and updates _build_orchestrator_prompt() so parent orchestrators know how to handle tool: run_recipe steps.

Group 3: Dev-Sprint Recipe (Recipe Composition Consumer)

Creates the dev-sprint bundled 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;
Loading

Color Legend:

Color Category Description
Dark Blue Terminal Start, success, failure, and validation result states
Red Detector Decision/guard checks (gate, recipe found, validation conditions)
Orange Handler Processing nodes (tool handler, recipe resolution, list/context)
Purple Phase Session build steps (prompt, cmd, runner)
Teal State Sub-session MCP init and step execution
Green New unknown-sub-recipe rule — new in this PR

Module 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;
Loading

Color Legend:

Color Category Description
Dark Blue Server Tools L3 tool handler (REQ-IMP-003 restricted)
Teal Bridge Module server/helpers.py — architecture-compliance bridge
Green New rules_recipe.py — new file added by this PR
Purple Recipe Layer L2 recipe modules — analysis, API, init
Orange Execution Layer L1 execution modules — commands, headless, init
Dark Teal Core Layer L0 foundation — types, public surface

Closes #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.md

Token Usage Summary

Token Summary

implement

  • input_tokens: 20305
  • output_tokens: 537078
  • cache_creation_input_tokens: 1621507
  • cache_read_input_tokens: 103595580
  • invocation_count: 17

audit_impl

  • input_tokens: 9681
  • output_tokens: 144617
  • cache_creation_input_tokens: 563252
  • cache_read_input_tokens: 4118056
  • invocation_count: 14

open_pr

  • input_tokens: 2269
  • output_tokens: 172827
  • cache_creation_input_tokens: 544287
  • cache_read_input_tokens: 9113477
  • invocation_count: 9

retry_worktree

  • input_tokens: 45
  • output_tokens: 22321
  • cache_creation_input_tokens: 111687
  • cache_read_input_tokens: 3405410
  • invocation_count: 1

make_plan

  • input_tokens: 44
  • output_tokens: 69854
  • cache_creation_input_tokens: 121021
  • cache_read_input_tokens: 2735054
  • invocation_count: 1

review_pr

  • input_tokens: 8787
  • output_tokens: 383468
  • cache_creation_input_tokens: 801892
  • cache_read_input_tokens: 10830228
  • invocation_count: 12

dry_walkthrough

  • input_tokens: 57
  • output_tokens: 30444
  • cache_creation_input_tokens: 114452
  • cache_read_input_tokens: 1679519
  • invocation_count: 3

resolve_review

  • input_tokens: 669
  • output_tokens: 349584
  • cache_creation_input_tokens: 838303
  • cache_read_input_tokens: 38375492
  • invocation_count: 12

fix

  • input_tokens: 356
  • output_tokens: 132867
  • cache_creation_input_tokens: 480031
  • cache_read_input_tokens: 21980041
  • invocation_count: 7

investigate

  • input_tokens: 2053
  • output_tokens: 12607
  • cache_creation_input_tokens: 52059
  • cache_read_input_tokens: 382017
  • invocation_count: 1

rectify

  • input_tokens: 4616
  • output_tokens: 21997
  • cache_creation_input_tokens: 75946
  • cache_read_input_tokens: 854207
  • invocation_count: 1

open_pr_step

  • input_tokens: 52
  • output_tokens: 27985
  • cache_creation_input_tokens: 102646
  • cache_read_input_tokens: 1871801
  • invocation_count: 2

analyze_prs

  • input_tokens: 13
  • output_tokens: 18525
  • cache_creation_input_tokens: 55984
  • cache_read_input_tokens: 358201
  • invocation_count: 1

merge_pr

  • input_tokens: 54
  • output_tokens: 11011
  • cache_creation_input_tokens: 124523
  • cache_read_input_tokens: 1156759
  • invocation_count: 4

create_review_pr

  • input_tokens: 35
  • output_tokens: 17881
  • cache_creation_input_tokens: 52674
  • cache_read_input_tokens: 1060679
  • invocation_count: 1

plan

  • input_tokens: 6248
  • output_tokens: 238067
  • cache_creation_input_tokens: 765044
  • cache_read_input_tokens: 9628970
  • invocation_count: 9

verify

  • input_tokens: 2305
  • output_tokens: 144281
  • cache_creation_input_tokens: 598389
  • cache_read_input_tokens: 7105068
  • invocation_count: 11

🤖 Generated with Claude Code via AutoSkillit

Trecek and others added 10 commits March 10, 2026 10:18
- 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.
Copy link
Collaborator Author

@Trecek Trecek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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>",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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",
]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

"""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]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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 = {}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Collaborator Author

@Trecek Trecek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoSkillit review found 9 blocking issues (4 critical, 5 warning). See inline comments for details.

Critical issues:

  1. Duplicate @mcp.tool registration: run_recipe stub in tools_execution.py conflicts with real implementation in tools_recipe.py — FastMCP will conflict at startup.
  2. _result None dereference after runner() exception in run_subrecipe_session — TypeError will mask the original exception.
  3. build_subrecipe_prompt placed 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.

Trecek and others added 3 commits March 10, 2026 15:55
…, 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>
Trecek added a commit that referenced this pull request Mar 11, 2026
- 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
@Trecek
Copy link
Collaborator Author

Trecek commented Mar 11, 2026

Collapsed into integration PR #323 (#323)

@Trecek Trecek closed this Mar 11, 2026
Trecek added a commit that referenced this pull request Mar 11, 2026
…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>
Trecek added a commit that referenced this pull request Mar 11, 2026
…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>
@Trecek Trecek deleted the combined-recipe-composition-recipe-calls-recipe-infrastructu/303 branch March 15, 2026 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant