Skip to content

t1k:cocos:playable:parameter

FieldValue
Moduleplayable
Version0.5.6
Efforthigh
Tools

Keywords: canvas-scan, config-scan, dashboard, dynamic-config, parameter, playable-config, playable-parameter, ui-composite

/t1k:cocos:playable:parameter
[--ui|--config|--all] [--deep] [--canvas <path>] [--path <folder>] [natural language]

Orchestrator for the flat-primitive parameter architecture.

Every ObjectParameter MUST contain ONLY primitive BaseParameter instances. No ComponentParameter subclass instances. No nested ObjectParameter. Max depth = 2 (ObjectParameter -> primitive). Doctor: tools/check-playable-config-depth.cjs.

Every other rule in this skill is a corollary of that one. If you must choose between this rule and any example below, the rule wins.

PlayableConfig is serialized to JSON for the dashboard. Nested ObjectParameter or ComponentParameter instances produce structures the dashboard cannot render or edit. Flattening at the config layer keeps dashboard JSON shallow; ComponentParameter reconstruction happens only at apply-time inside the controller.

The submodule db://assets/PlayableParamterTool/ owns the runtime. Skill points at it; do not duplicate.

Use asImports from
Primitives in PlayableConfig entriesparameter/BaseParameter: ColorParameter, NumberParameter, BooleanParameter, TextParameter, ImageParameter, SelectParameter, CoordinatesParameter, ObjectParameter
Apply-time component typesparameter/component-parameter/: SpriteComponentParameter, LabelComponentParameter, ButtonComponentParameter, TransformComponentParameter, UIOpacityComponentParameter, …
Apply functionsparameter/ParameterApply: applySpriteComponentParams, applyLabelComponentParams, applyButtonComponentParams, applyTransformComponentParams, applyUIOpacityComponentParams, …
Async trackingparameter/ParameterBinder: ParameterBinder (use only binder.waitForAsync() in v6)
SDK manager basebase/BaseParameterManager: extend in project’s ParameterManager
SDK selection / redirectsdk/voodoo: Vsdk (used for CTA redirect)
Config injectionconfig/PlayableConfigProvider: PlayableConfigProvider.initialize(PlayableConfig, CURRENT_SDK)

Forbidden inside PlayableConfig entries: any ComponentParameter subclass instance, any nested ObjectParameter. ComponentParameter classes are apply-time-only types.

LayerFlagApprovalUse Case
UI Layer--uiNone (auto)Visual components: Sprite, Label, Button, UIOpacity
Config Layer--configRequiredGameplay values: HP, damage, timing, speed
Both--allConfig onlyFull parameter pass

UI parameters are visual, safe to auto-apply. Config parameters affect gameplay balance, require human approval.

User IntentDelegate To
”Parameterize all UI components”t1k-cocos-playable-parameter-scan --mode ui
”Scan a specific Canvas node”t1k-cocos-playable-parameter-scan --mode ui --canvas <path>
”Extract config values for params”t1k-cocos-playable-parameter-scan --mode config
”Find magic numbers in gameplay code”t1k-cocos-playable-parameter-scan --mode config --deep
”Scan specific game folder for config”t1k-cocos-playable-parameter-scan --mode config --path <folder>
”Do full parameter pass”t1k-cocos-playable-parameter-scan --mode all
”What composite/type for this component?”t1k-cocos-playable-parameter-composite
”Generate PlayableConfig / wire parameters”t1k-cocos-playable-parameter-implement
”Assign scene nodes via MCP”t1k-cocos-playable-parameter-mcp

When invoked with flags, pass them to sub-skills. Never drop flags silently.

Flag PatternRoute ToBehavior
--mode ui, --uiscan skillAuto-delegate, no AskUserQuestion
--mode config, --configscan skillAuto-delegate, no AskUserQuestion
--mode all, --allscan skillAuto-delegate, no AskUserQuestion
--canvas <path>scan skillAppend to delegated args
--path <folder>scan skillAppend to delegated args
--deepscan skillAppend to delegated args
--autoall sub-skillsSkip approval gates

If args contain any flag: parse all flags, select the first step, pass them along — step-transition gates still fire. If args contain no flags: AskUserQuestion for entry-point chooser, then begin the gated sequence.

Delegates work as discrete steps. Gates EVERY transition with AskUserQuestion. Gates fire regardless of --auto / --all / --mode.

StepSub-SkillReport File
1 — Scan...-parameter-scanparam-step1-scan-{YYMMDD-HHMM}-{slug}.md
2 — Composite...-parameter-compositeparam-step2-composite-{YYMMDD-HHMM}-{slug}.md
3 — Implement...-parameter-implementparam-step3-implement-{YYMMDD-HHMM}-{slug}.md
4 — MCP Assign...-parameter-mcpparam-step4-mcp-{YYMMDD-HHMM}-{slug}.md

