Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Designs and maintains semantic groupings and readable layouts on the filmmaking canvas — scenes, character-reference sets, act beats, and other titled visual frames. Use when nodes on the canvas cluster around a shared meaning and would read more clearly if arranged together and wrapped in a frame. Don't force it — groups are a view concern, not an organizing tax.
.claude/skills/utopai-research-groups-compose/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-20 | ✗→✓ | ▲ Improved | -2% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -19% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 241% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 9% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 45% | 0% |
Skip if fewer than 3 members or the tie is just generation order.
canvas_layout.js to write member positions and groupFrames in one atomic sidecar update.workflow.json; never put x / y into node data.x, y, width, height) is a bounding box computed from the final member positions.frameId: frame_<unix_ms>. Titles ≤30 chars recommended.Pick the one that fits. Grouping is current canvas state; read workflow.json per the project PROJECT_AGENT.md § "Choosing context" to verify ids.
Scene <N> — <location or beat>.A character card + its reference images.
<Character name> — references.image_result nodes depicting the same character.Act <N> or beat name../workflow.json + ./canvas_positions.json. workflow.json gives you node ids + labels + subtypes; canvas_positions.json gives you each node's x / y AND the existing groupFrames map. Reads are unrestricted; writes go through canvas_layout.js.nodes.groupFrames.upsert with memberIds minus the evictee.groupFrames.delete.note: 280 × 420 (width hardcoded; height = NOTE_CARD_FALLBACK_HEIGHT for first paint)image_result: 290 × 220 (16:9 default; if data.metadata.aspect_ratio is present, scale accordingly)video_result: 290 × 220 (same caveat; check data.aspect or data.metadata.aspect_ratio)audio_result: 240 × 64pending / pending_generation / pending_attachment: 260 × 200If measured heights appear in canvas_positions.json, prefer them. minX = min(node.x for each member) minY = min(node.y for each member) maxX = max(node.x + node.w for each member) maxY = max(node.y + node.h for each member) x = minX - 24 y = minY - 24 width = (maxX - minX) + 48 height = (maxY - minY) + 48
node "$PAI_REPO_ROOT/server/cli/canvas_layout.js" \ --layout-json '{"positions":{"note_2":{"x":120,"y":80},"image_3":{"x":440,"y":80},"video_1":{"x":760,"y":80}},"groupFrames":{"upsert":{"frame_1716579123456":{"memberIds":["note_2","image_3","video_1"],"x":96,"y":56,"width":978,"height":468,"hue":200,"title":"Scene 1 — Causeway"}},"delete":[]}}'
memberIds, recomputed bbox.canvas_positions.json directly. Use canvas_layout.js so positions and frames apply together.addGroup, updateGroup, deleteGroup).memberIds).| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-20 | fail→pass | 11,063 | 1,245 | -89% | 1 | 1 | 0% | 1,827 | 1,788 | -2% | 0 | 0 | — |
case-01 | fail→fail | 12,627 | 5,473 | -57% | 1 | 1 | 0% | 2,599 | 1,982 | -24% | 0 | 0 | — |
case-02 | fail→fail | 4,607 | 6,909 | +50% | 1 | 1 | 0% | 709 | 1,938 | +173% | 0 | 0 | — |
case-03 | fail→fail | 7,113 | 4,624 | -35% | 1 | 1 | 0% | 1,312 | 1,839 | +40% | 0 | 0 | — |
case-04 | pass→pass | 4,704 | 2,570 | -45% | 1 | 1 | 0% | 783 | 2,053 | +162% | 0 | 0 | — |
case-05 | pass→pass | 9,295 | 6,873 | -26% | 1 | 1 | 0% | 1,919 | 2,684 | +40% | 0 | 0 | — |
case-06 | fail→pass | 13,208 | 2,624 | -80% | 1 | 1 | 0% | 2,472 | 1,994 | -19% | 0 | 0 | — |
case-07 | fail→pass | 3,002 | 2,810 | -6% | 1 | 1 | 0% | 639 | 2,179 | +241% | 0 | 0 | — |
case-08 | fail→pass | 9,542 | 1,795 | -81% | 1 | 1 | 0% | 1,713 | 1,866 | +9% | 0 | 0 | — |
case-09 | fail→pass | 9,023 | 2,505 | -72% | 1 | 1 | 0% | 1,422 | 2,059 | +45% | 0 | 0 | — |
case-10 | fail→pass | 5,565 | 3,619 | -35% | 1 | 1 | 0% | 1,081 | 2,377 | +120% | 0 | 0 | — |
case-11 | fail→pass | 3,812 | 11,646 | +206% | 1 | 1 | 0% | 590 | 3,903 | +562% | 0 | 0 | — |
case-12 | fail→pass | 10,645 | 2,976 | -72% | 1 | 1 | 0% | 1,759 | 2,205 | +25% | 0 | 0 | — |
case-13 | fail→pass | 12,605 | 3,882 | -69% | 1 | 1 | 0% | 2,024 | 2,298 | +14% | 0 | 0 | — |
case-14 | pass→pass | 12,371 | 4,597 | -63% | 1 | 1 | 0% | 2,118 | 2,315 | +9% | 0 | 0 | — |
case-21 | pass→pass | 4,767 | 2,335 | -51% | 1 | 1 | 0% | 848 | 1,996 | +135% | 0 | 0 | — |
case-15 | pass→pass | 10,360 | 1,807 | -83% | 1 | 1 | 0% | 1,626 | 1,875 | +15% | 0 | 0 | — |
case-16 | pass→fail | 5,870 | 3,550 | -40% | 1 | 1 | 0% | 877 | 2,231 | +154% | 0 | 0 | — |
case-17 | pass→pass | 30,024 | 7,929 | -74% | 1 | 1 | 0% | 2,253 | 3,186 | +41% | 0 | 0 | — |
case-18 | fail→pass | 8,123 | 1,701 | -79% | 1 | 1 | 0% | 1,170 | 1,819 | +55% | 0 | 0 | — |
case-19 | fail→pass | 12,789 | 2,749 | -79% | 1 | 1 | 0% | 1,933 | 2,129 | +10% | 0 | 0 | — |
case-22 | fail→pass | 19,677 | 4,549 | -77% | 1 | 1 | 0% | 878 | 2,475 | +182% | 0 | 0 | — |
case-23 | fail→pass | 9,903 | 1,436 | -85% | 1 | 1 | 0% | 1,697 | 1,802 | +6% | 0 | 0 | — |
case-24 | fail→pass | 4,234 | 1,895 | -55% | 1 | 1 | 0% | 702 | 1,926 | +174% | 0 | 0 | — |
case-25 | fail→fail | 8,482 | 1,798 | -79% | 1 | 1 | 0% | 1,424 | 1,843 | +29% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted, and 21 counted toward the lift figure. The other 4 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +52 percentage points is the difference between those two pass rates over the 21 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.