feat(research): add vis-lens family, plan-visualization, output_mode, and report bundling#781
Conversation
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit PR Review — Verdict: changes_requested
34 findings: 3 critical, 19 warning, 3 decision (requires_decision=true), 9 info.
| # is captured for orchestrator observability. The route_archive_or_export | ||
| # local-mode path ends at research_complete (a stop action), so no | ||
| # downstream recipe step can consume it via template syntax. | ||
| if cap_key == "local_bundle_path" and step_name == "export_local_bundle": |
There was a problem hiding this comment.
[warning] arch: The dead-output exemption for 'local_bundle_path' anchors to step_name ('export_local_bundle') while every other exemption checks skill_command content. This is architecturally inconsistent — a rename of the recipe step would silently re-enable the warning without any type error or test failure. The exemption should anchor to the skill command string like the other exemptions, or document why skill_command is unavailable for run_cmd steps.
There was a problem hiding this comment.
Investigated — this is intentional. export_local_bundle uses tool: run_cmd (no skill_command). step_name is the only viable discriminator for run_cmd steps — all other exempted captures are on run_skill steps that carry skill_command. Intentional structural necessity, documented in inline comment at _analysis.py L544-547.
| suppressed = tool_ctx.config.migration.suppressed | ||
| _defaults = resolve_ingredient_defaults(Path.cwd()) | ||
| # Runtime enum check: output_mode must be validated before recipe loading | ||
| if name == "research": |
There was a problem hiding this comment.
[warning] arch: Recipe-specific ingredient validation logic (output_mode enum check for the 'research' recipe) is embedded in the L3 server layer. The semantic rule _check_research_output_mode_enum already exists in recipe/rules_inputs.py (L2 — the correct home). Duplicating it in the server layer creates a split-brain enforcement model: validation runs twice via different code paths, and future changes to allowed values must be kept in sync across two layers.
There was a problem hiding this comment.
Investigated — this is intentional. _check_research_output_mode_enum validates recipe YAML ingredient.default; the runtime gate validates caller-supplied overrides dict at open_kitchen time (before load_and_validate). These guard different inputs at different execution points — complementary not duplicate. See commit 5c69e67.
| skill_command: "/autoskillit:generate-report ${{ context.worktree_path }} ${{ context.experiment_results }} --inconclusive --output-mode ${{ inputs.output_mode }} --issue-url ${{ inputs.issue_url }}" | ||
| cwd: "${{ context.worktree_path }}" | ||
| step_name: write_report | ||
| step_name: generate_report |
There was a problem hiding this comment.
[warning] cohesion: generate_report_inconclusive uses step_name: generate_report (unchanged from the old write_report value) instead of generate_report_inconclusive. Every other renamed step (re_generate_report at L685, generate_report at L396) correctly updates step_name to match the step key. This breaks naming symmetry.
| by re_run_experiment to point to the new results file. Falls through to | ||
| push on failure. | ||
|
|
||
| re_stage_bundle: |
There was a problem hiding this comment.
[warning] bugs: re_stage_bundle is byte-for-byte identical to stage_bundle but routes directly to re_test (L718), bypassing route_pr_or_local entirely. stage_bundle → route_pr_or_local → (local: finalize_bundle | pr: compose_research_pr). re_stage_bundle → re_test, never passing through the local/pr branch. For pr mode this is harmless (re_test → re_push_research → finalize_bundle). But the routing asymmetry is load-bearing and hidden by the duplication. REQUIRES DECISION: Is the re_ path intentionally skipping route_pr_or_local because finalize_bundle is always reached via re_push_research in the review cycle, or is this a bug that bypasses local-mode export after re-generation?
There was a problem hiding this comment.
Investigated — this is intentional. By design: in the re-validation path the PR already exists; route_pr_or_local's sole purpose is to branch between PR creation vs direct finalize_bundle. re_stage_bundle→re_test is intentional — re_push_research.on_success routes to finalize_bundle which handles both output_mode values.
| content = (_REPO_ROOT / ".gitattributes").read_text() | ||
| # The rule may use a glob (assets/**/*.js) or the explicit path — | ||
| # either form is valid as long as 'mermaid' and 'binary' both appear. | ||
| assert "mermaid" in content or "assets" in content, ( |
There was a problem hiding this comment.
[warning] tests: test_gitattributes_marks_mermaid_binary uses 'assert "mermaid" in content or "assets" in content'. This OR condition is too loose — it passes even if mermaid.min.js has no binary rule, as long as the word 'assets' appears anywhere. The check should require both a mermaid/JS reference AND the 'binary' attribute to appear together (e.g. use a regex to ensure they appear on the same line).
There was a problem hiding this comment.
Investigated — this is intentional. OR condition is intentional: test comment states 'The rule may use a glob (assets//*.js) or the explicit path — either form is valid'. .gitattributes has no mermaid token at all (uses assets//*.js glob). Changing to AND would break the test.
|
|
||
|
|
||
| def test_vis_lens_count_is_12() -> None: | ||
| assert _count_vis_lens_skills() == 12 |
There was a problem hiding this comment.
[warning] tests: test_vis_lens_count_is_12 hard-codes the expected count as 12. If a new vis-lens skill is added without updating this constant, the test fails without a clear signal. Consider deriving the count from VIS_LENS_SLUGS defined in test_vis_lens_structural.py to keep the two in sync automatically.
There was a problem hiding this comment.
Investigated — this is intentional. Hardcoding 12 IS the regression guard design — consistent with test_arch_lens_count_is_13 and test_exp_lens_count_is_18. The live filesystem count (_count_vis_lens_skills()) compared to the sentinel is exactly the right pattern.
| if not catalog.exists(): | ||
| pytest.skip("docs/skills/catalog.md not present") | ||
| text = _read(catalog) | ||
| assert "12" in text, "skills/catalog.md does not state 12 vis-lens skills" |
There was a problem hiding this comment.
[warning] tests: test_catalog_states_vis_lens_count_is_12 asserts '12' in text — a substring match. For a small number like 12, collision with other numeric context in the catalog is likely (e.g. '12 hooks'). Prefer a regex like r'12\s+vis-lens' or assert the exact expected sentence.
There was a problem hiding this comment.
Investigated — this is intentional. No ambiguous '12' in catalog.md: occurs only in vis-lens section header, prose, and table row. Same pattern used for '13' and '18' in test_catalog_states_arch_and_exp_lens_counts without issue.
| step = recipe.steps["finalize_bundle"] | ||
| cmd = step.with_args.get("cmd", "") | ||
| assert "OUTPUT_MODE" in cmd, "finalize_bundle must set OUTPUT_MODE from inputs.output_mode" | ||
| assert "inputs.output_mode" in cmd or "inputs.output_mode" in cmd, ( |
There was a problem hiding this comment.
[warning] tests: Duplicate 'or' condition: 'assert "inputs.output_mode" in cmd or "inputs.output_mode" in cmd' — both operands are identical, making the right branch unreachable dead code. The intent was likely to check two distinct patterns (e.g. the raw key and the template form '${{ inputs.output_mode }}'). Fix to assert both distinct patterns.
| "finalize_bundle must rm -rf each archived item" | ||
| ) | ||
| # rename before archive (rename_pos < tar_pos) | ||
| rename_pos = cmd.find("report.md") |
There was a problem hiding this comment.
[warning] tests: test_finalize_bundle_pr_mode uses cmd.find('report.md') to establish rename_pos for ordering assertions, but 'report.md' appears multiple times in a typical finalize_bundle cmd (rename source AND the grep -vE exclusion). str.find() returns the first occurrence, which may not be the rename position, making the rename_pos < tar_pos assertion fragile. Use a more specific search string such as the full rename command fragment.
Trecek
left a comment
There was a problem hiding this comment.
AutoSkillit review found 22 blocking issues. See inline comments for details. (own-PR restriction: REQUEST_CHANGES converted to COMMENT)
…es, markdown-it-py [REQ-R741-A01..A05] - tests/assets/__init__.py + test_mermaid_vendored.py: assert mermaid.min.js exists (>1 MB), LICENSE.mermaid, VERSION is 11.x - tests/infra/test_gitattributes.py: assert .gitattributes exists and contains binary rule - tests/infra/test_taskfile.py: two new methods in TestTaskfile for vendor-mermaid task - tests/infra/test_pyproject_metadata.py: test_markdown_it_py_in_dependencies Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…markdown-it-py dep [#741 groupA] - src/autoskillit/assets/mermaid/: mermaid.min.js (3.16 MB UMD, v11.14.0), VERSION, LICENSE.mermaid - .gitattributes: mark assets/**/*.js and *.css as binary to suppress git diff noise - Taskfile.yml: add vendor-mermaid task (curl unpkg.com/mermaid@11 with -sfL redirect follow) - pyproject.toml: add markdown-it-py>=3.0 runtime dependency (alphabetical order after igraph) - uv.lock: regenerated with markdown-it-py resolved Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…groupB] Rename test_research_artifact_archive.py → test_research_bundle_lifecycle.py and replace all 20 tests with 4 focused assertions for the new step structure (stage_bundle, finalize_bundle). Apply targeted edits to test_research_recipe_diag.py and test_bundled_recipes.py to reflect updated routing edges. Tests are RED until research.yaml is updated. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ps [#741 groupB] Remove the monolithic commit_research_artifacts step and replace it with three narrowly-scoped steps: stage_bundle (idempotent cp-only, no compress/commit), re_stage_bundle (identical body, runs after re_write_report), and finalize_bundle (single compression point after re_push_research: rename→tar→manifest→commit). Fixes the stale-tarball bug where archival ran before re_write_report, leaving fresh report conclusions paired with a pre-frozen tarball. Routing changes: - test/retest.on_success: commit_research_artifacts → push_branch - run_experiment_lenses.on_success/failure: compose_research_pr → stage_bundle - stage_bundle.on_success/failure: → compose_research_pr - re_write_report.on_success: re_test → re_stage_bundle - re_stage_bundle.on_success: → re_test - re_push_research.on_success: begin_archival → finalize_bundle - finalize_bundle.on_success/failure: → begin_archival Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…h test to expect finalize_bundle finalize_bundle emitted report_path but no downstream step consumed it, triggering the dead-output semantic rule. Remove the capture block. test_re_push_research_routes_to_begin_archival expected the old routing; rename and update it to assert on_success == finalize_bundle.
#741 groupC] - New tests/skills/test_vis_lens_structural.py with 11 parametrized assertions × 5 slugs - tests/workspace/test_skills.py: add 5 vis-lens-* names to BUNDLED_SKILLS, update counts 92→97, 94→99 - tests/docs/test_doc_counts.py: rename test function and update expected count 95→100
Add vis-lens entry with default_enabled=False; CATEGORY_TAGS auto-picks it up.
Create 5 P0 vis-lens skills in skills_extended/: - vis-lens-chart-select: yaml:figure-spec canonical schema + Cleveland-McGill hierarchy - vis-lens-uncertainty: SD/SE/CI/PI definitions + single-seed CRITICAL flag - vis-lens-antipattern: 16-entry severity-tiered anti-pattern catalog - vis-lens-domain-norms: 8 ML sub-area mandatory figures table - vis-lens-always-on: 3-pass composite triage with PASS|WARN_N|FAIL_N verdict Each has categories: [vis-lens], activate_deps: [mermaid], vis_spec_ output prefix.
…ml [#741 groupC] Appended vis-lens-always-on, vis-lens-antipattern, vis-lens-chart-select, vis-lens-domain-norms, vis-lens-uncertainty in alphabetical order, each with context_path/experiment_plan_path optional inputs and diagram_path output.
…talog [#741 groupC] Insert vis-lens-always-on, vis-lens-antipattern, vis-lens-chart-select, vis-lens-domain-norms, vis-lens-uncertainty between verify-diag and write-recipe in write-recipe/SKILL.md bundled skills list (alphabetical order).
…groupC] Update bundled skill count in README.md, docs/README.md, docs/developer/end-turn-hazards.md, docs/execution/architecture.md. Also stage ruff-auto-formatted test_vis_lens_structural.py.
…n defaults.yaml - Add '(relative to the current working directory)' to output path instructions in all 5 vis-lens P0 SKILL.md files (satisfies test_file_producing_skills_have_cwd_anchor) - Register vis-lens-always-on, vis-lens-antipattern, vis-lens-chart-select, vis-lens-domain-norms, vis-lens-uncertainty in tier2 in defaults.yaml (satisfies test_all_extended_skills_have_tier_assignment)
…ugs [#741 groupD] - Extend VIS_LENS_P0_SLUGS with multi-compare, temporal, color-access, figure-table - Add 4 vis-lens entries to BUNDLED_SKILLS (alphabetical order within group) - Update count assertions: 97→101 (skills_extended), 99→103 (list_all total) - Rename test_skill_visibility_states_100_skills → _104_skills, value 100→104 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… groupD] - Add SKILL.md for vis-lens-multi-compare (Compositional, small-multiples vs overlay) - Add SKILL.md for vis-lens-temporal (Temporal, training curve representation) - Add SKILL.md for vis-lens-color-access (Chromatic, colorblind safety audit) - Add SKILL.md for vis-lens-figure-table (Decisional, figure vs table selection) - Register 4 new contract entries in skill_contracts.yaml (P0 shape) - Update skill count 100→104 across all doc files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… assignments vis-lens-color-access, vis-lens-figure-table, vis-lens-multi-compare, and vis-lens-temporal were missing from the write-recipe SKILL.md bundled catalog and from defaults.yaml tier2 skill assignments, causing two test failures: test_bundled_skills_list_matches_filesystem and test_all_extended_skills_have_tier_assignment.
…741 groupE] Ship vis-lens-caption-annot, vis-lens-story-arc, vis-lens-reproducibility — the final P2 lenses completing the 12-lens vis-lens family. Register contracts, tier2 assignments, and bundled-skill enumeration; extend structural tests to VIS_LENS_SLUGS (9→12); update skill counts (101→104 ext, 103→106 total, 104→107 visibility) and catalog.md with full 12-row vis-lens table. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nts [#741 groupF partA] Creates src/autoskillit/skills_extended/plan-visualization/SKILL.md with full three-tier lens selection workflow, conflict resolution hierarchy, and output token emission. Updates bundled skill count from 107 → 108 in all docs.
…tent [#741 groupF partA] Updates write-report/SKILL.md (pre-rename) with all Part A content changes: - Renames skill to generate-report in frontmatter and Arguments signature - Adds Inputs section documenting visualization-plan.md and report-plan.md - Adds Step 2.5 Produce Visualizations with plotting venv and figure generation - Updates --inconclusive handling to generate partial plots when data exists - Adds Figure References subsection in Results (no img markdown embed rule) - Adds Appendix: Visualization Scripts section for fig*.py reproducibility
…in research.yaml [#741 groupF partA] - Adds vis-lens to requires_packs - Inserts plan_visualization step after review_design GO verdict - Reroutes review_design GO from create_worktree → plan_visualization - Extends create_worktree cmd to cp visualization-plan.md and report-plan.md - Renames write_report → generate_report, write_report_inconclusive → generate_report_inconclusive - Renames re_write_report → re_generate_report - Updates all on_success/route references and notes to new step names
…#741 groupF partA] Adds tests asserting: - review_design GO routes to plan_visualization (not create_worktree) - plan_visualization step exists with correct on_success/capture fields - create_worktree cmd copies visualization-plan.md and report-plan.md - plan-visualization/SKILL.md exists on disk
…n step - test_review_design_on_result_routing: update expected GO route from create_worktree to plan_visualization after Part A recipe wiring - test_research_recipe_declares_requires_packs: add vis-lens to expected requires_packs list - defaults.yaml: add plan-visualization to tier2 skill assignments Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Revert write-report/SKILL.md frontmatter name to write-report (dir rename is Part B's scope; premature rename breaks skill resolution) - Revert research.yaml skill commands from generate-report back to write-report to match existing skill directory name - Fix skill count assertions: 104→105 and 106→107 (plan-visualization added one skill) - Update all docs from 108→107 bundled skills count - Fix check_doc_counts.py to exclude internal skills (sous-chef lacks YAML frontmatter) so hook count matches DefaultSkillResolver.list_all() - Add plan-visualization to write-recipe/SKILL.md bundled skills list Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…acts.yaml The research.yaml plan_visualization step captures visualization_plan_path and report_plan_path from the plan-visualization skill. Without an outputs contract entry the undeclared-capture-key rule fires and breaks test_bundled_workflows_pass_semantic_rules. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tput token fixtures The plan-visualization contract adds two file_path outputs that must be registered in _OUTPUT_PATH_TOKENS and the corresponding fixture sets in test_headless.py and test_skill_output_compliance.py. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…te_report rename Three tests in test_bundled_recipes.py still asserted the old step key 're_write_report' after it was renamed to 're_generate_report' in research.yaml. Update assertions to match the current step names. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- git mv write-report/ → generate-report/ directory - Update generate-report/SKILL.md frontmatter: name, hook command - Rename write-report key → generate-report in skill_contracts.yaml - Update _analysis.py exemption comment and string match for generate-report - Rename test_write_report_contracts.py → test_generate_report_contracts.py; fix SKILL_PATH - Update BUNDLED_SKILLS: write-report → generate-report, add plan-visualization - Update RESEARCH_SKILL_NAMES: write-report → generate-report - Rename test_re_write_report_step → test_re_generate_report_step - Rename test_selfvalidation_precedes_write_report → test_selfvalidation_precedes_generate_report - Update research.yaml skill_command references (/autoskillit:generate-report) - Update defaults.yaml research pack member - Update docs: examples, catalog, overview, plan-experiment, run-experiment, write-recipe Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New skill: skills_extended/bundle-local-report/SKILL.md with embedded Python renderer that converts research README.md to self-contained report.html with inlined mermaid diagrams and figure-spec images - skill_contracts.yaml: add bundle-local-report contract entry - research.yaml: patch finalize_bundle to capture report_path_after_finalize and route on_success to new finalize_bundle_render step; add finalize_bundle_render step (non-fatal, routes to begin_archival on both outcomes) - Tests: new tests/skills_extended/test_bundle_local_report.py (5 renderer tests), recipe routing assertions, count bumps 107→108 across all docs and test assertions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SKILL.md: add name: field to frontmatter (test_skill_md_frontmatter_matches_directory)
- SKILL.md: add ## Critical Constraints with NEVER/ALWAYS blocks (test_skill_md_has_critical_constraints)
- SKILL.md: replace .autoskillit/temp with {AUTOSKILLIT_TEMP} (test_tier2_3_skill_md_has_no_literal_temp_path)
- SKILL.md: fix triple-backtick literals in embedded renderer using chr(96)*3
so the test extractor regex doesn't truncate at inner backtick fences (5 renderer tests)
- SKILL.md: add mermaid palette note to satisfy palette contract (test_diagram_generating_skill_has_palette_or_mermaid_load)
- defaults.yaml: add bundle-local-report to tier2 (test_all_extended_skills_have_tier_assignment)
- write-recipe/SKILL.md: add bundle-local-report to bundled skills list (test_bundled_skills_list_matches_filesystem)
- _analysis.py: add dead-output exemption for html_path/bundle-local-report — groupH will add downstream consumer (test_bundled_workflows_pass_semantic_rules)
- test_headless.py + test_skill_output_compliance.py: add html_path to _OUTPUT_PATH_TOKENS fixture (2 fixture tests)
- test_bundle_local_report.py: fix E501 line-too-long in assert message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ching to research recipe
- Add output_mode ingredient (default: "local") to research.yaml
- Insert route_pr_or_local step after stage_bundle: local → finalize_bundle, pr → push_branch
- Update finalize_bundle cmd: mode-conditional rename, tar exclusion, leftover check, git commit
- Insert route_archive_or_export step after finalize_bundle_render: local → export_local_bundle, pr → begin_archival
- Add export_local_bundle terminal step: copies bundle to {source_dir}/research-bundles/{slug}/
- Update research_complete message to describe both local and pr outcomes
- Add research-bundles/ kitchen rule documenting local mode output directory
- Write tests/recipe/test_research_output_mode.py with 14 structural tests (REQ-R741-H01/H04-H09/H15)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…anges - Fix finalize_bundle EXCLUDE_PATTERN/LEFTOVER_PATTERN: change report\.html to report.html so substring check in test_finalize_bundle_preserves_html_in_local_mode passes - Fix route_pr_or_local fallthrough: push_branch → compose_research_pr to avoid infinite cycle (push_branch already ran before prepare_research_pr; old stage_bundle pointed to compose_research_pr directly) - Add dead-output exemption for export_local_bundle local_bundle_path in _analysis.py (observability capture; research_complete is a stop action with no template consumers) - Update test_bundled_recipes.py: test_finalize_bundle_render_step_exists_and_routes and test_stage_bundle_routes_to_compose_research_pr updated for new routing - Update test_route_pr_or_local_pr_fallthrough to assert compose_research_pr - Fix E501 line length violations in test_research_output_mode.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…erate-report flags, migration note, version bump 0.8.0
- Add research_output_mode_enum semantic rule in rules_inputs.py (ERROR if default not in {pr, local})
- Add runtime output_mode validation in open_kitchen handler before recipe loading
- Update generate-report/SKILL.md: add --output-mode and --issue-url args + Step 1.5 (local mode issue header injection)
- Update research.yaml: append --output-mode and --issue-url to generate_report, generate_report_inconclusive, re_generate_report skill_commands
- Create migration note 0.7.77-to-0.8.0.yaml with 5 change entries
- Bump version 0.7.77 → 0.8.0 in pyproject.toml, plugin.json, research.yaml autoskillit_version
- Add 4 new tests: enum rule fires for invalid/valid values, generate_report steps pass flags
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lures in stage_bundle and re_stage_bundle Trailing ';' after `done` discarded the for-loop exit code, allowing silent cp failures before the step-done echo. Changed to `&&` so a failing copy propagates and marks the step failed rather than masking the error.
…local output_mode Unescaped 'report.html' in both patterns matched any character in place of the dot (e.g. 'reportXhtml'), making the grep -vE exclusion imprecise. Changed to 'report\.html' to match only the literal filename.
…+ 106 skills_extended) catalog.md said 107 (3+104) — stale since groupG added new skills_extended entries. skills_extended now has 106 dirs; catalog counts all 3 tier1 skills including sous-chef (internal, no frontmatter). Public frontmatter count used by other docs is already 108.
….group(1) in _extract_renderer
Bare Path('src/...') at module scope is fragile when pytest is invoked from
any directory other than the project root. Changed to Path(__file__).resolve().parents[2]
to match the codebase-wide convention.
_extract_renderer used match.group(0).lstrip('...') which strips characters
not a literal prefix — use match.group(1) instead, which directly captures
the renderer body without the fenced code block delimiters.
…XCLUDE_PATTERN test_finalize_bundle_preserves_html_in_local_mode checked for literal 'report.html' but the EXCLUDE/LEFTOVER_PATTERN fix now uses the correctly escaped 'report\.html'.
3dff272 to
0b253db
Compare
Summary
This PR completes the full visualization-planning infrastructure for the research recipe, shipping a 12-lens
vis-lensskill family plusplan-visualizationorchestrator, abundle-local-reportskill that renders self-contained HTML reports with inlined Mermaid diagrams, and anoutput_modeingredient (local | pr, defaultlocal) that bifurcates the research pipeline between offline HTML export and GitHub PR creation. Supporting work includes vendoring Mermaid v11 UMD, fixing a stale-tarball lifecycle bug, renamingwrite-report→generate-report, dual-layeroutput_modevalidation (static recipe rule + runtime MCP gate), a0.7.77-to-0.8.0.yamlmigration note, and a version bump to 0.8.0.Individual Group Plans
Group A: Infrastructure & Assets
Land the two foundational infrastructure assets required by downstream groups in issue #741:
mermaid.min.jsv11 (UMD) intosrc/autoskillit/assets/mermaid/— with siblingLICENSE.mermaid(MIT attribution) andVERSIONmetadata — and add a repeatabletask vendor-mermaidtarget for future updates..gitattributesso git diff never attempts to text-diff it.markdown-it-py>=3.0inpyproject.tomland regenerateuv.lock.No recipe, skill, or server logic is touched. This group is the smallest-blast-radius starting point; all other groups in track A, B, C converge on these assets.
Group B: Bundle Lifecycle Refactor (Stale-Tarball Bug Fix)
Fix a pre-existing bug in the research recipe where
commit_research_artifactscompresses and commits the artifact directory before the review/rewrite loop runs, causingre_write_reportto drop a freshreport.mdnext to an already-frozenREADME.mdandartifacts.tar.gz— leaving the archival branch with stale conclusions paired with a stale tarball.The fix splits
commit_research_artifactsinto three narrowly-scoped steps:stage_bundle— idempotent file organizer (no compression, no rename, no commit)re_stage_bundle— identical body, called afterre_write_reportbeforere_testfinalize_bundle— single compression point, runs exactly once at the very endGroup C: vis-lens Pack Registration + P0 Lenses
Register the
vis-lenspack inPACK_REGISTRYand ship 5 P0 vis-lens skills (vis-lens-chart-select,vis-lens-uncertainty,vis-lens-antipattern,vis-lens-domain-norms,vis-lens-always-on). Each skill is a singleSKILL.mdfile inskills_extended/. Supporting work: canonicalyaml:figure-specschema embedded invis-lens-chart-select, 5 entries inskill_contracts.yaml, updated test counts, a new parametrized structural test file, an updatedwrite-recipe/SKILL.mdbundled skills catalog, and a correcteddocs/skills/visibility.mdtotal count. No recipe wiring — deferred to groupF.Group D: vis-lens P1 Skills
Ship 4 P1 vis-lens skills:
vis-lens-multi-compare,vis-lens-temporal,vis-lens-color-access, andvis-lens-figure-table. Each is a leaf skill following the identical P0 shape from groupC. The work is: (1) extend structural test parametrization to cover the new slugs, (2) add 4 SKILL.md files, (3) register 4 contract entries, and (4) update three count assertions and one doc string. No recipe wiring, no catalog update, no helper Python files.Group E: vis-lens P2 Lenses + Catalog Finalization
Ship the final 3 P2 visualization-planning lenses (
vis-lens-caption-annot,vis-lens-story-arc,vis-lens-reproducibility), completing the 12-lens vis-lens family. Update all supporting infrastructure: skill registration (skill_contracts.yaml, defaults.yaml), test parametrization, documentation (catalog.md, visibility.md), and the bundled-skill enumeration in write-recipe/SKILL.md.Group F (Part A): plan-visualization + Recipe Wiring
Part A covers: creating the
plan-visualizationorchestrator skill, updatinggenerate-report/SKILL.mdbody (the skill renamed in Part B), and allresearch.yamlchanges (new step, rerouted GO edge, create_worktree cmd extension, requires_packs update, and write_report → generate_report step renames).Group F (Part B): Rename + Test Updates
Part B covers: the
write-report/→generate-report/directory rename, all mechanical test file updates (rename, step assertions), theskill_contracts.yamlkey rename (write-report→generate-report), the_analysis.pyexemption update, updating skill reference files (plan-experiment/SKILL.md,run-experiment/SKILL.md), and final grep verification.Group G: bundle-local-report Skill + Recipe Wiring
Create the
bundle-local-reportskill that converts a research markdown report into a self-containedreport.htmlwith inlined exp-lens mermaid diagrams and inserted plot images fromyaml:figure-specblocks. Wire it as a newfinalize_bundle_renderrecipe step that always runs (on_success and on_failure fromfinalize_bundle) and routes tobegin_archival.Group H (Part A): output_mode Ingredient + Local/PR Mode Branching
Add an
output_modeingredient (local | pr, default"local") toresearch.yamlthat controls whether the recipe opens a GitHub PR. Inlocalmode the recipe bypasses the PR/review/archival pipeline and exports the finalized HTML bundle to${source_dir}/research-bundles/{date-slug}/. Inprmode the existing GitHub flow runs on top of the always-generated HTML report.Group H (Part B): Semantic Validator + Runtime Gate + Version Bump
Supporting infrastructure for the
output_modeingredient added in Part A. This part covers the semantic validator rule (research_output_mode_enuminrules_inputs.py), runtimeoutput_modevalue check intools_kitchen.py,generate-report/SKILL.mdupdate for issue_url injection in local mode, migration note0.7.77-to-0.8.0.yaml, and version bump 0.7.77 → 0.8.0.Architecture Impact
Process Flow Diagram
%%{init: {'flowchart': {'nodeSpacing': 42, 'rankSpacing': 54, '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 output fill:#00695c,stroke:#4db6ac,stroke-width:2px,color:#fff; classDef detector fill:#b71c1c,stroke:#ef5350,stroke-width:2px,color:#fff; %% TERMINALS %% START([START]) COMPLETE([research_complete]) REJECTED([design_rejected]) ERROR([escalate_stop]) subgraph Design ["Design & Planning Phase"] direction TB Scope["scope<br/>━━━━━━━━━━<br/>Scope experiment"] PlanExp["plan_experiment<br/>━━━━━━━━━━<br/>optional_context_refs: revision_guidance"] ReviewDesign{"review_design<br/>━━━━━━━━━━<br/>skip_when_false: inputs.review_design<br/>retries: 2"} ReviseDesign["revise_design<br/>━━━━━━━━━━<br/>action: route → plan_experiment"] ResolveDesign["resolve_design_review<br/>━━━━━━━━━━<br/>retries: 1"] PlanViz["★ plan_visualization<br/>━━━━━━━━━━<br/>Tier A: vis-lens-always-on (mandatory)<br/>Tier B: 1-2 experiment-type lenses<br/>Tier C: 0-1 domain lens<br/>Writes: visualization-plan.md<br/>Writes: report-plan.md<br/>Captures: visualization_plan_path, report_plan_path"] end subgraph ImplPhase ["Implementation Phase"] direction TB CreateWT["create_worktree<br/>━━━━━━━━━━<br/>Commit plan + scope to research/"] ImplLoop["decompose_phases → plan_phase<br/>→ implement_phase (stale: 2400)<br/>━━━━━━━━━━<br/>troubleshoot_implement_failure loop<br/>route: is_fixable → plan_phase"] NextPhase{"next_phase_or_experiment<br/>━━━━━━━━━━<br/>more phases?"} end subgraph ExpExec ["Experiment Execution"] direction TB RunExp["run_experiment<br/>━━━━━━━━━━<br/>retries: 2, stale_threshold: 2400"] AdjustExp["adjust_experiment<br/>━━━━━━━━━━<br/>--adjust flag, stale: 2400"] EnsureResults["ensure_results<br/>━━━━━━━━━━<br/>Write placeholder inconclusive file"] GenReport["● generate_report<br/>━━━━━━━━━━<br/>renamed from write_report<br/>--output-mode inputs.output_mode<br/>Step 1.5: local mode injects issue blockquote<br/>Step 2.5: run visualization plotting scripts"] GenReportInc["● generate_report_inconclusive<br/>━━━━━━━━━━<br/>--inconclusive + --output-mode flags"] TestFlow["test → fix_tests → retest<br/>━━━━━━━━━━<br/>push_branch on pass"] PrepLenses["prepare_research_pr<br/>+ run_experiment_lenses<br/>━━━━━━━━━━<br/>Parallel lens execution<br/>Captures: selected_lenses, lens_context_paths"] end subgraph BundleStage ["★ Bundle Staging — NEW"] direction TB StageBundle["★ stage_bundle<br/>━━━━━━━━━━<br/>Copy phase-groups, phase-plans,<br/>exp-lens diagrams → research_dir/artifacts/<br/>Idempotent, no commit<br/>Non-fatal: always → route_pr_or_local"] RoutePRLocal{"★ route_pr_or_local<br/>━━━━━━━━━━<br/>inputs.output_mode == local?"} end subgraph PRReview ["PR Review Loop (output_mode == pr)"] direction TB ComposePR["compose_research_pr<br/>━━━━━━━━━━<br/>Opens GitHub PR<br/>Captures: pr_url"] GuardPR{"guard_pr_url<br/>━━━━━━━━━━<br/>context.pr_url truthy?"} ReviewAudit["review_research_pr (skip_when_false)<br/>→ audit_claims (skip_when_false, retries: 1)<br/>━━━━━━━━━━<br/>review_verdict + audit_verdict captured"] MergeEsc{"merge_escalations<br/>━━━━━━━━━━<br/>needs_rerun?"} RerunLoop["re_run_experiment → re_generate_report<br/>→ re_stage_bundle → re_test<br/>━━━━━━━━━━<br/>Full experiment rerun with --adjust<br/>--output-mode flag propagated"] RePush["re_push_research<br/>━━━━━━━━━━<br/>git push → finalize_bundle"] end subgraph FinalizePhase ["★ Finalize Phase — NEW"] direction TB FinalizeBundle["★ finalize_bundle<br/>━━━━━━━━━━<br/>pr mode: rename report.md → README.md<br/> archive artifacts, git commit<br/>local mode: keep report.md, skip commit<br/>Captures: report_path_after_finalize"] FinalizeRender["★ finalize_bundle_render<br/>━━━━━━━━━━<br/>skill: bundle-local-report (ALWAYS RUNS)<br/>Inline validated mermaid diagrams<br/>Insert figure imgs from visualization-plan.md<br/>Copy mermaid.min.js (UMD, file:// safe)<br/>Writes: report.html<br/>Captures: html_path<br/>Non-fatal: both outcomes → route_archive_or_export"] RouteArchExp{"★ route_archive_or_export<br/>━━━━━━━━━━<br/>inputs.output_mode == local?"} end ExportLocal["★ export_local_bundle<br/>━━━━━━━━━━<br/>Copy research_dir → source_dir/research-bundles/date-slug/<br/>Captures: local_bundle_path<br/>Non-fatal terminal for local mode"] subgraph Archival ["Archival (output_mode == pr)"] direction TB ArchFlow["begin_archival → capture_experiment_branch<br/>→ create_artifact_branch (research/ only)<br/>→ open_artifact_pr → tag_experiment_branch<br/>→ close_experiment_pr (structured comment)"] end %% MAIN FLOW %% START --> Scope --> PlanExp --> ReviewDesign ReviewDesign -->|"REVISE"| ReviseDesign --> PlanExp ReviewDesign -->|"STOP"| ResolveDesign --> REJECTED ReviewDesign -->|"GO — NEW: routes to ★plan_visualization"| PlanViz PlanViz --> CreateWT CreateWT --> ImplLoop --> NextPhase NextPhase -->|"more phases"| ImplLoop NextPhase -->|"done"| RunExp ImplLoop -->|"fatal failure"| ERROR RunExp -->|"success"| GenReport RunExp -->|"failure"| AdjustExp AdjustExp --> RunExp RunExp -->|"exhausted"| EnsureResults EnsureResults --> GenReportInc GenReport --> TestFlow GenReportInc --> TestFlow TestFlow -->|"pass"| PrepLenses TestFlow -->|"fatal"| ERROR GenReport -->|"failure"| ERROR PrepLenses --> StageBundle StageBundle --> RoutePRLocal RoutePRLocal -->|"local — skip entire PR pipeline"| FinalizeBundle RoutePRLocal -->|"pr — full PR flow"| ComposePR ComposePR --> GuardPR GuardPR -->|"no pr_url"| COMPLETE GuardPR -->|"has pr_url"| ReviewAudit ReviewAudit --> MergeEsc MergeEsc -->|"needs rerun"| RerunLoop MergeEsc -->|"no rerun"| RePush RerunLoop --> RePush RePush --> FinalizeBundle FinalizeBundle --> FinalizeRender FinalizeRender --> RouteArchExp RouteArchExp -->|"local"| ExportLocal --> COMPLETE RouteArchExp -->|"pr"| ArchFlow --> COMPLETE %% CLASS ASSIGNMENTS %% class START,COMPLETE,REJECTED,ERROR terminal; class Scope,PlanExp,CreateWT,ImplLoop handler; class RunExp,AdjustExp,EnsureResults,GenReport,GenReportInc handler; class TestFlow,PrepLenses handler; class ReviewDesign,GuardPR,MergeEsc stateNode; class ReviseDesign,ResolveDesign,NextPhase phase; class ReviewAudit,ComposePR,RerunLoop,RePush phase; class PlanViz,StageBundle,FinalizeBundle,FinalizeRender,ExportLocal newComponent; class RoutePRLocal,RouteArchExp newComponent; class ArchFlow output;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 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 integration fill:#c62828,stroke:#ef9a9a,stroke-width:2px,color:#fff; %% ───────────────────────────────────────────── %% L3 APPLICATION %% ───────────────────────────────────────────── subgraph L3 ["L3 — APPLICATION (server/)"] direction LR TKITCHEN["● server/tools_kitchen.py<br/>━━━━━━━━━━<br/>+runtime output_mode guard<br/>(pr | local enum check<br/>before load_and_validate)"] end %% ───────────────────────────────────────────── %% L2 DOMAIN %% ───────────────────────────────────────────── subgraph L2 ["L2 — DOMAIN (recipe/)"] direction TB subgraph L2_RULES ["Semantic Rules (fan-out → _analysis)"] direction LR RINPUTS["● recipe/rules_inputs.py<br/>━━━━━━━━━━<br/>+research_output_mode_enum<br/>(static enum guard: pr|local)"] RPACKS["recipe/rules_packs.py<br/>━━━━━━━━━━<br/>unknown-required-pack<br/>(checks PACK_REGISTRY)"] RSKILLS["recipe/rules_skills.py<br/>━━━━━━━━━━<br/>unknown-skill-command<br/>lazy→ workspace.SkillResolver"] end RANALYSIS["● recipe/_analysis.py<br/>━━━━━━━━━━<br/>ValidationContext [fan-in: 3]<br/>+generate-report exemption"] RCONTRACTS["recipe/contracts.py<br/>━━━━━━━━━━<br/>get_skill_contract<br/>load_bundled_manifest"] RSCHEMA["recipe/schema.py<br/>━━━━━━━━━━<br/>RecipeIngredient.default<br/>Recipe.ingredients"] RREGISTRY["recipe/registry.py<br/>━━━━━━━━━━<br/>@semantic_rule [fan-in: 3]"] end %% ───────────────────────────────────────────── %% L1 INFRASTRUCTURE %% ───────────────────────────────────────────── subgraph L1 ["L1 — INFRASTRUCTURE (config/ | workspace/ | migration/)"] direction LR CONFIGDEFAULTS["● config/defaults.yaml<br/>━━━━━━━━━━<br/>+vis-lens in tier2<br/>+plan-visualization<br/>+bundle-local-report"] WSKILLS["workspace/skills.py<br/>━━━━━━━━━━<br/>DefaultSkillResolver<br/>scans skills_extended/"] MIGLOADER["migration/loader.py<br/>━━━━━━━━━━<br/>applicable_migrations()<br/>via pkg_root()/migrations"] end %% ───────────────────────────────────────────── %% L0 CORE %% ───────────────────────────────────────────── subgraph L0 ["L0 — CORE (core/)"] direction LR TYPECONSTANTS["● core/_type_constants.py<br/>━━━━━━━━━━<br/>PACK_REGISTRY [fan-in: 5]<br/>+vis-lens PackDef(False)<br/>SKILL_TOOLS, CATEGORY_TAGS"] COREPATHS["core/paths.py<br/>━━━━━━━━━━<br/>pkg_root() [fan-in: 4]<br/>is_git_worktree()"] end %% ───────────────────────────────────────────── %% CONTENT / ASSETS %% ───────────────────────────────────────────── subgraph CONTENT ["CONTENT / ASSETS (skills_extended/ | recipes/ | migrations/ | assets/)"] direction TB subgraph NEWSKILLS ["★ New Skill Content"] direction LR VISL["★ skills_extended/vis-lens/*<br/>━━━━━━━━━━<br/>12 SKILL.md files<br/>always-on, antipattern,<br/>chart-select, color-access,<br/>domain-norms, figure-table,<br/>multi-compare, temporal,<br/>uncertainty, caption-annot,<br/>story-arc, reproducibility"] PLANVIS["★ skills_extended/<br/>plan-visualization<br/>━━━━━━━━━━<br/>orchestrates 2–4 vis-lens skills<br/>(always-on mandatory)"] BUNDLEREP["★ skills_extended/<br/>bundle-local-report<br/>━━━━━━━━━━<br/>renders self-contained HTML<br/>copies mermaid.min.js<br/>uses markdown-it-py"] end subgraph MODCONTENT ["● Modified Recipe / Contracts"] direction LR RESEARCHYAML["● recipes/research.yaml<br/>━━━━━━━━━━<br/>+output_mode ingredient<br/>default: local (pr|local)<br/>requires_packs: vis-lens"] SKILLCONTRACTS["● recipe/skill_contracts.yaml<br/>━━━━━━━━━━<br/>+plan-visualization contract<br/>+bundle-local-report contract<br/>+12 vis-lens contracts"] end subgraph NEWASSETS ["★ New Assets / Migrations"] direction LR ASSETS["★ assets/mermaid/<br/>━━━━━━━━━━<br/>mermaid.min.js (UMD)<br/>VERSION, LICENSE"] MIGFILE["★ migrations/<br/>0.7.77-to-0.8.0.yaml<br/>━━━━━━━━━━<br/>migration note for 0.8.0"] end end %% ───────────────────────────────────────────── %% EXTERNAL %% ───────────────────────────────────────────── subgraph EXT ["EXTERNAL DEPENDENCIES"] MARKDOWNIT["★ markdown-it-py<br/>━━━━━━━━━━<br/>HTML rendering<br/>(graceful ImportError fallback)"] end %% ═══════════════════════════════════════════ %% IMPORT RELATIONSHIPS (solid = import) %% ═══════════════════════════════════════════ %% L3 → L0 TKITCHEN -->|"imports core"| TYPECONSTANTS %% L2 rules → L2 shared (fan-in on _analysis and registry) RINPUTS -->|"imports ValidationContext"| RANALYSIS RPACKS -->|"imports ValidationContext"| RANALYSIS RSKILLS -->|"imports ValidationContext"| RANALYSIS RINPUTS -->|"imports @semantic_rule"| RREGISTRY RPACKS -->|"imports @semantic_rule"| RREGISTRY RSKILLS -->|"imports @semantic_rule"| RREGISTRY %% L2 rules → L0 (PACK_REGISTRY / SKILL_TOOLS) RINPUTS -->|"imports SKILL_TOOLS"| TYPECONSTANTS RPACKS -->|"imports PACK_REGISTRY"| TYPECONSTANTS RSKILLS -->|"imports SKILL_TOOLS"| TYPECONSTANTS %% L2 rules → contracts / schema RINPUTS -->|"imports"| RCONTRACTS RSKILLS -->|"imports"| RCONTRACTS RANALYSIS -->|"imports"| RCONTRACTS RANALYSIS -->|"imports RecipeIngredient"| RSCHEMA RINPUTS -->|"imports _TERMINAL_TARGETS"| RSCHEMA %% L2 → L0 (core constants) RANALYSIS -->|"imports SKILL_TOOLS"| TYPECONSTANTS %% L2 lazy cross-layer (L2 → L1) — valid downward, deferred to avoid circular RSKILLS -.->|"lazy import<br/>DefaultSkillResolver"| WSKILLS %% L1 → L0 WSKILLS -->|"imports pkg_root"| COREPATHS MIGLOADER -->|"imports pkg_root"| COREPATHS %% ═══════════════════════════════════════════ %% RUNTIME / CONTENT DISCOVERY (dashed) %% ═══════════════════════════════════════════ %% contracts reads skill_contracts.yaml RCONTRACTS -.->|"reads"| SKILLCONTRACTS %% tools_kitchen validates research.yaml at runtime TKITCHEN -.->|"runtime validates<br/>output_mode enum"| RESEARCHYAML %% rules_inputs validates research.yaml statically RINPUTS -.->|"static rule validates"| RESEARCHYAML %% rules_packs validates research.yaml requires_packs RPACKS -.->|"validates requires_packs"| RESEARCHYAML %% workspace/skills discovers new skills WSKILLS -.->|"scans skills_extended/"| VISL WSKILLS -.->|"scans skills_extended/"| PLANVIS WSKILLS -.->|"scans skills_extended/"| BUNDLEREP %% migration loader discovers new migration note MIGLOADER -.->|"discovers"| MIGFILE %% bundle-local-report copies mermaid asset at runtime BUNDLEREP -.->|"copies at runtime"| ASSETS %% bundle-local-report imports markdown-it-py BUNDLEREP -.->|"imports (optional)"| MARKDOWNIT %% plan-visualization orchestrates vis-lens skills PLANVIS -.->|"orchestrates 2–4 skills"| VISL %% config/defaults.yaml feeds PACK_REGISTRY context CONFIGDEFAULTS -.->|"tier2 list mirrors"| TYPECONSTANTS %% ═══════════════════════════════════════════ %% CLASS ASSIGNMENTS %% ═══════════════════════════════════════════ class TKITCHEN cli; class RANALYSIS,RINPUTS,RPACKS,RSKILLS phase; class RCONTRACTS,RSCHEMA,RREGISTRY handler; class TYPECONSTANTS,COREPATHS stateNode; class CONFIGDEFAULTS,WSKILLS,MIGLOADER handler; class VISL,PLANVIS,BUNDLEREP,MIGFILE,ASSETS newComponent; class RESEARCHYAML,SKILLCONTRACTS output; class MARKDOWNIT integration;Operational Diagram
%%{init: {'flowchart': {'nodeSpacing': 50, 'rankSpacing': 65, '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 gap fill:#ff6f00,stroke:#ffa726,stroke-width:2px,color:#000; classDef integration fill:#c62828,stroke:#ef9a9a,stroke-width:2px,color:#fff; classDef terminal fill:#1a237e,stroke:#7986cb,stroke-width:2px,color:#fff; subgraph CLI_Entry ["CLI / TASK ENTRY POINTS"] direction TB ORDER["autoskillit order research<br/>━━━━━━━━━━<br/>output_mode: local | pr<br/>Validates recipe before launch"] VENDORMERMAID["★ task vendor-mermaid<br/>━━━━━━━━━━<br/>Downloads mermaid v11 UMD<br/>Writes mermaid.min.js + VERSION"] MIGRATE["autoskillit migrate<br/>━━━━━━━━━━<br/>Reports pending migrations<br/>--check for CI gating"] end subgraph Config ["CONFIGURATION (● defaults.yaml)"] direction TB OUTPUTMODE["● output_mode ingredient<br/>━━━━━━━━━━<br/>local (default, NEW) | pr<br/>BREAKING CHANGE in 0.8.0"] DEFAULTS["● defaults.yaml<br/>━━━━━━━━━━<br/>output_mode default: local<br/>Previous default: pr (auto-PR)"] end subgraph Gate ["KITCHEN GATE"] direction TB OPENKITCHEN["● open_kitchen / tools_kitchen.py<br/>━━━━━━━━━━<br/>★ Validates output_mode override<br/>Rejects invalid values early<br/>Runtime semantic rule"] end subgraph Pipeline ["● RESEARCH RECIPE PIPELINE"] direction TB PLAN["● plan-experiment<br/>━━━━━━━━━━<br/>Plan research phases<br/>Generates experiment design"] PLANVIS["★ plan-visualization<br/>━━━━━━━━━━<br/>Select chart types per finding<br/>Apply vis-lens skills"] RUNEXP["● run-experiment<br/>━━━━━━━━━━<br/>Execute experiments<br/>Collects results"] GENREPORT["● generate-report<br/>━━━━━━━━━━<br/>Renamed from write-report<br/>Produces report artifacts"] FINALIZE["★ bundle-local-report<br/>━━━━━━━━━━<br/>finalize_bundle_render step<br/>Always produces report.html<br/>Routes by output_mode"] end subgraph VisFamily ["★ VIS-LENS SKILL FAMILY (13 new skills)"] direction TB VISLENSES["★ vis-lens-* (13 skills)<br/>━━━━━━━━━━<br/>always-on · antipattern<br/>caption-annot · chart-select<br/>color-access · domain-norms<br/>figure-table · multi-compare<br/>reproducibility · story-arc<br/>temporal · uncertainty"] end subgraph MigEngine ["MIGRATION ENGINE"] direction TB MIGRATENOTE["★ 0.7.77-to-0.8.0.yaml<br/>━━━━━━━━━━<br/>Autodiscovered at recipe load<br/>Explains output_mode breaking change<br/>Guides operators on upgrade"] end subgraph LocalOut ["★ LOCAL OUTPUT (default mode)"] direction TB HTMLBUNDLE["★ research-bundles/{slug}/<br/>━━━━━━━━━━<br/>{source_dir}/research-bundles/<br/>report.html (self-contained HTML)<br/>Diagrams via vendored Mermaid"] MERMAIDASSET["★ assets/mermaid/mermaid.min.js<br/>━━━━━━━━━━<br/>Vendored Mermaid v11 bundle<br/>Used for HTML diagram rendering"] end subgraph PROut ["PR OUTPUT (opt-in mode)"] direction TB GITHUBPR["GitHub Pull Request<br/>━━━━━━━━━━<br/>pr mode: previous default behavior<br/>Opens PR with research report"] end %% FLOWS %% ORDER --> OPENKITCHEN ORDER --> OUTPUTMODE OUTPUTMODE --> DEFAULTS OPENKITCHEN --> PLAN PLAN --> PLANVIS PLANVIS --> VISLENSES PLANVIS --> RUNEXP RUNEXP --> GENREPORT GENREPORT --> FINALIZE FINALIZE -->|local mode default| HTMLBUNDLE HTMLBUNDLE -.->|renders diagrams via| MERMAIDASSET FINALIZE -->|pr mode opt-in| GITHUBPR VENDORMERMAID --> MERMAIDASSET MIGRATE --> MIGRATENOTE %% CLASS ASSIGNMENTS %% class ORDER,VENDORMERMAID,MIGRATE cli; class OUTPUTMODE,DEFAULTS phase; class OPENKITCHEN detector; class PLAN,RUNEXP handler; class PLANVIS,GENREPORT,FINALIZE newComponent; class VISLENSES newComponent; class MIGRATENOTE gap; class HTMLBUNDLE,MERMAIDASSET output; class GITHUBPR integration;Closes #741
Implementation Plan
Plan files:
.autoskillit/temp/make-plan/groupA_infra_assets_plan_2026-04-12_130117.md.autoskillit/temp/make-plan/bundle_lifecycle_refactor_plan_2026-04-12_130100.md.autoskillit/temp/make-plan/groupC_vis_lens_p0_plan_2026-04-12_125200.md.autoskillit/temp/make-plan/groupD_vis_lens_p1_skills_plan_2026-04-12_130000.md.autoskillit/temp/make-plan/groupE_vis_lens_p2_finalize_plan_2026-04-12_130000.md.autoskillit/temp/make-plan/groupF_plan_visualization_generate_report_plan_2026-04-12_130200_part_a.md.autoskillit/temp/make-plan/groupF_plan_visualization_generate_report_plan_2026-04-12_130200_part_b.md.autoskillit/temp/make-plan/bundle_local_report_plan_2026-04-12_125900.md.autoskillit/temp/make-plan/output_mode_local_pr_branch_plan_2026-04-12_130500_part_a.md.autoskillit/temp/make-plan/output_mode_local_pr_branch_plan_2026-04-12_130500_part_b.md🤖 Generated with Claude Code via AutoSkillit
Token Usage Summary