t1k:cocos:playable:mechanic-split
| Field | Value |
|---|---|
| Module | playable |
| Version | 2.14.4 |
| Effort | high |
| Tools | — |
Keywords: by-type layout, mechanic split, modularize gameplay, portable mechanic, refactor folder, reusable module, uuid safe move, vertical slice
How to invoke
Section titled “How to invoke”/t1k:cocos:playable:mechanic-split[<cocos-root>] [--audit-only] [--resume <phase-file>] [--mechanics-from <manifest>]Mechanic Split
Section titled “Mechanic Split”Turns assets/scripts/<GameName>/ from a by-type layout (core/, ui/,
managers/, effects/) into vertical mechanic folders — grid-board/,
block-drag/, line-clear/, goals/ — each self-contained enough to copy into a
different game. The library grows as folders inside the game root: plain git mv,
no CPM tooling.
Scope is in-place split only — no package extraction, no publishing, no wiring
a mechanic into another project (that is the t1k-cocos-playable-modularize
family’s job). <GameName> differs per project and Phase 0 detects it; never
hardcode it, and never leave it hardcoded in an import.
When to use
Section titled “When to use”Gameplay code is grouped by type, a second project wants one of its mechanics, or a file has grown past ~400 lines holding several unrelated concerns.
Two entry points — standalone and delegated
Section titled “Two entry points — standalone and delegated”This skill runs from two different callers, and which one you are changes how the mechanic list is decided:
- Standalone (unchanged): Phases 0-3 derive the mechanic list from the
coupling matrix and
references/decision-rubric.md. Self-classified, per the workflow above. - Delegated from
t1k-cocos-playable-modularizeTier 2.6 (--mechanics-from <manifest>): the mechanic list is the design doc’s module table, read from the manifest (modules[].name+modules[].files). Phases 0-2 (detect root, inventory, coupling/blocker audit) still run — their output is what makes the move safe, and the blocker scan is still needed — but Phase 3’s own classification is SKIPPED. Phase 4 onward is unchanged.
Why: re-deriving boundaries inside a pipeline that already has a design-doc module
map produces two competing module maps decided by different methods. The design
doc is the authority there; t1k-cocos-playable-modularize decides boundaries,
this skill executes them in place.
Failure mode, stated plainly: if --mechanics-from names a manifest that is
absent or unreadable, or its modules[] is empty, STOP and say so. Never fall
back silently to self-classification — a silent fallback is indistinguishable
from the delegated path having worked
(rules/negative-result-scope.md; rules/errors-over-silent-fallbacks in
development-principles.md).
Non-negotiable: component identity is uuid-derived
Section titled “Non-negotiable: component identity is uuid-derived”Every .ts has a sibling .ts.meta carrying a uuid. Scenes and prefabs reference
components by a compressed cid derived from that uuid — not by class name, not
by path. A mid-sized playable typically carries a few dozen scene/prefab files and
a few dozen distinct script cids; count yours in Phase 1 rather than assuming.
So a split is a move, never an add-then-delete. git mv the .ts and its
.ts.meta together and refs survive; drop the .meta and the editor mints a fresh
uuid, turning every reference into a silent Missing Script.
Snapshot cid counts before each move and diff after. A non-empty diff means
git checkout — never repair it in the editor, which mints more new uuids.
Commands and the full table: references/move-safety.md.
Workflow
Section titled “Workflow”Two stages with a mandatory human review between them. Phases 0-3 are read-only and always run; Phase 4 never starts in the same turn that presents the plan.
| Phase | Does | Writes |
|---|---|---|
| 0 | Detect the game root under assets/scripts/ | — |
| 1 | Inventory files, LOC, orphan metas | — |
| 2 | Coupling matrix, shared kernel, external imports, blockers | audit/*.md |
| 3 | Classify mechanics, order leaf-first, resolve decisions, write the plan | plan.md, phase-*.md |
| ⛔ | REVIEW GATE — stop and hand the plan to the user | — |
| 4 | Move one mechanic: git mv, rewrite imports, barrel + README, verify, commit | source |
| 5 | Reuse verification per mechanic | README.md |
Phase detail and stop conditions: references/split-workflow.md.
Plan layout and what each phase file must carry: references/plan-artifact.md.
Phases 0-3 are read-only and token-heavy — delegate them to ONE background
t1k-code-reviewer, never a new agent and never a fan-out. The gate stays in the
main session (a background agent has no user to answer it). Phase 4 goes to a
FOREGROUND sub-agent, one mechanic at a time; background only for an explicit
unattended batch. Brief template and both cases: references/agent-brief.md.
The review gate
Section titled “The review gate”After Phase 3, present in chat: the mechanic table with coupling numbers and split order, the blockers by file:line, exactly what phase 1 touches, and the decisions the audit already resolved on the user’s behalf, with rationale — flag any to reverse. Phase 3 never defers a structural decision to this gate or to Phase 4 (rubric: references/decision-rubric.md). Then AskUserQuestion — three options, never assumed:
| Option | Then |
|---|---|
| Cook now | Run Phase 4 for the first unchecked phase, in this session |
| Hand back a resume command | Print the copy-pasteable line, stop, change nothing |
| Revise the plan | Take the correction, rewrite the plan, gate again |
The resume line names a real file, so a fresh session needs no memory of this one: /t1k:cook plans/<plan-dir>/phase-02-<mechanic>.md. Each phase file is self-sufficient by construction — “as discussed above” and a BLOCKED/“do not guess” heading are the same defect: the next session has no above, and no one to resolve the surfaced decision either.
After each phase lands, tick its checkbox in plan.md and advance the Resume
line in the same commit as the move.
What counts as a mechanic
Section titled “What counts as a mechanic”A vertical slice another game could adopt. Not a layer, not a single class. All
four must hold: nameable without the game (line-clear, not <GameName>Stuff) ·
deleting the folder removes exactly one capability · it owns its own signals and
data · its dependencies fit in a short table.
Folder shape, the barrel-import rule, and the required README.md contract:
references/mechanic-shape.md.
Audit the kernel before believing the coupling matrix
Section titled “Audit the kernel before believing the coupling matrix”The files everyone imports — Enums.ts, Interfaces.ts, GameSignals.ts —
routinely mix generic types with game-specific ones, making every mechanic look
more coupled than it is. Split contaminated kernel files first. Watch too for
one game-specific concern threaded through generic classes; cutting it is usually
the highest-leverage single edit. Numbers: references/worked-example.md.
Four blockers that compile fine and destroy reuse
Section titled “Four blockers that compile fine and destroy reuse”| Blocker | Why it kills reuse |
|---|---|
db://assets/scripts/<GameName>/... self-import | Bakes the folder name in; breaks on copy |
| Import from the game root barrel | Circular; pulls the whole module graph |
Import escaping assets/ (4+ ../) | Not part of the build; usually dead auto-import |
| Deep cross-mechanic import past the barrel | Contract stops describing real dependencies |
Scans and fixes: references/portability-blockers.md.
Verification — a compiler gate exists, and it is not the whole story
Section titled “Verification — a compiler gate exists, and it is not the whole story”Despite the common claim that a Cocos project has no CLI build, the editor ships
its own tsc and the generated config already maps db://assets/*.
~/.claude/skills/t1k-cocos-playable-mechanic-split/scripts/tsc-gate.sh <cocos-root>
compiles assets/ in seconds, is proven to go red on a broken import, and refuses
to pass if it checked zero files. Run it after every mechanic move, beside the cid
diff. The path is anchored at the installed skill, not at your Cocos project —
the script ships with this skill, so a project-relative scripts/tsc-gate.sh will
not resolve.
It still does not prove reuse: everything compiles because everything else is
still there. That test is copying <mechanic>/ + _shared/ into an empty folder —
does every import resolve inside the copy or appear in the README’s Depends on
table? Anything else is undeclared coupling: record it, do not delete it to pass.
Likewise, “uses SignalBus” is not “decoupled”. Count signal fires against direct singleton calls — the direct calls are the actual coupling.
Gotchas
Section titled “Gotchas”- Editor open during a move races the asset importer into regenerating metas.
Close it before Phase 4, and never delete a
.tswithout the pre-delete reference check. Commit each move by explicit pathspec —git add -Asweeps in the editor’s own regenerated files and makes the cid diff unattributable. - Batching mechanics into one commit makes a broken cid diff unattributable.
- Scaffolding empty subfolders — create
logic/,signals/only when non-empty. - Renaming classes during the split. Scene refs survive (cid is uuid-derived) but every importer breaks. Move first, rename later, separate commits.
- Inline lambdas in
signalBus.subscribe—unsubscribe()usesindexOf(), so a lambda never unsubscribes. Named arrow-function class fields only. - A mechanic depending on 4+ others is not a mechanic — report it as a boundary decision instead of moving it.
- An agent that returns a summary but writes no plan files has failed; re-spawn it rather than reconstructing the plan from its message.
Related
Section titled “Related”references/worked-example.md — one real Phase 1-3 run with its coupling numbers ·
references/decision-rubric.md — the tie-breaker rubric and decisions.md format ·
t1k-cocos-playable-modularize — the primary entry point for a project already
in the modularize pipeline, and this skill’s caller via Tier 2.6; the peer family
this skill also defers packaging and publishing to · t1k-cocos-playable-signalbus · t1k-cocos-playable-gameflow ·
t1k-cocos-base-code-conventions