After invoking step N’s sub-skill:

  1. Write step report to plans/reports/param-step{N}-{YYMMDD-HHMM}-{slug}.md: step name, sub-skill invoked, flags, output summary, files created/modified, suggested next step + 1-2 alternatives, open questions.
  2. Print report path to chat.
  3. Suggest next step in one line.
  4. Call AskUserQuestion with exactly 4 options: Continue to N+1 (Recommended) / Review report first / Adjust step N / Abort workflow.
  5. Never skip the gate. Only --auto + --no-gates together may suppress gates.

Before invoking step N, read the step (N-1) report if present and pass its path as --from-report <path> to the next sub-skill.

--resume step{N} skips steps 1..N-1, loads the most recent matching report for step (N-1), starts gating from step N.

{slug} is derived from the user’s natural-language request, or auto if flag-only. Reuse the same slug across all step reports.

UI categories use the UILayer-{Screen} prefix where {Screen} names the logical UI grouping. Non-UI uses semantic prefixes (Gameplay-{X}, Audio, Sdk).

Why specific UILayer- prefix: dashboard sidebar can collapse by category; prefix groups all nodes from one logical screen while each node stays its own ObjectParameter entry. Generic names (UI, Combat) are too coarse.

Component-Prefix Rule for Inlined Primitives

Section titled “Component-Prefix Rule for Inlined Primitives”

When a node has multiple components, prefix each primitive with the lowercase component name. Single-field components (UIOpacityopacity) keep their field name only.

ComponentPrefixed primitive examples
cc.SpritespriteColor, spriteType, spriteFillType, spriteFillStart, spriteFillRange, spriteSizeMode, spriteTrim, spriteFrame
cc.LabellabelString, labelColor, labelFontSize, labelEnableOutline, labelOutlineColor, labelOutlineWidth
cc.UITransform / cc.NodetransformScale, transformPosition, transformRotation
cc.UIOpacityopacity (single field — no prefix)
cc.ButtonbuttonTransition, buttonZoomScale, buttonNormalColor, buttonPressedColor, buttonHoverColor, buttonDisabledColor
Custom widgetSemantic name without prefix (e.g., mainBarColor) when the field applies via method rather than direct component property

Project may add thin adapters to bridge the flat-primitive config to apply-time ComponentParameter types. These are project-local conventions, NOT mandated by the skill:

  • Factory shortcuts wrapping primitive constructors (e.g., color(label, def)new ColorParameter(...)). DRY for repetitive config.
  • Flat→Component adapters (e.g., spriteFromFlat(p)) building a SpriteComponentParameter from flat primitives so existing apply*ComponentParams can be reused.
  • Common-shape factories (e.g., makeLabel, makeButton) producing literal objects of flat primitives — never ComponentParameter instances.
  • bind<T>(entry: ObjectParameter<T>, applier: (p: T) => void) helper typed by the entry’s value — avoids string-keyed onUpdate registration, gives autocomplete on the params object.
  • Property containers with @property({ type, group }) Inspector grouping — keep scene wiring stable as parameter names evolve.

These all compose primitives — none of them violate the load-bearing rule.

  1. Flat-Node Rule (UI): 1 Cocos scene Node = 1 ObjectParameter. No exceptions.
  2. No ComponentParameter wrappers in dashboard JSON. Use prefixed primitives directly. ComponentParameter classes are apply-time only.
  3. No nested ObjectParameter. Max depth = 2. Doctor-checked.
  4. Component-prefix on multi-component nodes. Single-field components skip the prefix.
  5. UILayer-XXX category for UI nodes. Non-UI uses Gameplay-XXX, Audio, Sdk.
  6. Scene values as defaults. Use actual component property values, not generic placeholders.
  7. Typed @property in containers. @property(Sprite), @property(Label) — not @property(Node).
  8. Containers stay stable across refactors. Property containers keep their nested layout; bind methods route flat params to existing container fields.
  9. Full-field coverage by default (BOTH architectures). Expose EVERY field the node’s component(s) offer — never a hand-picked subset. Authoritative field list: references/field-coverage.md (flat-primitive path) and the component’s XxxConfig interface in db://assets/PlayableParamterTool/parameter/component-parameter/*ComponentParameter.ts (wrapper path). Flat-primitive → one prefixed primitive per field. ComponentParameter-wrapper → pass every XxxConfig key to the constructor. Narrow the set ONLY with code evidence the field is programmatically managed, or an explicit user note. A LabelComponentParameter emitted with only string/color/fontSize (dropping lineHeight, overflow, alignment, bold/italic/underline, outline, shadow, wrapText) is a DEFECT, not a default.
  • ComponentParameter wrapper instance in a PlayableConfig entry — forbidden; inline its fields directly as flat prefixed primitives.
  • Bundling multiple Cocos nodes under one ObjectParameter — each node becomes its own top-level entry.
  • Generic UI category names — UI, Combat. Always UILayer-{Screen}.
  • Missing component prefix on multi-component primitives — collision risk + machine-unreadable.
  • Inheritance for composites (e.g., Button IS-A Sprite) — wrong; flat primitives only.
  • Pre-filter nodes by name before checking components.
  • Generic defaults when scene values are available.
  • Skip UIOpacity or Widget detection.
  • Modify the submodule for project-specific types — never. Keep project additions in assets/scripts/parameter/.
  • references/code-templates.md — Generic v6 templates for PlayableConfig entries + controller wiring
  • references/type-hierarchy.md — v6 two-stage model (primitives at config layer, ComponentParameter at apply layer)
  • references/field-coverage.md — Component-by-component primitive field tables
  • references/scan-strategy.md — Canvas scan procedure
  • references/intent-detection.md — Natural-language intent → mode routing
  • references/discovery-helpers.md — Scan helpers (context, comparison, scene JSON)
  • references/mcp-assignment.md — MCP node→property assignment patterns
  • references/workflow-steps.md — End-to-end step procedure

