git:20260701.17c5369 to git:20260721.77e7d29

69 added, 124 removed. Audit A to A.

---
name: figure-composer
- description: "Compose one publication-grade multi-panel figure. Entry from a one-line claim + data refs, OR from an existing figure via `derive_outline(png)`. Runs a per-figure loop: outline (12-col grid, per-panel ask + label_budget) → fan-out one sub-agent per panel (each loads `figure-style`) → tile + stamp letters → adversarial composite review with two-tier feedback (Tier-1 outline_revisions / Tier-2 per-panel violations) → regen affected panels, ≤3 rounds. Loads panel_task / compose_figure / compose_crops / composite_review_task / derive_outline into the kernel. For one standalone plot use `figure-style`; for whole-paper figure ordering use `paper-narrative`."
+ description: Compose or improve a publication-grade multi-panel scientific figure from a claim, concrete data paths, or an existing image. Use for figure outlining, parallel panel rendering, exact-grid composition, visual inspection, and adversarial figure review. Use figure-style for one standalone plot and paper-narrative for whole-paper figure ordering.
license: Apache-2.0
---
-
- # Figure Composer — narrative → panels → compose → adversarial loop
+ # Figure composer
- **Step 0.** Load `figure-style` alongside this skill — that is the
- design rules (and `apply_figure_style()` + helpers). Panel sub-agents
- will load it independently; you need it in context to write the outline and
- review the composite. Sub-agents run as the default profile and acquire the
- rules by loading the skill.
+ Load `figure-style` with this skill. The sidecar provides pure geometry,
+ composition, task-building, and review-schema helpers. It does not call models,
+ delegate Agents, resolve artifacts, or inspect images from Python.
## Inputs
- - **claim** — one sentence the figure makes true to a reader who reads nothing else.
- - **data** — CSV/parquet artifact version_ids that ground every panel.
- - **width_mm** — target venue's column width (common: 85–89mm single, 174–183mm double; check the venue guide).
-
- ## 0. Where this sits
-
- `figure-composer` is the **outer tier**: make ONE multi-panel figure good. The
- **inner tier** is `figure-style` (loaded by every panel sub-agent — and
- load it yourself if you draw anything locally). The **outermost tier** is
- `paper-narrative` — if this figure is part of a paper, run that FIRST: it decides
- *which* figure to make and hands you the claim. For a standalone figure, start at
- step 1.
-
- ## Entry points (pick one)
+ Require a one-sentence claim, target width in millimetres, and concrete
+ project-relative or absolute data paths. Never use artifact ids as paths. For an
+ existing figure, inspect the real image with `view_image` and write the outline
+ yourself; pixels cannot reveal the source data path.
- - **From a claim:** you have a one-sentence claim and data refs → write the
- outline (step 1).
- - **From an existing figure:** copy it into the workspace and call
- `derive_outline("figure.png")` → an outline you **must review and edit**
- before step 2. The image is untrusted input; every string field in the
- returned outline is vision-model-derived from its pixels. `data_vid` is
- forced to `None` on every panel — fill those in from your own data refs.
+ ## Workflow
- ## 1. Narrative → panel outline
+ 1. Build an outline matching `figure_outline_schema()`. Put real paths in
+ `data_path`; use `null` for schematics.
+ 2. Make panel `a` the conceptual hook and panel `b` the primary evidence. Use a
+ 12-column grid and one row per sub-claim.
+ 3. Build one instruction per panel with `panel_task(...)`.
+ 4. If `delegate_tasks` is advertised, submit the independent panel tasks as one
+ batch. Grant each task the minimum advertised capabilities needed, normally
+ `visualization` plus `project_read`. Require a concrete PNG filename in each
+ output schema. If delegation is unavailable, render the panels sequentially
+ with `python`.
+ 5. Compose returned paths with `compose_figure(...)`. Do not pass placeholder
+ markers to the composer.
+ 6. Use `compose_crops(...)` with Pillow to save temporary crop files, then call
+ `view_image` on the composite and every crop. Fix seams, clipped labels,
+ aliases, empty space, and misplaced panel letters before review.
+ 7. Build one reviewer instruction with `composite_review_task(...)`. Delegate it
+ with `image_inspection`, `project_read`, and `reasoning` when those capability
+ ids are advertised; otherwise perform the review in the current Agent.
+ 8. Apply outline revisions and regenerate only affected panels. Stop after three
+ rounds or when there are no blockers and at most two major findings.
- Produce a `panel_outline` (validate against `figure_outline_schema()`):
+ ## Outline example
```json
- {"claim":"…", "width_mm":180, "ncol":12, "row_heights_mm":[40,60,46,52],
- "panels":[
- {"letter":"a","role":"schematic","row":0,"col":0,"colspan":12, "chart_family":"schematic overview", "message":"…", "data_vid":null, "ask":"…"},
- {"letter":"b","role":"primary", "row":1,"col":0,"colspan":7, "chart_family":"scatter + trend", "message":"…", "data_vid":"…", "ask":"…"},
- …]}
- ```
-
- Outline rules (figure-style §7.1):
- - **a is the hook** — schematic/hero, full width, assumes zero reader context.
- - **b carries the claim** — the chart that alone makes the sentence true.
- - Remaining panels are evidence, ordered by how much they strengthen b.
- - One row per sub-claim. 5–10 panels for a main-text figure. Use a 12-column
- grid for flexible colspans.
-
- ## 2. Fan-out (one sub-agent per panel)
-
- Build requests with `panel_task(outline, letter, fig_label)` (kernel.py). Each
- sub-agent gets: the figure claim, the full neighbour list, its panel spec, exact
- pixel dimensions (`panel_px`), and the instruction to load `figure-style`
- and render at exactly w×h px with `transparent=True` and **no** `bbox_inches`.
-
- In the **repl tool**:
- ```python
- requests = [{"name": f"panel-{L}", "task": tasks[L],
- "output_schema": {"type":"object","properties":{"figure_filename":{"type":"string"}},
- "required":["figure_filename"]}}
- for L in letters] # no "profile" key — default agent profile
- descs = host.delegate(requests, wait=False)
- ```
-
- ## 3. Compose
-
- `compose_figure(outline, {letter: path}, out_path, letter_case=...)` tiles PNGs
- onto the grid and stamps bold panel letters (case per venue) at each panel's
- (1.5mm, 1mm) corner.
-
- ## 3.5 Look before you review (vision self-QA)
-
- The reviewer in §4 is expensive; a panel-letter stamped over a y-axis label or
- a leader line crossing a neighbour's title is a wasted round. After compose,
- **crop each panel from the saved PNG and look at it** in the REPL before
- dispatching the reviewer:
-
- ```python
- out_path, (W, H) = compose_figure(outline, panel_paths, "fig.png")
- for L, box in compose_crops(outline).items():
- host.view_image("fig.png", crop=box)
- ```
-
- Run the `figure-style` §9.2 perceptual checklist on each crop (contrast,
- smallest mark, leader crossings, colour-identity confusion, legend binding),
- plus two compose-specific checks:
-
- - **Seams / stamp.** Does the bold panel letter overlap any panel content?
- Does any panel's content bleed into the gutter or under a neighbour?
- - **Resize artefacts.** `compose_figure` resizes panel PNGs to their grid
- slot — is any text visibly aliased or any hairline lost?
-
- Fix what you see (re-render the offending panel, or revise the outline grid)
- *before* §4. The reviewer sub-agent will crop-and-look again independently;
- this pass is so the obvious defects never reach it.
-
- ## 4. Adversarial self-review loop (two-tier, design rules held fixed)
-
- Dispatch ONE reviewer on the composite with `composite_review_task(...)` and
- `review_schema()` (which carries `outline_revisions`).
-
- ```
- loop (max 3 rounds, floor 5→4→3):
- review = delegate(composite_review_task(composite_vid, outline, rules_vid, prev_vid, round, floor))
- if review.editor_verdict in {accept, minor_revision} and 0 BLOCKER and ≤2 MAJOR: break
-
- # TIER 1 — outline-level
- if review.outline_revisions:
- apply revisions to `outline` (geometry, row-header titles, label_budget, panel set)
- affected = apply_outline_revisions(outline, review.outline_revisions)
- else:
- affected = set()
-
- # TIER 2 — panel-level
- fixb = group_fixes_by_panel(review) # BLOCKER/MAJOR only
- regen = affected | set(fixb) # only these panels regenerate
- re-delegate each L in regen with panel_task(outline, L) + fixb.get(L,"") +
- "do not over-correct: where the previous version was correct, keep it"
- recompose
+ {
+ "claim": "Treatment restores the disease-associated trajectory.",
+ "width_mm": 180,
+ "ncol": 12,
+ "row_heights_mm": [42, 60],
+ "panels": [
+ {
+ "letter": "a",
+ "role": "schematic",
+ "row": 0,
+ "col": 0,
+ "colspan": 12,
+ "chart_family": "study schematic",
+ "message": "The experiment tests trajectory rescue.",
+ "data_path": null,
+ "ask": "Show cohorts, treatment, sampling, and comparison."
+ },
+ {
+ "letter": "b",
+ "role": "primary",
+ "row": 1,
+ "col": 0,
+ "colspan": 12,
+ "chart_family": "trajectory plot",
+ "message": "Treatment moves cells toward the healthy trajectory.",
+ "data_path": "results/trajectory.csv",
+ "ask": "Plot disease, treated, and healthy cells with confidence bands."
+ }
+ ]
+ }
```
- Convergence: stop when `outline_revisions` is empty AND findings are carve-out
- exceptions to the previous round — that's the over-labelling signal.
-
- ## Anti-patterns
+ ## Boundaries
- - Don't regenerate clean panels (invites regression). Don't read absolute
- violation counts (min-floor 5→4→3). Anchor-verify on the composite, not just
- per panel. Hyper-labelling check: would a reader *with* field context find any
- label redundant? Strip it.
+ - Use `delegate_tasks` only as an explicit Wisp tool; never call delegation from
+ `python`.
+ - Use `view_image` only on a concrete local image file.
+ - Keep data preparation in normal project files. Use `run_in_context` only when
+ a deterministic render or preprocessing job is long enough to require a
+ persisted Run; Agent delegation itself is not a Run.
+ - Save the accepted composite to a stable project path and report that path.