| name | scifig-generate |
| description | Upload experimental data (CSV/Excel/matrix), auto-detect structure, infer scientific domain, recommend publication-grade charts, generate Nature/Cell/Science-aligned figure code, optimize multi-panel composition and palette systems, and export vector graphics with statistical reports. Triggers on "generate figure", "plot data", "sci figure", "科研图", "画图", "多 panel". |
| allowed-tools | Agent, AskUserQuestion, TodoWrite, Read, Write, Edit, Bash, Glob, Grep |
SciFig Generate
End-to-end workflow for turning real experimental data into submission-ready scientific figures. The skill is journal-token driven, domain-aware, and narrative-first: it reads the data, infers the scientific context, picks chart families and statistics, builds a multi-panel story when needed, and exports reproducible code plus publication assets.
Architecture
Pipeline: preference gates -> Phase 1 dataProfile -> Phase 2 chartPlan -> Phase 3 styledCode -> Phase 4 outputBundle. Each phase owns one artifact, and blocking findings route back to the owning phase before completion.
Package Integration (v0.1.5+)
Users who pip install scifig can import the canonical legend-contract finalizer directly instead of relying on the skill's embedded helpers.py source:
from scifig.polish import enforce, sanitize_columns, apply_chart_polish
df, col_map = sanitize_columns(df)
report = enforce(fig)
fig.savefig("output.svg")
scifig.polish is the single source of truth for the finalizer pipeline. The skill's phases/code-gen/helpers.py continues to provide the same functions inline (for environments without the package), but new code should prefer the package import. Function parity for the broader crowding-management and visual-density pipelines (apply_visual_content_pass, audit_figure_layout_contract, etc.) lands progressively over the v0.1.5 -> v0.2.0 milestone window.
Key Design Principles
- Journal-token driven: Use explicit style profiles instead of ad-hoc plotting choices. Nature dimensions are grounded in the official Nature figure guide; Cell-like and Science-like presets maintain the same production-safe discipline.
- Domain-aware charting: Infer likely domains such as genomics, single-cell, pharmacology, neuroscience, or clinical survival and bias chart recommendations toward the conventions of that field.
- Narrative multi-panel design: Treat multi-panel figures as a story with hero, support, validation, and mechanism panels rather than a loose grid of unrelated plots.
- Palette governance: Prefer restrained, colorblind-safe palettes; keep semantic mappings consistent across panels; avoid rainbow and uncontrolled red-green contrasts.
- Statistical honesty: No inferential claims without replicate or cohort meaning. When data only support descriptive visualization, say so.
- Reference visual grammar: Add data-supported evidence layers that make figures look like dense top-journal panels: metric tables, perfect-fit/reference lines, density halos, marginal distributions, density-colored points, sample-shape overlays, matrix cell labels, p-value stars only when supplied, and dual-axis error bars only when error columns exist.
- Policy-driven defaults: Thresholds for scale, crowding, visual density, render retries, and export QA come from shared workflow policies rather than ad-hoc literals.
- Agent-assisted quality gates: Use read-only Agents for complex schema review, chart/stat planning, layout/palette audit, generated-code review, and rendered QA.
- Reproducibility-first: Every figure should be exportable as code plus metadata, source-data manifests, render-QA evidence, and methods-ready statistical descriptions.
- Template-mining grounded: All "顶刊感" choices (rcParams kernel, sandwich layering, palette, GridSpec recipes, in-axes idioms, narrative arcs) trace back to the 77 reference cases under
template/articles/, distilled into the 7-module knowledge base under template-mining/. Operational helpers live in phases/code-gen/template_mining_helpers.py.
Template-Mining Knowledge Base
template-mining/ is the project's canonical reference layer for visual grammar. All Phase 2 narrative-arc / chart-family decisions and Phase 3 styling decisions must consult this knowledge base instead of inventing patterns.
Loading protocol (see template-mining/INDEX.md):
- Always load
template-mining/INDEX.md before Phase 2/3 decisions.
- Phase 2 narrative + chart selection:
- Read
06-narrative-arcs.md to bind the figure to one of 10 corpus arcs.
- Read
04-grid-recipes.md if panel_count > 1.
- Lookup
case-index.json for ≥3 cases matching chart_families or narrative_arc.
- Phase 3 code generation:
- Read
01-rcparams-kernel.md for the kernel; call apply_journal_kernel(variant, journalProfile).
- Read
02-zorder-recipes.md for the matching chart family; call apply_zorder_recipe(family, ax, layers).
- Read
03-palette-bank.md to bind palette names to hex; call resolve_palette(name) and role_color(role).
- Read
05-annotation-idioms.md to apply in-axes idioms; call add_metric_box, add_perfect_fit_diagonal, add_zero_reference, add_group_dividers, add_panel_label, density_color_scatter, add_polygon_polar_grid, draw_gradient_box from template_mining_helpers.
- Read
07-techniques/<family>.md only if the chart family has a dedicated deep-dive (radar, shap-composite, dual-axis, heatmap-pairwise, marginal-joint, time-series-pi, lollipop-bipolar, gradient-box, inset-distribution).
- Phase 4 render QA: every required motif from the chosen arc + family must be present (
arc_required_motifs(arc)); failures route back to Phase 3.
For finalizer-safe usage rules and ready annotation snippets, see
templates/finalizer-safe-template-contract.md and
templates/annotation-idioms-ready.md.
Re-extraction: when template/articles/ changes, run python .claude/skills/scifig-generate/template-mining/_extraction/extract.py then enrich.py to refresh case-index.json, stats.md, narratives.md. Then audit the "Distilled Universal Findings" section in INDEX.md.
Code promotion: when the user asks to absorb article examples into the skill, run optional Phase 5 and follow specs/template-distillation-contract.md. Promote reusable Matplotlib logic into helpers/generators first; update prose only after executable behavior and tests exist.
Autonomous distillation: when the user asks for recurring or overnight optimization, each cycle runs Phase 5 once, promotes one or two reusable template-derived improvements, generates ignored smoke figures under output/autonomous_distill/, validates render/test contracts, and commits tracked skill changes only.
Operational entry point — Phase 3 generators should start with:
from template_mining_helpers import (
apply_journal_kernel, resolve_palette, role_color,
add_metric_box, add_perfect_fit_diagonal, add_zero_reference,
add_group_dividers, add_panel_label,
density_sort, density_color_scatter,
add_polygon_polar_grid, draw_gradient_box,
add_forest_panel, add_heatmap_pairwise_panel,
apply_scatter_regression_floor, resolve_split_palette,
set_polar_title,
build_grid, select_narrative_arc, arc_required_motifs,
arc_default_grid, apply_zorder_recipe, bootstrap_chart,
)
apply_journal_kernel(variant="hero", journalProfile=journalProfile)
fig, axes, palette = bootstrap_chart(arc="hero", panel_count=1,
palette="nature_radar_dual",
journalProfile=journalProfile)
Finalizer Auto-corrections (cycle-22 + cycle-24 + V0.1.1)
enforce_figure_legend_contract(...) runs five zero-touch retrofit passes before audit_figure_layout_contract so common occlusion modes are repaired automatically without modifying generator source. Generators do not need to call these helpers explicitly — they fire from inside the contract finalizer.
| Pass | What it does | Generator-visible side effect |
|---|
center_figure_titles | Centers fig.suptitle(...) and moves left/right ax.set_title(...) text into the centered title slot. | Left-aligned titles are normalized; panel letters should come from add_panel_label(...), not title positions. |
sanitize_figure_text | Replaces fragile label glyphs such as R⊕, M⊕, log₁₀, and em dashes with ASCII-safe text. | Labels render reliably on clean systems without missing-glyph boxes. |
_promote_inaxes_text_safety | Promotes every in-axes Text artist to zorder>=20 and adds a rounded white bbox (alpha 0.85) when none exists. | ax.text(... fontsize=10) at default zorder=3 will be silently lifted to zorder=20 with a white background. |
_shrink_heatmap_cell_labels | Detects QuadMesh axes (sns.heatmap / pcolormesh) and reformats numeric cell labels to fit physical cell width via choose_heatmap_fmt. When fmt forced to .0f (very dense), applies graceful degradation: keeps only diagonal cells and |val| ≥ 0.5, removing the noise. | sns.heatmap(annot=True, fmt=".3f") may have its labels reformatted to .2f / .1f / .0f and some non-significant cells may lose their annotation entirely. |
_text_data_overlap_issues (audit) | Reports text-vs-line/scatter/patch geometric overlap > 30%. White-bbox text is treated as already-resolved and skipped. | audit["textDataOverlapCount"] and the annotation_text_buried_under_data failure flag. |
| normalize_axes_map (audit) | Single-panel charts with inset axes receive all non-colorbar fig.axes; no generator opt-in is needed. | audit["audited_axes_count"] includes main axes plus inset axes. |
| _text_text_overlap_issues / bbox coverage (audit) | Reports label-on-label collisions and oversized white bboxes that still cover plotted data. | audit["textTextOverlapCount"] and audit["bboxDataCoverageOverflowCount"] hard-fail Phase 4. |
Excluded artists (never modified)
- Axis chrome: title, x/y axis labels, tick labels.
- Panel labels (single uppercase A-Z).
- Heatmap cell labels (white bbox would erase the cell colour).
- Anything carrying a managed
gid:
scifig_metric_box, scifig_metric_table, scifig_inplot_label, scifig_panel_label.
Generator opt-outs
| If you need... | Use... |
|---|
| Raw text without a white bbox (LaTeX equation, decorative callout, custom-coloured background) | ax.text(... gid="scifig_no_safety_bbox") |
Scientific notation cell labels in a heatmap (1.2e-3) | The shrink pass auto-skips text containing e+ / e- / E+ / E- |
Significance markers in a heatmap (*, **, ns, n.s.) | The shrink pass auto-skips text containing *, †, ‡, §, ns, n.s. |
What the finalizer does NOT auto-invoke
The finalizer does not run expensive annotation relocation. Call these helpers
explicitly when dense annotation layouts require them.
Handcrafted helpers (manual invocation)
Helpers in template_mining_helpers.py Section 10 that are available but not auto-invoked:
safe_annotate(ax, text, xy, ...) — drop-in replacement for ax.annotate / ax.text that pre-applies the same zorder + bbox guards as the retrofit. Use when you want explicit control instead of relying on the retrofit.
auto_relocate_annotations(ax, ...) — heavyweight collision-avoidance relocator (probes 12 candidate offsets in display coords). Use only when annotation density is high enough that bbox alone is not enough; the default retrofit covers most cases.
Source lint
Generator source files must not contain ax.legend(...) with bbox_to_anchor=(1.02, 1) or (0.5, -X). phases/code-gen/source-lint.py blocks these reverse examples before generated code is finalized.
Bundled Fonts (assets/fonts/)
Templates anchor font.family to commercial typefaces (Arial / Helvetica / Times New Roman / SimHei) that the skill cannot legally redistribute. On Linux servers, Docker containers, and clean macOS installs these fonts are typically absent, so matplotlib falls back to DejaVu Sans, prints a findfont warning, and produces figures whose metrics no longer match the journal profile.
Resolution: drop legally-obtained *.ttf / *.otf / *.ttc files into assets/fonts/. The first call to apply_journal_kernel(...) scans that directory and registers every font with matplotlib's font_manager (idempotent across calls; safe under Phase 3's exec() runtime embed).
- What to drop in:
Arial.ttf, Times-New-Roman.ttf, Helvetica.ttf, SimHei.ttf — or their open metric-compatible replacements (Liberation Sans, Liberation Serif, Noto Sans SC, Carlito).
- Resolution priority for finding the directory:
SCIFIG_FONTS_DIR env var → injected __SCIFIG_SKILL_ROOT__ global (set by Phase 3 runtime) → __file__-relative (skill_root/assets/fonts/) → cwd-relative.
- Fallback discipline: every
font.family chain in the skill ends in DejaVu Sans (matplotlib's bundled default) so figures still render even when no user fonts are present — only the visual fidelity to the journal profile is degraded.
- Licensing:
*.ttf / *.otf / *.ttc files are gitignored at the project root. The skill ships zero font payloads to keep the repo license-clean.
See assets/fonts/README.md for licensing notes, troubleshooting (clearing matplotlib's font cache, CJK glyph fallback), and the full list of metric-compatible open replacements.
Interactive Preference Collection
Collect workflow preferences before dispatching to phases, but treat data availability, file-path validation, and mode selection as separate gates.
- Never use Bash, Glob, Grep, or Read to scan the workspace for candidate CSV, TSV, XLSX, or XLS files.
- Never assume files under
tests/, output/, fixtures, or nearby folders are the user's real data.
- Mirror the user's language in all AskUserQuestion cards. If the request is Chinese, use Chinese cards from the start.
- If the user did not explicitly provide
FILE: or a concrete file path, first use AskUserQuestion to ask whether a data file already exists.
- If the user says a file exists, use a second AskUserQuestion card to ask where it is. Tell the user to choose Other and paste the exact path there so the answer still appears under
User answered Claude's questions.
- If the path card comes back with the canned option label instead of a real path, ask one direct follow-up for the exact path and stop there.
- Only accept concrete file paths ending in
.csv, .tsv, .xlsx, or .xls. If the reply is a directory, a folder-like path, or anything without one of those suffixes, ask again and stop there.
- Never run Bash, Read, Grep, or directory inspection on a provided path before both the file-path gate and the mode gate are complete.
- Mode selection is a separate gate after the data-status decision, and after the file path only when a real file already exists. Never infer
auto from any answer that is not an exact mode-card selection, and never fall through to auto defaults after an invalid answer.
- If the user answers a card with a localization request such as
请用中文, treat it as a language-switch request, re-issue the same card in Chinese, and do not consume it as a data or mode decision.
- If the user does not have data yet, branch explicitly: either help define a schema/template first, or generate a synthetic dataset only if the user explicitly asks for simulated data. Custom domains entered through Other must be preserved and must not collapse back to biomedical defaults.
- AskUserQuestion payloads must stay tool-compatible: every question needs
id, header, question, and 2-3 options. Do not use multiSelect. Use 1-3 questions per card by default; the final visual preference card is the only permitted 4-question exception because journal style, color, resolution, and crowding must be answered together. Before showing that card, perform a domain-journal synthesis step from the selected or custom scientific domain. The journal-style options must be field-leading journals, conferences, or venue families for that exact domain, not a fixed Nature/Science/Cell list.
Preference collection helpers, bilingual card templates, answer maps, and resolution maps are defined in specs/preference-collection.md. Read that file before executing any AskUserQuestion card. The helpers use a single bilingual-lookup pattern instead of duplicated zh/en branches.
Key functions from that file used in the flow below:
normalize_card_answer(raw_answer, mapping) -- resolves card answers, detects language-switch requests
is_concrete_data_file_path(path_candidate) -- validates file suffix
resolve_domain_answer(raw_answer, mapping) -- maps domain card answers to family keys
infer_journal_style_options(domain_family, custom_domain_text, language, context=None) -- AI-generated journal options with domain-aware fallback
infer_synthetic_bundle_options(domain_family, custom_domain_text, language) -- domain-aware bundle options
infer_template_chart_bundle_options(domain_family, custom_domain_text, language, context=None) -- template-case-backed chart package options
resolve_chart_bundle_choice(selected_label, chart_bundle_options) -- maps the chart-package answer to primary/secondary charts plus template anchors
generate_options_with_ai(context, question_type, fallback_options) -- AI option generation with fallback chain
_call_option_generator(prompt, question_type, fallback_options) -- coordinator-interceptable call with proper fallback
Preference Collection Flow
Execute the base preference gates defined in specs/preference-collection.md § "Card Definitions and Flow", plus the template-backed chart-bundle card when a domain is available. Use the bilingual answer maps, card option templates, and resolution maps from that file. The flow is:
- Data-status card:
normalize_card_answer(answer, DATA_STATUS_MAP) → file_exists / synthetic_data / template
- Data-path card (only if file exists):
normalize_card_answer(answer, DATA_PATH_MAP) → validate with is_concrete_data_file_path()
- Mode card:
normalize_card_answer(answer, MODE_MAP) → auto / interactive
- Synthetic-domain card (only if synthetic):
resolve_domain_answer(answer, SYNTHETIC_DOMAIN_MAP) → domain family + custom text
- Synthetic-bundle card (only if synthetic):
infer_synthetic_bundle_options() + optional generate_options_with_ai()
- Template chart-bundle card: ask
你希望生成哪类图表套餐? and fill options with infer_template_chart_bundle_options(..., context={...}). Options must be domain-matched template-case packages ranked against dataProfile, not generic chart names. Pass only {label, description} to AskUserQuestion, but keep the full chartBundleOptions list in state so Phase 2 receives selectedChartBundle.primaryChart, secondaryCharts, templateAnchors, templateFamilies, and techniqueRefs. For real-file interactive mode, ask this after Phase 1 domain inference and before Phase 2 locks chartPlan; for synthetic runs, ask it after the synthetic-bundle card; for auto mode, choose the first compatible returned bundle without another user card.
- Visual-preference card (4 questions): use
_localize_options(template, lang) for palette/resolution/crowding; use infer_journal_style_options(..., context={...}) for AI-generated field-top-journal options based on the selected domain. Pass only {label, description} to AskUserQuestion, but keep the full journalOptions list in state for styleKey resolution. Example: audio/signal/acoustics should produce options such as IEEE/ACM TASLP, JASA, IEEE TSP / Signal Processing, ICASSP, or Interspeech when appropriate, not generic Nature-like / Science-like / Cell-like options.
After all cards, assemble workflowPreferences using the resolution maps (JOURNAL_STYLE_RESOLVE, STORY_MODE_RESOLVE, MISSING_RESOLVE, COLOR_MODE_RESOLVE, DPI_RESOLVE, CROWDING_RESOLVE, EXPORT_FORMATS_RESOLVE, STATS_RIGOR_RESOLVE, PANEL_LAYOUT_RESOLVE) as defined in § "Building workflowPreferences".
For auto mode: fill visual-preference card with submission-safe defaults unless the user selected interactive mode. Freeze workflowPreferences before phase dispatch.
Auto Mode Defaults
When workflowPreferences["interactionMode"] == "auto":
- Ask the data-status card first, then always ask the explicit mode card before any plotting or synthetic-data generation branch. Ask the data-path card only when the user already has a file.
- If the user chooses synthetic data, ask the synthetic-domain card after mode selection, infer suitable journal-style, synthetic-bundle, and template chart-bundle options from that answer, then ask the synthetic-bundle card and template chart-bundle card as separate follow-ups.
- Ask journal style, color, raster DPI, and crowding together in the final visual preference card for every synthetic-data run. The journal-style options in that card must come from
infer_journal_style_options(..., context={...}), which first asks AI to reason from the selected domain, custom domain text, and available data/profile context, then falls back to domain-specific top-journal seeds only if AI generation is unavailable or invalid. Do not show the broad Nature/Science/Cell trio unless the selected field itself is broad high-impact biology or the user explicitly asks for cross-disciplinary top-journal styling. For real-file auto mode, fill these with submission-safe defaults unless the user selected interactive mode.
- Use
crowdingPolicy=auto_simplify and overlapPriority=clarity_first.
- Continue directly into Phase 1 only after either a concrete user-confirmed file path exists or a user-approved synthetic-data plan exists, and only after the user explicitly selected free mode.
- Let the data, detected domain cues, and template-backed
selectedChartBundle drive the recommendation unless the user already supplied explicit DOMAIN_OVERRIDE or MUST_HAVE constraints. When a known template family is selected or detected, Phase 2/3 must preserve that template's chart composition, layer structure, palette anchors, and technique refs before making data-specific adaptations.
Execution Flow
COMPACT DIRECTIVE: The phase currently marked in_progress in TodoWrite is the active execution phase and must remain uncompressed. If a sentinel survives but the detailed protocol does not, re-read that phase file before continuing.
Phase Reference Documents (read on-demand):
| Phase | Document | Purpose | Compact |
|---|
| 1 | phases/01-data-detect.md | Data ingestion, semantic role mapping, domain inference | TodoWrite driven |
| 2 | phases/02-recommend-stats.md | Chart taxonomy selection, stats, panel blueprint | TodoWrite driven + sentinel |
| 3 | phases/03-code-gen-style.md | Journal profiles, palette system, code generation, composition | TodoWrite driven + sentinel |
| 4 | phases/04-export-report.md | Export bundle, source data, metadata, reporting | TodoWrite driven |
| 5* | phases/05-template-distill.md | Optional article-code distillation and runtime promotion | TodoWrite driven + sentinel |
Reference Specs (read on-demand when needed):
Compact Rules:
TodoWrite in_progress -> preserve full content
TodoWrite completed -> safe to compress to summary
- If a sentinel remains without the full step protocol ->
Read("phases/0N-xxx.md") before continuing
Agent Delegation Policy
Do not spawn any Agent before the data-status, file-path, and mode gates are complete. After those gates, delegate only when complexity or risk justifies the overhead, and keep agent work read-only unless the coordinator explicitly asks for a rewritten artifact.
| Agent | Phase | Trigger | Output |
|---|
data-profile-auditor | 1 | structure=="matrix", missing_rate>10%, no group/value roles, survival/dose-response with missing roles, n_groups>10 | dataProfile.audit |
chart-stats-planner | 2 | inferential claims requested, custom domain, n_groups>=6, survival or dose-response charts | chartPlan.delegationReports.stats |
panel-layout-auditor | 2 | panel_count>2, shared legend/colorbar, labels>24 chars, n_groups>=8 | chartPlan.delegationReports.layout |
palette-journal-auditor | 2 | n_categories>=8, domain semantic colors, grayscale-safe request, journal submission | chartPlan.delegationReports.palette |
scientific-color-harmony | 2 | after build_palette_plan | chartPlan.delegationReports.color_harmony |
layout-aesthetics | 2 | after build_panel_blueprint | chartPlan.delegationReports.aesthetics |
content-richness | 2 | after build_visual_content_plan | chartPlan.delegationReports.content_richness |
code-reviewer | 3 | before Phase 3 completes | styledCode.codeReview |
rendered-qa | 4 | after code execution and before outputBundle | outputBundle.renderQa |
visual-impact-scorer | 4 | after rendered-qa | outputBundle.renderQa.impactScore |
Blocking agent findings must route back to the owning phase before advancing. Never bury them in final notes.
Core Rules
- Do not claim a Nature- or Cell-like figure solely from a palette; typography, spacing, line weight, and panel discipline must also match.
- Do not use bar charts to hide distributions when individual-level or cohort-level data can be shown.
- Do not mix unrelated semantic color mappings across panels of the same figure.
- Do not use rainbow colormaps unless the variable is cyclic and the legend explicitly justifies it.
- Keep all figure text editable, sans serif, and legible at final print size.
- Treat every legend as a figure-level layout element in final output, not as an axes annotation. When panels share group, color, marker, or line semantics, keep one shared rectangular-frame
fig.legend at the figure bottom center, outside every plotting area and below the panels, using 7 pt text, bbox_to_anchor=(0.5, 0.01), and a compact 0.06-0.10 bottom margin. Never use loc="best", in-axes, top-center, or outside-right legends for publication output.
- Every generated script must call
enforce_figure_legend_contract(...) immediately before the first savefig for each figure. Direct ax.legend(...) calls are temporary handle sources only; if the finalizer is missing, or if any axis legend remains after the finalizer, return to Phase 3.
- Do not hand-write replacement runtime helpers when the skill already provides them. Generated code must embed and execute the helper source from
phases/code-gen/helpers.py, so legendContractEnforced, layoutContractEnforced, overlap checks, and typography gates are real runtime results rather than local approximations.
- Use shared legends or shared colorbars when panels encode the same semantics.
- Multi-panel figures must have an explicit panel blueprint before code generation.
- Titles default to centered alignment. Use
add_panel_label(..., x=-0.06, y=1.08, fontsize=9) for A/B/C/D labels outside the data rectangle; do not use left-positioned titles as panel labels.
- Use ASCII-safe scientific labels (
Earth radii, Earth masses, log10, -) instead of fragile glyphs (⊕, subscript digits, em dashes) unless the user explicitly requires symbol typography and supplied fonts cover it.
- For implemented single-panel charts, increase Nature/Cell-style information density through data-derived summaries, in-plot explanatory labels, reference lines, callouts, insets, sample-size labels, metric tables, prediction diagnostics, marginal distributions, density-colored points, density halos, matrix labels, and effect-size context before adding new chart types.
- Treat
specs/template-visual-motifs.md as the grammar for learning from reference examples. Add motifs to visualContentPlan.templateMotifs and render them through existing generators/helpers; do not register a new chart key until a real generator exists and passes QA.
- When learning from
template/articles, promote reusable code into helpers.py, template_mining_helpers.py, or split generator files before expanding coordinator prose.
- Do not invent statistics for visual impact. Every p-value, AUC, effect size, threshold count, or fitted parameter must come from the supplied data or a documented upstream result.
- Prefer vector export and generate source-data friendly artifacts for quantitative panels.
- If domain inference is weak, fall back to general rules instead of overfitting to a guessed specialty; however, explicit AI/ML/computer-science signals (
model, algorithm, RF, XGBoost, SHAP, train/test metrics, AUC/F1/RMSE/R2, residuals) are strong domain evidence and must route to the computer_ai_ml template packages before biomedical defaults.
- If statistical assumptions are uncertain, downgrade to a conservative or descriptive choice and explain why.
- If rendered QA reports overlap, cross-panel title/table/text collision, colorbar overlap with any panel layout box, metric-table overlap with bar/rectangle data marks, negative axes text without a reserved slot, poster-scale font sizes, blank/tiny output, missing
legendContractEnforced, missing layoutContractEnforced, any remaining in-axes legend, too few visual enhancements, missing template/reference visual grammar motifs, missing in-plot explanatory labels, non-editable vector text, or missing formats, return to Phase 3 or Phase 2 before declaring completion.
- Use
specs/workflow-policies.md for thresholds and budgets; do not add new magic numbers in phase logic without naming the policy.
Input Processing
User input is normalized into:
FILE: /path/to/data.csv
EXTRAS: optional figure request or hypothesis
DOMAIN_OVERRIDE: optional explicit domain hint
MUST_HAVE: optional chart or panel requirements
Normalize a confirmed real-file reply into FILE: and carry that exact path into Phase 1 as {{FILE_PATH}}. If DOMAIN_OVERRIDE conflicts with detected cues, keep the user override but warn in Phase 2. All preference collection rules and card flow are defined in the "Interactive Preference Collection" section above and in specs/preference-collection.md.
Data Flow
Carry these canonical fields forward:
dataProfile: format, structure, columns, semanticRoles, domainHints, nGroups, nObservations, replicateInfo, riskFlags, panelCandidates, warnings, audit
chartPlan: primaryChart, secondaryCharts, statMethod, multipleComparison, annotations, panelBlueprint, crowdingPlan, visualContentPlan, templateMotifs, templateCasePlan, palettePlan, delegationReports, journalOverrides, rationale
styledCode: pythonCode, journalProfile, figureSpec, colorSystem, panelGeometry, statsReport, codeReview, seed
outputBundle: figures, code, statsReport, sourceData, panelManifest, requirements, metadata, renderQa
TodoWrite Pattern
Keep exactly one active phase. Expand the active phase into concrete sub-tasks, then collapse completed work back to a phase-level summary before starting the next phase.
Post-Phase Updates
- After Phase 1: If domain inference is high confidence, note the selected domain playbook and any field-specific warnings.
- After Phase 2: Freeze the chart vocabulary, panel blueprint, visual-content plan, and palette plan unless the user requests revision.
- After Phase 3: Validate syntax, imports, code-review findings, generator coverage, and layout consistency before export.
- After Phase 4: Summarize render QA and how the user can iterate without re-running Phase 1.
Error Handling
- Ambiguous domain -> fall back to
General biomedical, present the top alternatives in rationale.
- Unsupported chart request -> map to the closest supported family and explain the substitution.
- Overcrowded multi-panel plan -> reduce to fewer panels or use a hero-plus-support recipe.
- Palette collision -> fall back to journal-safe muted palette with grayscale-safe accents.
- Weak statistical support -> switch to descriptive mode or a more conservative test.
- Rendered QA failure -> return to Phase 3 for layout/style/code or Phase 2 for an overpacked plan.
Coordinator Checklist
- Confirm a readable input file exists. If the path is missing, use a data-status card, a data-path card, validate the file suffix, and then use a separate mode card before Phase 1.
- Never scan the workspace for candidate data files unless the user explicitly asked for that behavior.
- If the confirmed path is a directory or lacks a supported file suffix, ask again and stop there.
- Never run Bash, Read, Grep, or directory inspection before the file-path gate and mode gate are both complete.
- If any card answer is a language-switch request or otherwise invalid, re-ask the same card and do not consume it as a decision.
- Collect
workflowPreferences with tool-compatible AskUserQuestion rounds before any phase dispatch, preserving the final 4-question visual preference card when synthetic data or interactive mode requires it.
- In auto mode, do not stop for extra style questions after defaults are set unless the user explicitly asks to switch to interactive refinement.
- Read chart/domain/journal references only when needed.
- Keep the active phase marked
in_progress.
- Before Phase 3, ensure the panel blueprint and palette plan are explicit.
- Before Phase 3, resolve blocking chart/stat/layout/palette agent findings.
- Before Phase 4, ensure code generation includes source-data, render-QA, and metadata hooks.
- Before completion, require
renderQa.hardFail == false, legendContractEnforced == true, layoutContractEnforced == true, legendOutsidePlotArea == true, axisLegendRemainingCount == 0, layoutContractFailures == [], colorbarPanelOverlapCount == 0, colorbar reflow recorded in colorbarReflowCount when used, metricTableDataOverlapCount == 0, metric-table relocation/suppression/fallback counts recorded when used, legendModeUsed in ["bottom_center", "none"], exactly one rectangular-frame shared bottom-center legend when a legend exists, centered title normalization recorded, ASCII text replacement recorded, and enough data-derived visual content to satisfy visualContentPlan.minTotalEnhancements and visualContentPlan.minInPlotLabelsPerFigure.
Related Commands
/spec-add learning ... to record new plotting or domain edge cases.
/spec-add arch ... when the eventual runtime package introduces permanent APIs.