Sub-skills:

  • t1k-cocos-playable-parameter-scan — Scene discovery
  • t1k-cocos-playable-parameter-composite — Type system + presets
  • t1k-cocos-playable-parameter-implement — Code generation
  • t1k-cocos-playable-parameter-mcp — Editor integration
  • Composite-based projects: follow project convention, not this rule. Some projects deliberately use the composite/wrapper architecture (ObjectParameter<SpriteParameter>, ObjectParameter<SpriteComponentParameter>, ParameterBinder fluent API, NodeComposites/PLAComposites/component-parameter wrapper classes). This is a valid, signed-off alternative shipped by the same submodule. When extending an existing composite project, match the project’s pattern — do NOT introduce flat-primitive entries alongside composites (SSOT/consistency hit). The flat-primitive rule below is the default for NEW projects only. Detection: if PlayableConfig.ts uses ObjectParameter<SpriteParameter>, ObjectParameter<…ComponentParameter>, or ParameterBinder, the project is composite/wrapper-based; ask user to confirm architecture before generating code.
  • Full-field coverage applies on the wrapper path too. When generating an ObjectParameter<XxxComponentParameter> (wrapper path), populate EVERY key of that component’s XxxConfig interface by default — not a hand-picked few. Open db://assets/PlayableParamterTool/parameter/component-parameter/XxxComponentParameter.ts, read the XxxConfig interface, and pass all keys (using scene values when MCP is connected). Example defect: a LabelComponentParameter config with only { string, color, fontSize } while the LabelConfig interface also offers lineHeight, overflow, horizontalAlign, verticalAlign, fontFamily, isBold, isItalic, isUnderline, enableOutline, outlineColor, outlineWidth, enableShadow, shadowColor, shadowOffset, enableWrapText. Include them all unless the user says otherwise. See Critical Rule 9.
  • Hard rule, doctor-checked: every ObjectParameter contains primitives only. No ComponentParameter instance, no nested ObjectParameter.
  • “1 Cocos Node = 1 ObjectParameter” for UI. Logical composites (Crosshair, HPBar, RewardCard) become N top-level entries — never bundle.
  • Category prefix = UILayer-{Screen} for UI. Generic UI or Combat is forbidden. Non-UI uses Gameplay-XXX, Audio, Sdk.
  • Containers do NOT change shape across refactors. Property containers keep their nested layout; bind methods bridge the new flat top-level ObjectParameter to existing container refs. Zero scene re-wiring.
  • Single-field components skip the prefix. UIOpacity.opacity is opacity, not uiOpacityOpacity.
  • Custom-widget semantic names skip prefix. When a primitive applies through a method (e.g. hpBar.setBarColor(...)) rather than to a single Cocos component property, use the widget’s semantic name.
  • Flags govern sub-skill behavior, NOT orchestration gates. Step transition gates ALWAYS fire.
  • Every step writes a report under plans/reports/param-step{N}-…md BEFORE the gate.
  • Gate uses AskUserQuestion (mandatory). Never substitute prose options. Load via ToolSearch(query="select:AskUserQuestion") if deferred.
  • Resume flag is SSOT. --resume step{N} reads the latest matching param-step{N-1}-…md. Do not re-run earlier steps.
  • Apply-time only: ComponentParameter subclasses (SpriteComponentParameter, etc.) appear only inside apply*ComponentParams calls — never as ObjectParameter values.