executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
scenario-maya-groomUse when grooming hair or fur in Maya with XGen (Interactive Groom splines or legacy descriptions): scalp hair, eyebrows, eyelashes, fur, guides, clumps, noise, parting, density maps, a fill layer, aiStandardHair shading, noisy or flickering hair renders, hair that must move (Linear Wire, nHair, Alembic caches), or a groom for Unreal (groom Alembic, cards).…
Independent GitHub snapshot · Automated classification · Original documentation governs
Expert grooming sets the flow on a few guides first, then a clump hierarchy with loose roots and varied profiles, then breakup only where the reference shows it (tips, strays), judged in close, side-lit renders with the real hair shader. Interactive Groom brushes have no stroke API, so the agent grooms with data (guides from a flow sheet wired into the stack, masks written as images, modifiers set by value) and judges with numbers from exported strands plus renders. Code: scripts/mx_groom.py (import mx_groom as G). If a sibling skill named here is missing from your available skills, ask the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.
REQUIRED BACKGROUND: scenario-maya-expert (execution channel, review loop, 2027 version traps).
Status (2026-09-24): every Maya call here is not yet run in Maya (2027 not installed). The pure layer of mx_groom passes offline (tests/code/maya-groom/test_mx_groom_offline.py). Names not quoted from the 2027 help resolve at run time from G.NAME_CANDIDATES.
G.CLUMP_PROFILES: spike, onion).G.clump_follow).Write the answers to brief.json before any node.
| Input | Changes | Default when silent |
|---|---|---|
| Target | Arnold strands, Unreal strands, or cards (MtoA renders no XGen Card primitives) | Arnold strands |
| Hair types | one description each: body, fill, fringe, strays, brows, upper and lower lashes, transition, fuzz. Close-up face: rule peach fuzz in or out (Hadi) | body + brows |
| Style | realistic: random clump points, Randomness > 0, melanin. Stylized: clumps on the guides (FlippedNormals), flat Base Color with Melanin 0 [added]. Both: a point on the defined-to-soft range, same silhouette (Fernandez 07) | realistic |
| Motion, and who owns it | static follow; keyed or simulated wires; rig or CFX department (Flood) or the groom file (2027 help) | follows the scalp |
| Still or animated | still likeness: grow on the head (Hadi). Animated or real time: scalp cap with clean UVs | scalp cap |
| Camera distance | Scattering Mode: Accurate for close-ups (10 to 30 % slower), Adaptive for mixed shots | Adaptive |
| Budget | spline count and GPU memory (xgmSplineQuery), Unreal sim guides | state a number |
| Reference | flow arrows and clump counts come from it | ask; else write the assumed look down |
| New or inherited | Interactive Groom for new work; legacy XGen only for an inherited asset or non-spline primitives | Interactive Groom |
Headless mayapy (mx_run --plugins mtoa,xgen,abc, in that order) covers guides, masks, Alembic, Unreal tags, lint, analysis and Arnold reviews; the GUI bridge covers the menu queries and whatever the probe shows does not evaluate headless (IG computes on the GPU). references/procedures.md P16 is the whole job as one script with every gate asserted (G.gate raises).
G.probe() headless and in the GUI. Menu actions with no documented command are read, not clicked: through the bridge, G.menu_recipe(name) queries the item's command string (cmds.menuItem(item, q=True, command=True)) and stores it; Attribute Editor buttons (Make Wires Dynamic, Reference State Update, Rebuild) go through G.button_recipe(name, node). Export Cache opens a dialog: write its @path@ template from G.recipe_source. GATE: probe JSON saved; recipe_status() shows the recipes the plan needs as recorded.G.scalp_check(scalp, for_unreal=...). GATE: no error: line, border edges present (the hairline), UVs unique.base, whorl, part, direction, length regions) and count big clumps and small per big. GATE: G.check_flow_sheet(flow) == [].g = G.make_guides(scalp, flow, spacing=..., cvs=8, collide=head), few first (FlippedNormals); symmetric=True only on symmetric topology (Hadi). GATE: G.gate(G.measure_curves(...)["verdict"]), penetration 0, discontinuities only at parts and whorls.G.create_description(scalp, "hair_body") per hair type (recipe, renamed). G.wire_guides(desc, g["curves"]): a Guide modifier low in the stack interpolates the guides, fed by a Curve to Spline on its inGuide; it holds the region maps parts need. A Curve to Spline on the description itself turns each curve into one hair and disables everything below it: only for exact strands or caches. GATE: G.wiring_verdict: flow-sheet error of the hair drops after wiring, and the InGuide_base cache holds every guide.G.build_clumps), strand Noise on a share, length breakup, Collision. G.write_hairline_mask for the hairline (band=True: transition), one map shared across descriptions (G.connect_texture), G.patches holes in the primary's mask for stray clumps. GATE: G.gate(G.lint()["lines"]); the coverage pass (showthrough %) and spline count within budget.G.hierarchy_from_states (3 to 5), G.breakup_report and G.measure_abc. Render Clump before and after Noise once each (2027 help). GATE: breakup_verdict and strand_verdict clean; a side-lit close-up passes references/critique.md, triangles and squares in the reference's proportion.G.hair_shader(desc, preset=...) (replaces the default hairPhysicalShader), G.curve_render(desc, "ribbon", min_pixel_width=...), G.cost_sweep over the four knobs with flicker. Save before rendering XGen. GATE: G.review(...) sheet judged with the critique, no shader warn:.| Value | Relative to | Source |
|---|---|---|
| no clump pull over the first ~14 % | strand length, clump profile | Fernandez 02 [00:17:56] |
| spike: 0.01 at mid length; onion: mid bulge ~0.9 | legacy Clump Scale ramp (radius reading) | Fernandez 02 [00:05:38], frames 00:06:00, 00:10:51 |
| 3 to 5 small clumps per big; 2 levels, 3 for long hair | clump hierarchy | Fernandez 03 [00:01:31], [00:09:11] |
| second clump density 7.5, clump noise 2, Cut random up to 10 | his real-time scalp asset | Giovannini [00:09:43] to [00:11:25] |
| strays 5 % (up to 20 %), noise 3 on strays, 0 on body | share of hairs | Giovannini [00:09:28], [00:11:59] |
| noise mask 0.1 to 0.5 (1 kills clumping) | legacy Noise modifier | FlippedNormals [00:32:05] |
| 20 CVs with noise; 24 to 36 for curls | modifier or base CV count | FlippedNormals [00:28:52]; 2027 help |
| random length 0.8 to 1.2; clump cut 0.2 | balanced around 1; small-clump level | Fernandez 08 [00:09:33], [00:15:34] |
| melanin 0.2 blonde, 0.5 red or brown, 1.0 black | Standard Hair | Arnold hair doc |
| IOR 1.55 (outside 1.4 to 1.6 only wet); Shift 0 to 10 deg, 2.3 to 3.7 measured, 0 synthetic | Standard Hair | Arnold hair doc |
| Unreal: Sub Steps 40, Iterations 10, Collision Radius 3.5, Bend Scale tip 0.1 | UE 5.3 era [estimate] | Giovannini [00:51:04] to [00:54:27] |
| Nucleus Space Scale 0.01 | cm scenes | maya-version-deltas 2.8 |
In code. G.lint() (plug-in order, stack, clump wiring and profiles, noise placement, budget, shader), G.measure_abc(cache)["verdict"] (root pinch, profile variety, small per big, strays, tip noise, frequency and fray, length balance, flow, penetration), G.wiring_verdict, G.breakup_verdict, G.unreal_verdict(G.inspect_abc(abc)). G.gate turns any of them into an assertion. Thresholds marked [added] are this skill's defaults; each line cites its rule.
Visual. G.review(hair, out, head=head, scalp=scalp, closeups=[...]) renders the real shader: beauty (dome plus a key along the groom, Schneider), backlit (strays, halo), silhouette, coverage. Viewport captures only after G.viewport_aa(True) (FlippedNormals, Fernandez). Judge with references/critique.md: flow, then clumps, then strands (Fernandez).
| Mistake | Looks like | Fix |
|---|---|---|
| Guides not wired, or through Curve to Spline on the description | hair ignores the flow sheet, or one hair per curve | G.wire_guides (Guide modifier), then wiring_verdict |
| Clumped roots | bald lines between clumps at the scalp | root-loose Clump Scale ramp (Fernandez) |
| One clump shape everywhere, all triangles | "CG" regularity | Radius Variance, profiles, square clumps (Fernandez 02, 05) |
| Mask and tightness judged on straight guides | two knobs seem to do the same | noise below the Clump, G.clump_follow (Fernandez 06) |
| Strand noise to break big clumps; noise on the body | small clumps dissolve into frizz | clump-level noise, strand noise on a share (Fernandez 07, Giovannini) |
| One noise frequency, fray everywhere | fake, uniform frizz | vary frequency; fray on few hairs (Fernandez 05) |
| Strays as a new system only | strays unrelated to the clumps | holes in the primary's mask (Fernandez 03) |
| Extending strands | criss-crossing inside clumps | scale or cut at the small-clump level (Fernandez 08) |
| Jagged clumps, bad placement | stepped clump edges | raise Map Subdivision Level on every level; new Seed (2027 help) |
| Secondary density at or below primary | secondary clumps leak across cells | duplicate the primary, raise density, Use Control Map (2027 help) |
| Clumps crossing a part | clumps straddle the part | Guide region map as clump control map, or separate descriptions |
| Cache without Multiple Transforms and Write Final Width | curves wrong in other apps | turn both on (2027 help, Flood); check Epic's opposite advice |
Default hairPhysicalShader kept | no indirect diffuse on the hair (Ai Kd Ind defaults to 0) | aiStandardHair, or raise Ai Kd Ind (2027 help) |
| Judging an aliased viewport | density and clumps misread | G.viewport_aa(True) first |
map1 for Unreal, frozen, no history; it passes G.scalp_check.-uvWrite, -stripNamespaces), and the wires cache when the rig owns the wires.<asset>_ue5.abc with groom_group_id, groom_guide (real guides, no fill) and groom_root_uv (UV set map1), a clean unreal_verdict on the reimport, and the Unreal start values (Numbers).hairPhysicalShader (Ai Kd Ind 0). Standard Hair gained scattering_mode in MtoA 5.6.0. Load MtoA before XGen.mx_groom ports. No Arnold GPU on macOS; Arnold RenderView replaces Maya IPR. Ctrl means Control, not Command.references/procedures.md: code for every stage, P5b clump tests, P16 the whole job. Load before writing groom code.references/critique.md: the rubric for judging a groom from renders and numbers. Load at every gate.references/expert-notes.md: principles by expert, disagreements with deciding conditions. Load when choosing between options.references/gui-paths.md: menus, editors, brushes, hotkeys (computer-use agent).references/sources.md: every source, credentials, URLs, best timestamps.