Bug Fix — Decompose open-research-pr into prepare + lens + compose Steps#656
Merged
Trecek merged 10 commits intointegrationfrom Apr 7, 2026
Conversation
Establishes acceptance criteria before implementation: - test_research_recipe_diag.py: 10 new tests asserting three-step PR prep flow - test_skill_contracts.py: 3 new tests for prepare/compose contracts and open-pr removal - test_open_research_pr_decomposition.py: structural guards for new skills Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…search.yaml
- commit_research_artifacts now captures report_path after renaming report.md → README.md
- push_branch routes to prepare_research_pr (was open_research_pr)
- replace open_research_pr with prepare_research_pr, run_experiment_lenses,
compose_research_pr; use ${{ context.experiment_plan }} instead of hardcoded path
- fix rules_skills._has_dynamic_skill_name to skip validation for {placeholder} tokens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New skills decompose the monolithic open-research-pr into two sessions that never invoke sub-skills via the Skill tool, avoiding the end_turn termination bug. Skill count updated to 93 in docs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each exp-lens skill now documents optional context_path and experiment_plan_path positional arguments, and includes a Step 0 to read them before CWD exploration. This enables run_experiment_lenses to pass structured context per lens. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove open-research-pr contract (skill retired) - Add prepare-research-pr contract with prep_path, selected_lenses, lens_context_paths outputs - Add compose-research-pr contract with pr_url output (graceful degradation pattern) - Add context_path and experiment_plan_path optional inputs to all 18 exp-lens contracts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove open-research-pr/SKILL.md and update skill count to 92 in docs (net: +2 new skills, -1 retired = 92 total). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Guard test asserts that prepare/compose skills must not reference both 'Skill tool' AND 'exp-lens' to avoid recreating the end_turn bug. Use neutral language that doesn't trigger the co-occurrence guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Delete retired test_open_research_pr_contracts.py (skill removed)
- Remove 4 stale test_open_research_pr_* methods from test_bundled_recipes.py
- Update RESEARCH_SKILL_NAMES: replace open-research-pr with prepare/compose
- Update skill counts: 88→89 (skills_extended), 90→91 (list_all total)
- Update write-recipe/SKILL.md bundled skills list
- Update defaults.yaml tier3: replace open-research-pr with prepare/compose
- Add dead-output exemption for prepare_research_pr note-driven captures
- Add prep_path to _EXPECTED_OUTPUT_PATH_TOKENS fixtures (2 test files)
- Add 'current working directory' anchor to both new SKILL.md files
- Add mermaid palette note to compose-research-pr/SKILL.md
- Add bash placeholder allowlist for compose-research-pr title/pr_body_path
- Fix resolve_skill_name to return None for bash {placeholder} truncated names
- Add test for new resolve_skill_name bash-placeholder behavior
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Trecek
commented
Apr 7, 2026
Collaborator
Author
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit PR Review — Verdict: changes_requested
4 blocking issues found. See inline comments.
| @@ -431,17 +431,17 @@ def test_tier1_only_in_skills_dir(self) -> None: | |||
| assert names == {"open-kitchen", "close-kitchen", "sous-chef"} | |||
|
|
|||
| def test_87_skills_in_skills_extended(self) -> None: | |||
Collaborator
Author
There was a problem hiding this comment.
[warning] cohesion: Function name test_87_skills_in_skills_extended is stale — the docstring and assertion now assert 89 skills (not 87). Rename to test_89_skills_in_skills_extended.
| {comma-separated absolute paths} | ||
| ``` | ||
|
|
||
| Store the absolute path as `prep_file_path`. |
Collaborator
Author
There was a problem hiding this comment.
[warning] cohesion: Step 7 stores the written path as prep_file_path but the Output section and skill_contracts.yaml declare the output token as prep_path. Rename the internal variable to prep_path to match the output contract and keep it consistent with other skills (e.g., pr_url, report_path).
…ic, assert Skill tool never referenced
…ic, assert Skill tool never referenced
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
open-research-prfails 100% because it invokesexp-lens-*sub-skills via theSkilltool inside a headless-psession. When the sub-skill completes and emits its output token, it issuesend_turn; in headless mode that terminates the entire session — Steps 5-7 (results summary, PR body, PR creation) are never reached.The fix is a recipe-level decomposition into three isolated sessions:
prepare_research_pr(reads report + plan, selects lenses, writes context files — no sub-skill invocations),run_experiment_lenses(runs each selectedexp-lens-{slug}in its own parallelrun_skillcall), andcompose_research_pr(validates diagrams, composes the PR body, creates the PR — no sub-skill invocations). Two secondary bugs are also fixed: the staleexperiment_planpath and thereport.md→README.mdrename that invalidatescontext.report_pathbeforeopen_research_prreads it.open-research-pris retired once the new skills are validated.Architecture Impact
Process Flow Diagram
%%{init: {'flowchart': {'nodeSpacing': 40, 'rankSpacing': 55, 'curve': 'basis'}}}%% flowchart TB 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 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 phase fill:#6a1b9a,stroke:#ba68c8,stroke-width:2px,color:#fff; START([START]) ESCALATE([ESCALATE STOP]) RESEARCH_COMPLETE([research_complete]) push_branch["● push_branch<br/>━━━━━━━━━━<br/>git push -u origin HEAD<br/>on_success → prepare_research_pr"] subgraph S1 ["★ SESSION 1 — prepare_research_pr"] direction TB PrepareSkill["★ prepare-research-pr<br/>━━━━━━━━━━<br/>reads README.md + experiment_plan<br/>synthesizes recommendation<br/>selects 1-2 exp-lens lenses<br/>writes context file per lens<br/>writes PR prep file<br/>NO Skill tool invocations"] PrepCapture["● context captures<br/>━━━━━━━━━━<br/>prep_path<br/>selected_lenses<br/>lens_context_paths"] end subgraph S2 ["★ SESSION 2 — run_experiment_lenses (parallel per lens)"] direction LR LensRun["● exp-lens-{slug}<br/>━━━━━━━━━━<br/>reads context_path (per lens)<br/>reads experiment_plan<br/>creates diagram file<br/>emits diagram_path<br/>NO parent-skill dependency"] LensCapture["● capture_list accumulates<br/>━━━━━━━━━━<br/>all_diagram_paths<br/>(comma-separated)"] end subgraph S3 ["★ SESSION 3 — compose_research_pr"] direction TB ComposeSkill["★ compose-research-pr<br/>━━━━━━━━━━<br/>reads prep file<br/>validates diagrams<br/>(keyword check)<br/>composes PR body<br/>gh pr create<br/>NO Skill tool invocations"] PrCapture["context captures<br/>━━━━━━━━━━<br/>pr_url"] end guard_pr_url{"guard_pr_url<br/>━━━━━━━━━━<br/>pr_url non-empty?"} REVIEW["review_research_pr"] START --> push_branch push_branch -->|"on_success"| PrepareSkill push_branch -->|"on_failure"| ESCALATE PrepareSkill --> PrepCapture PrepCapture -->|"on_success"| LensRun PrepCapture -->|"on_failure"| ESCALATE LensRun --> LensCapture LensCapture -->|"on_success OR on_failure"| ComposeSkill ComposeSkill --> PrCapture PrCapture -->|"on_success"| guard_pr_url PrCapture -->|"on_failure"| ESCALATE guard_pr_url -->|"pr_url set"| REVIEW guard_pr_url -->|"pr_url empty"| RESEARCH_COMPLETE class START,ESCALATE,RESEARCH_COMPLETE terminal; class push_branch handler; class PrepareSkill,ComposeSkill newComponent; class PrepCapture,LensCapture output; class PrCapture output; class LensRun phase; class guard_pr_url stateNode; class REVIEW handler;Color Legend:
Operational Diagram
%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis'}}}%% flowchart TB 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 SkillRegistry ["● SKILL REGISTRY (skill_contracts.yaml + defaults.yaml)"] direction TB subgraph Tier3 ["Tier 3 — Automation/PR Lifecycle"] direction LR PrepSkill["★ prepare-research-pr<br/>━━━━━━━━━━<br/>Part 1/3 research-PR flow<br/>outputs: prep_path<br/>selected_lenses<br/>lens_context_paths"] ComposeSkill["★ compose-research-pr<br/>━━━━━━━━━━<br/>Part 3/3 research-PR flow<br/>outputs: pr_url<br/>write_behavior: always"] OpenPR["open-pr<br/>━━━━━━━━━━<br/>General PR creation<br/>(unchanged)"] end subgraph Tier2 ["● Tier 2 — Interactive/Cook+Headless"] direction LR ExpLens["● exp-lens-{slug} (18 skills)<br/>━━━━━━━━━━<br/>New optional inputs added:<br/>context_path<br/>experiment_plan_path<br/>output: diagram_path"] end end subgraph Recipe ["● research.yaml — PR Preparation Phase"] direction TB PushBranch["● push_branch<br/>━━━━━━━━━━<br/>git push origin HEAD<br/>routes to → prepare_research_pr"] PrepStep["● prepare_research_pr step<br/>━━━━━━━━━━<br/>run_skill: prepare-research-pr<br/>captures: prep_path<br/>selected_lenses<br/>lens_context_paths"] LensStep["★ run_experiment_lenses step<br/>━━━━━━━━━━<br/>run_skill: exp-lens-{slug}<br/>capture_list: all_diagram_paths<br/>on_failure → compose (partial OK)"] ComposeStep["★ compose_research_pr step<br/>━━━━━━━━━━<br/>run_skill: compose-research-pr<br/>captures: pr_url"] end subgraph Config ["● CONFIGURATION"] direction TB Defaults["● defaults.yaml<br/>━━━━━━━━━━<br/>prepare-research-pr → tier3<br/>compose-research-pr → tier3<br/>completion_marker: %%ORDER_UP%%"] Contracts["● skill_contracts.yaml<br/>━━━━━━━━━━<br/>New: prepare-research-pr contract<br/>New: compose-research-pr contract<br/>Updated: 18 exp-lens contracts<br/>Removed: open-research-pr contract"] end subgraph Monitoring ["OBSERVABILITY"] direction TB PrepLog["★ .autoskillit/temp/prepare-research-pr/<br/>━━━━━━━━━━<br/>pr_prep_{ts}.md<br/>exp_lens_context_{slug}_{ts}.md<br/>(READ-WRITE — compose consumes)"] ComposeLog["★ .autoskillit/temp/compose-research-pr/<br/>━━━━━━━━━━<br/>pr_body_{ts}.md<br/>(WRITE-ONLY — humans inspect)"] LensDiag["● .autoskillit/temp/exp-lens-{slug}/<br/>━━━━━━━━━━<br/>exp_diag_{slug}_{ts}.md<br/>(WRITE-ONLY — compose validates)"] end PrepSkill -->|"registered in"| Defaults ComposeSkill -->|"registered in"| Defaults PrepSkill -->|"contract in"| Contracts ComposeSkill -->|"contract in"| Contracts ExpLens -->|"contracts updated in"| Contracts PushBranch --> PrepStep PrepStep -->|"run_skill"| PrepSkill PrepStep --> LensStep LensStep -->|"run_skill (per selected lens)"| ExpLens LensStep --> ComposeStep ComposeStep -->|"run_skill"| ComposeSkill PrepSkill --> PrepLog ExpLens --> LensDiag ComposeSkill --> ComposeLog PrepLog -->|"read by"| ComposeSkill LensDiag -->|"validated by"| ComposeSkill class PrepSkill,ComposeSkill newComponent; class ExpLens,PushBranch,PrepStep,Defaults,Contracts phase; class LensStep,ComposeStep newComponent; class PrepLog,LensDiag,ComposeLog output; class OpenPR handler;Color Legend:
.autoskillit/temp/Closes #652
Implementation Plan
Plan file:
/home/talon/projects/autoskillit-runs/impl-20260407-101349-407505/.autoskillit/temp/make-plan/open_research_pr_decompose_plan_2026-04-07_101349.md🤖 Generated with Claude Code via AutoSkillit
Token Usage Summary