t1k:cocos:playable:pipeline
| Field | Value |
|---|---|
| Module | playable |
| Version | 2.14.4 |
| Effort | medium |
| Tools | — |
Keywords: board, build job, client project, deliverable, git repo, jenkins, log tasks, module, plane, playable pipeline, provision repo, task tree, tracking, work item
How to invoke
Section titled “How to invoke”/t1k:cocos:playable:pipeline<report-or-breakdown> [--client <name>] [--playable <name>] [--no-jenkins] [--provision-repo] [--dry-run]Cocos Playable — Plane + Jenkins Pipeline
Section titled “Cocos Playable — Plane + Jenkins Pipeline”One playable becomes one Plane module under its client’s project, holding the whole task
tree, plus one Jenkins job that builds it. This skill wires those two together from a source
document that already exists — an agreed task breakdown, or a
t1k:cocos:playable:preproduction estimate report.
Orchestrator only. Every Plane lifecycle mechanic — binding, state resolution by group,
guarded writes, the 4-hour leaf cap, degrade-never-block — is owned by core’s
skills/t1k-plane/references/workflow-enforcement.md and
skills/t1k-plane/references/binding.md. Every Jenkins
config.xml mechanic is owned by skills/t1k-jenkins-job/SKILL.md. This skill cites them and
adds only the three things neither can know: the studio hierarchy convention, the required-task set
for a playable, and the hand-off between the two systems.
Decision Tree
Section titled “Decision Tree”| Intent | Path |
|---|---|
| ”Log this estimate report to Plane” | Workflow steps 1-4, then --no-jenkins |
| ”Set up the full pipeline for this playable” | Workflow, all steps |
| ”Does this module have everything it needs?” | Step 4 alone — see task-template.md |
| ”Just the build job” | Skip to step 5, or call t1k:jenkins-job directly |
| ”Create the actual git repo for this playable” | Step 6 — repo-provisioning.md. Playable already has a repo? Skip step 6 |
| ”Why is the title format like that?” | hierarchy-contract.md |
Prerequisites
Section titled “Prerequisites”| Need | Check | If missing |
|---|---|---|
| Plane MCP | mcp__plane__* tools loaded | ~/.claude/skills/t1k-mcp-management/scripts/install-plane.sh — prompts for the key with hidden input, so the user runs it, then restart Claude Code |
| Jenkins creds | JENKINS_URL, JENKINS_USER, JENKINS_TOKEN in env | t1k:jenkins-job § Setup. Never pass as argv, never echo |
| A source document | An estimate report, or an agreed breakdown in-conversation | Run t1k:cocos:playable:preproduction first — do not invent a task list |
| Repo provisioning (optional step 6) | gh + git on PATH, gh auth status ok | repo-provisioning.md — parent-dir prompt required first run |
Plane absent is not fatal — core’s degrade rule applies unchanged: warn once and continue. A Jenkins job created with no Plane tree is still a valid partial outcome; say so explicitly rather than reporting success.
Delegation — the main session orchestrates, it does not do the work
Section titled “Delegation — the main session orchestrates, it does not do the work”This skill’s inputs are large (a 350-line estimate report) and its writes are numerous (30+ work
items, a Jenkins config.xml). Reading and performing all of that inline burns the main session’s
context on material it never needs again. Default to delegating every step below that is marked
fork; keep inline only what genuinely needs the conversation.
| Step | Where | Why |
|---|---|---|
| 1 Bind | inline | One status call plus a judgment about the project. Cheap. |
| 2 Read the source document | fork → Explore | The report is the single biggest context cost in the skill. Return the § 9 line-item table as JSON, not prose, and never the report body. |
| 3a Derive the tree | fork → Explore | Splitting rows to the 4h cap and reconciling against the report total is deterministic arithmetic over step 2’s JSON. |
| 3b The ONE confirmation | inline, always | Core requires the user see the whole tree before any write. A fork cannot ask. |
| 3c Write the tree | fork → t1k-fullstack-developer | Bulk API writes. See the MCP warning below — the fork must drive Plane’s REST API, not mcp__plane__*. |
| 4 Required-task check | fork → Explore | A set comparison against task-template.md. Read-only. |
| 5 Jenkins | fork → t1k-fullstack-developer | jenkins-job.cjs uses fetch, not MCP, so it works in a fork unchanged. Except the Discord thread ID — ask for that inline, before spawning. |
| 6 Provision repo (optional) | inline for the parent-dir prompt and any conflict/existing-repo stop; the run/plan call itself may be forked | Preflight, the parent-dir choice and merge-conflict handling are unresolved-decision points; the git/gh calls are deterministic. |
| 7 Link back | inline | One comment and one URL. |
Two separate savings, and they are not the same. Forking always protects the main session’s
context. Whether the fork also runs on a cheap model depends on transparent routing, and the two
floors below mean several obvious choices are not cheap. Do not promise a cost saving you have not
checked against t1k-config-mr.json.
Exploreis a read-only built-in with no spawn tool → routed viadefaultBuiltInModel. This is the genuinely cheap lane, and it is why steps 2, 3a and 4 are shaped as read-and-return-JSON.t1k-fullstack-developeris floored byDEVELOPER_SUFFIX_FLOOR(/-developer$/) and by declaring write tools, so it passes through to Anthropic at full rate. It still keeps the writes and their output out of the main session — that is the whole benefit; there is no model saving.
Explicit mr-delegate.sh is not needed here: the Task interceptor is the primary path and fires on
an ordinary Task spawn. Reach for the t1k-model-router-delegate skill (ships with
theonekit-model-router, not this kit) only to force a specific provider/model or to debug a
routing decision.
The hierarchy
Section titled “The hierarchy”workspace cocos <- the engine project <Client> <- the CLIENT (publisher / partner) module <Playable> <- the PLAYABLE (the deliverable) work item <Client> | <Playable> | <task> work item <Client> | <Playable> | <task>This is core’s documented deliverable convention (binding.md § “What each hierarchy level
means”), under which the module binding is REQUIRED, not optional — a repo bound to the project
alone is a mis-binding, because every item then lands on the whole client’s board with nothing
identifying which playable it belongs to.
The Client | Playable | Task title is a studio deviation from core’s [Discipline][Kind] Title
taxonomy. It is deliberate, it is recorded, and it has a cost. Read
hierarchy-contract.md before changing it.
Phase parents carry the prefix too — <Client> | <Playable> | First Build, not a bare
First Build. Both spellings exist on the board today; the prefixed one is the convention this
skill composes.
The separator changed from brackets to pipes on 2026-08-22. This skill used to compose
[Client][Playable] Task; the live board never did. A board now holds three forms — pipe items,
legacy [Client][Playable] items from the old composer, and unprefixed items. applyTo: "created-only" means none is ever renamed. Do not “fix” the old ones.
Workflow
Section titled “Workflow”Steps 2, 3a, 3c, 4 and 5 are forked by default — see Delegation for which agent and why. Doing them inline is the exception, and worth a sentence saying why.
-
Bind the repo.
node $HOME/.claude/scripts/t1k-plane-binding.cjs status. Unbound → run core’sbinding.md§ “Repo → project” resolution. Two Cocos-specific notes:- Score projects against the client name, not the repo name. A repo called
blast-festscores near-zero against a project calledRollic, and that is the convention working, not a missing project. Never create a project to fix a low score — the client already exists. - The module is the playable. Missing → creating one is the right guarded write far more often than creating a project.
- Score projects against the client name, not the repo name. A repo called
-
Read the source document — fork to
Explore; return the § 9 table as JSON, never the report body. An estimate report’s line-item table (preproduction § “Estimate — full line-item breakdown”) is the tree, already carrying per-row hours. A conversation breakdown is the tree as agreed. Never synthesise tasks that appear in neither. Mapping rules:report-ingest.md. -
Build the work-item tree — 3a derive and 3c write are forked; the confirmation between them is not. Core owns this entirely —
workflow-enforcement.md§ Stage 1 and § “Decomposition”. Do not reimplement the 4-hour leaf cap, the parent-carries-no-estimate rule, theget_me()assignment, or the one-confirmed-batch presentation. Compose each title with:Terminal window node $HOME/.claude/skills/t1k-cocos-playable-pipeline/scripts/pipeline-title.cjs compose \--client '<project name>' --playable '<module name>' --title '<task>'Pass the module name exactly as the board has it —
compose()derives the playable segment itself, stripping a leading<client> |where a board uses one and passing a bare name through. Both live shapes therefore work unchanged;hierarchy-contract.md§ “Why pipes” records the measured distribution. Single-quote every argument:<task>is report-derived text, and inside double quotes a title containing$(…)or a backtick would be executed by the shell.Core’s
t1k-plane-taxonomy.cjs composeis still called — for itslabelsoutput only. The title it returns is discarded. Seehierarchy-contract.md. -
Verify the required-task set — fork to
Explore. Compare the module’s work items againsttask-template.md: the five-item phase skeleton, then the build packages underFirst Build. Only2/2rows count as missing —1/2rows are surfaced as consider, never as a gap. Report them; propose, never auto-create — the same rule core applies to work items applies here. -
Create the Jenkins job — fork;
jenkins-job.cjsneeds no MCP. Hand off tot1k:jenkins-jobwith the folder, the template job, and the playable name. Two of its rules bind hardest here:- Always ask for the Discord thread ID — inline, BEFORE spawning the fork. A fork cannot ask, and a routed hop has no user to ask even if it tried. Never inherit one from the template, from a previous playable, or from earlier in this conversation.
- Smoke one job, verify it off the server, then continue — a 200 on
createItemdoes not prove the config landed.
-
Provision the repo — OPTIONAL, off by default; skip it whenever the playable already has a repo.
scripts/provision-repo.cjs plan|run --playable '<name>' --parent-dir '<dir>'creates<org>/<Name>PLAfromPlayableAdsTemplatewith realmain+develophistory, runsSetupProject.baton Windows, commits the result. Ask for (and persist) the parent dir; never auto-resolve a conflict or existing-repo hit.repo-provisioning.md. -
Link back.
create_work_item_commenton the module’s parent work item with the Jenkins job URL, and hand the user a clickable Plane link built witht1k-plane-binding.cjs url --work-item <uuid>— no Plane tool returns a URL, and a bareROLLIC-289is not clickable.
Gotchas
Section titled “Gotchas”- A routed fork has NO MCP servers — so a fork cannot call
mcp__plane__*at all.mr-delegate.shlaunches the hop with--strict-mcp-config --mcp-config '{"mcpServers":{}}'(verified in the shipped script, not inferred from docs), which strips every server including Plane and Jenkins. A brief that says “create the work items withcreate_work_item” therefore fails inside a routed hop while working perfectly when you test it inline. Drive Plane from a fork through its REST API —POST {base}/api/v1/workspaces/{slug}/projects/{id}/issues/with anx-api-keyheader, credentials read from theplaneentry of~/.claude.json. Jenkins is already safe:jenkins-job.cjsusesfetch. - Prefer one REST script over N MCP calls even inline. A 33-item tree is 33
create_work_itemround-trips, plus one estimate call each. As a single Node script it is one Bash call, it can dry-run, it can reconcile the leaf total against the report before writing anything, and it can read every value back afterwards. That shape is also the only shape a fork can run. - Plane’s estimate endpoint is
PUT .../issues/{id}/workload-estimate/. NotPOST(405), not/estimates/(404). Measured 2026-08-22. Thehoursreads back asnullwith arollupon any parent — that is correct, not a failed write: parents derive from the sub-tree. PLANE_WORKSPACE_SLUGmay hold a full URL rather than a slug. On at least one machine it ishttps://plane.the1studio.org/cocos/. The MCP server normalises it internally, so the misconfiguration is invisible through the tools and only bites a script that builds the API path itself. Take the last non-empty path segment rather than using the value raw.titleFormatcannot be configured to do this. Core’st1k-plane-taxonomy.cjsthrows on anytitleFormatother than{discipline}{kind} {title}, because itsparse()is the pinned exact inverse of itscompose(). SettingplaneWorkflow.taxonomy.titleFormatto a scope format breaks every Plane skill in the session, not just this one. The prefix is composed by this skill’s own script for exactly that reason.- Never append
[3D][Feature]after the scope to “get both”. Core’sparse()reads tokens only at the start of the string, soRollic | Blast Fest | [3D] Xreturnskind:null, discipline:null— the scope prefix blinds core to the taxonomy tokens that follow. The pipe change did not fix this; it is a scope-first problem, not a bracket problem. Measured, with the full table, inhierarchy-contract.md. Kind and discipline belong in labels. - Turning
taxonomy.enabled: falseto get the title you want also kills the labels. Stage 1 steps (a), (b), (c) and (e) are all skipped together. Labels are the only thing left carrying kind/discipline once the prefix is gone — keep the taxonomy enabled and discard only its title. parseno longer needs core’s config, and no longer returnsvocabularyChecked. Under the old bracket form[Client][Playable]and[Discipline][Kind]were structurally identical, soparsehad to load core’s vocabulary to tell them apart and flag a degraded read when it could not. Pipes cannot collide with bracket tokens, so that whole path is deleted. If you find code readingvocabularyChecked, it predates 2026-08-22.applyTo: "created-only"still holds. An item this skill matched is never renamed into the scope convention. An inconsistent board is a smaller failure than renaming a teammate’s item.- A low project score is the convention, not an error. See step 1.
- The estimate report’s hours are per line item, not per leaf. A 6-hour row is an undecomposed leaf under core’s cap — split it before writing any estimate, never write the 6.
- Jenkins
Job/CreateandJob/Deleteare separate permissions. You may be unable to delete a job you just made. Never create a scratch job to try something out. - Estimate-report tasks and required-template tasks overlap but are not the same set. The report covers what was quoted; the template covers what every playable ships. Reconcile both, and report anything present in one and absent from the other.
- Step 6 is optional and off by default — a playable on an existing repo never runs it. When it
does: the clone dir MUST be
<parent>/<Name>PLAexactly (SetupProject.batreads its own folder name); an existing target repo or adevelopmerge conflict is a hard stop; non-Windows skips setup.
Binding a Cocos playable repo to its Plane client-project + playable-module, turning an existing
breakdown or estimate report into the module’s work-item tree under the studio title convention,
checking that tree against the required-task set, creating the playable’s Jenkins job, and linking
the two. Not for: producing the breakdown itself (t1k:cocos:playable:preproduction), Plane
lifecycle mechanics (t1k:plane), Jenkins config.xml mechanics (t1k:jenkins-job), triggering or
watching builds, or any per-phase state transition during the cook (core’s Stage 2 already owns it).
Related
Section titled “Related”skills/t1k-plane/references/workflow-enforcement.md— the enforced lifecycle (SSOT)skills/t1k-plane/references/binding.md— the deliverable convention this skill instantiatesskills/t1k-plane/references/taxonomy.md— the title convention this skill deviates fromskills/t1k-jenkins-job/SKILL.md— the job mechanics step 5 delegates torepo-provisioning.md— step 6 mechanics,SetupProject.batdetailst1k:cocos:playable:preproduction— produces the estimate report step 2 ingestsrules/plane-workitem-workflow.md(core, auto-loaded) — why any of this is mandatory