[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M79RYSQ8C3MWDFTDSRAYFJYA":3,"skill-same:01M79RYSQ8C3MWDFTDSRAYFJYA":46},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":14,"repositoryHtmlUrl":27,"repositoryHomepageUrl":6,"ownerHtmlUrl":28,"ownerBlogUrl":29,"ownerDescription":30,"updatedAt":31,"install":32,"resources":45},"01M79RYSQ8C3MWDFTDSRAYFJYA","tk-studio-track",null,"Tracking verbs on the bound Jira project — `block` moves a story the loop cannot progress, and each dependent story, to \"Blocked - Awaiting User\" with a comment that @mentions the owner (or, with `--engagement`, one engagement's questions issue, which `close` later moves to \"Done\"); `reply` posts the owner's answer (or \"Done.\") with the owner's host-only token and moves the issue to \"Block Resolved\"; `validate` checks that resolution and passes the issue back to work or re-blocks it with a follow-up. Use when the user says \"block this story\", \"tk track block\", \"put it in the owner's inbox\", \"reply to the blocked issue\", \"validate the resolution\", or a supervisor or engagement payload names this skill.","thkennedy\u002Ftk-studio","thkennedy","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F4164502?v=4","01M79QZ8DMP5FF2HC6GS3S6FDF","Tim Kennedy","01M79QZ8EM9JBWJ15R5JNC4PW3",0,"MIT","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-track\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: tk-studio-track\ndescription: Tracking verbs on the bound Jira project — `block` moves a story the loop cannot progress, and each dependent story, to \"Blocked - Awaiting User\" with a comment that @mentions the owner (or, with `--engagement`, one engagement's questions issue, which `close` later moves to \"Done\"); `reply` posts the owner's answer (or \"Done.\") with the owner's host-only token and moves the issue to \"Block Resolved\"; `validate` checks that resolution and passes the issue back to work or re-blocks it with a follow-up. Use when the user says \"block this story\", \"tk track block\", \"put it in the owner's inbox\", \"reply to the blocked issue\", \"validate the resolution\", or a supervisor or engagement payload names this skill.\n---\n\n# tk-studio-track\n\nThe owner's one inbox starts here (§6.9 items 1 and 2, §7.4): a story the\nloop cannot progress — a dev session's `blocked` end, a `paused` run\nawaiting a ruling, an engagement question — sits in Jira's\n`Blocked - Awaiting User` with its reason, and so does every story that\ndepends on it. The sync-back then derives `_bmad-output\u002Ftracking\u002Finbox.json`\nfrom those statuses. Every Atlassian write goes through the planning adapter\n(`lib\u002Fjirabackend.py`, AD-4); nothing else is promoted (D9).\n\n## Behavior (both modes)\n\n1. Run the verb through the plugin root:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" block --directory \u003Cproject-root> \\\n     --story \u003CST-NNN | N-M-slug> --reason \"\u003Cwhy it cannot progress>\" \\\n     --resolves \"\u003Cwhat resolves it>\" [--source run|engagement] [--dry-run]\n   ```\n\n   `block` resolves the story's plan entity (an entity id, or a loop story\n   key `N-M-…` matched by the entity's `source: epics.md#story-N.M`) and its\n   `external.jira.key`, plus each dependent story — the story entities whose\n   `depends_on` names it (a dependent already `done` or `dropped` is\n   skipped, named in `notes[]`). Each issue moves to `Blocked - Awaiting User` by\n   the transition's target **name** (never its category: `Block Resolved`\n   shares it), skipped for an issue already there, and gets one agent\n   comment (REST v2 wiki mention):\n\n   ```text\n   [~accountid:\u003Cowner>] Blocked: \u003Creason>\n   Resolves when: \u003Cresolves>\n   Blocked by: \u003Cstory's key>        (a dependent's comment only)\n   ```\n\n   The comment is skipped only when the issue was already in\n   `Blocked - Awaiting User` before the run (no transition made) and its\n   newest agent comment (read live, `GET \u002Fissue\u002F\u003CKEY>\u002Fcomment`; agent is\n   anyone not the owner) has the same body — a re-block after `Block\n   Resolved` always comments. The\n   result is `{ok, dry_run, story, source, issues:[{issue, entity, role:\n   story|dependent, transitioned, commented, skipped?, error?}], notes[],\n   written[], retry?}` with the status block last; no local file is written.\n   `--source engagement` fixes the shape for the engagement loop's questions\n   (EP-32); the supervisor's paused-run pass calls it with `--source run`.\n\n   An engagement's questions (Story 32.3) block one issue per engagement\n   instead of a story's; the supervisor's questions pass calls both verbs on\n   the host:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" block --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> [--dry-run]\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" close --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> [--dry-run]\n   ```\n\n   `--engagement` is exclusive with `--story`, implies `--source\n   engagement` and takes no `--reason` or `--resolves`. `block` finds the\n   issue by its exact summary `ENG-NNN: questions` in\n   `planning.jira.project_key` (`GET \u002Fsearch\u002Fjql`) or creates it (`POST\n   \u002Fissue`, type `Task`, label `tk-studio-engagement`, the request's first\n   line as its description), moves it to `Blocked - Awaiting User` by name\n   with the skip rules above, and comments the record's open questions:\n\n   ```text\n   [~accountid:\u003Cowner>] Blocked: ENG-NNN needs \u003Cn> answer(s)\n   Resolves when: reply with one \"\u003Cid>: \u003Coption>\" line per question\n\n   \u003Cid>: \u003Ctext>\n   Impact: \u003Cimpact>\n   - \u003Clabel> (default)\n   - \u003Clabel>\n   ```\n\n   The result is `{ok, dry_run, story: null, engagement, source:\n   \"engagement\", issues: [{issue, created, transitioned, commented,\n   skipped?}], notes[], written: [], retry?}`; its refusals add\n   `EngagementNotFound`, `NoOpenQuestions`, `IssueCreateRefused` (a 400\n   on the create, e.g. no `Task` type) and `EngagementLockUnwritable`. `close` (the agent seat) moves the\n   issue by name to `Block Resolved` (unless it is there or at `Done`), then\n   to `Done` — no validation leg: the planner's next turn judges the\n   answers; a move already in place is named in `skipped[]`, an absent issue\n   is `NoIssue`, a move no transition makes `NoTransition` (an issue never\n   blocked, e.g. at `To Do`, skips `Block Resolved` when no transition\n   reaches it, named in `skipped[]`); the result is `{ok, dry_run, engagement, issue, moved[],\n   skipped[], notes[], written: [], retry?}`.\n   Both verbs hold `_bmad-output\u002Fengagements\u002F\u003Cid>\u002Ftrack.lock` (exclusive\n   create, stale after 300 s; a dry run takes none): a lock held ends\n   `partial` with `retry: true`, so two callers never create two issues. A\n   re-block after an owner reply that answered nothing posts the comment\n   again, even with the same body.\n\n   Story 32.6 adds the engagement block's kind; the supervisor's\n   engagement-run pass calls it on the host:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" block --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> [--kind questions|cap|ask] [--reason \"\u003Ctext>\"] [--dry-run]\n   ```\n\n   `--kind questions` (the default) is the block above and takes no\n   `--reason`. `--kind cap` (the engagement reached its quote's cap) and\n   `--kind ask` (a story landed under `ask` autonomy) need `--reason` and no\n   open questions; they block the same `ENG-NNN: questions` issue with the\n   31.5 prefix kept — `[~accountid:\u003Cowner>] Blocked: ENG-NNN \u003Cthe reason's\n   first line>`, then ``Resolves when: reply with `go {cap}` from the web or\n   the API, or `cancel` `` (cap) or ``Resolves when: reply `continue` to run\n   the next story`` (ask), then the reason's other lines (the figures). The\n   result carries `kind`; more refusals by name before any call:\n   `UnknownKind` and `ReasonMissing`, and `--kind` is refused with\n   `--story`.\n   Payload mapping: `story`, `reason`, `resolves` map to their flags;\n   `kind` (with `engagement`) adds `--kind \u003Ckind>`;\n   `source` adds `--source \u003Csource>`; `dry_run` adds `--dry-run` (reads\n   only, writes nothing).\n\n   The `unblock` verb (Story 36.3) closes a block that was resolved by the\n   operator's resume, or by the owner's `go` or `continue`; the supervisor\n   calls it on the host, with the agent seat:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" unblock --directory \u003Cproject-root> \\\n     --story \u003CST-NNN | N-M-slug> --reason \"\u003Ctext>\" [--source run] [--dry-run]\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" unblock --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> --kind cap|ask --reason \"\u003Ctext>\" [--dry-run]\n   ```\n\n   An issue in `Blocked - Awaiting User` gets one `Resolved: \u003Creason>` agent\n   comment with no account mention and moves by name to `Block Resolved`; any\n   other status is named in `skipped[]`, with no move and no comment. `--story`\n   also moves the dependents `block` moved (one blocked by something else is\n   skipped). `--engagement` moves its issue to `Block Resolved`, never `Done`.\n   The result is `{ok, dry_run, story, source, issues: [{issue, entity, role,\n   from, to, transitioned, commented, skipped?, error?}], skipped[], notes[],\n   written: [], retry?}`, plus `engagement` and `kind` on the engagement form.\n   Refusals end `blocked` as `block`'s do, with `NoIssue`, `UnknownKind` and\n   `ReasonMissing` by name; a temporary failure ends `partial` with `ok: false`\n   and `retry: true`.\n\n   The `drop` verb (DW-51) retires the issues a superseded plan promoted;\n   the supervisor's approve pass calls it on the host, with the agent seat,\n   when an engagement is re-approved after `revise` with its earlier approve\n   PR still open:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" drop --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> --issue \u003CKEY> [--issue \u003CKEY> …] --reason \"\u003Ctext>\" [--dry-run]\n   ```\n\n   Before any write, every issue not already dropped must have a transition\n   reaching `DNI`, else `NoTransition` with nothing moved or commented. Each\n   issue then moves by name to `DNI` and gets one `Dropped: \u003Creason>` agent\n   comment with no account mention; an issue already in a dropped status is\n   not moved, and gets the comment only when its newest agent comment is not\n   that same body, else it is named in `skipped[]` (a retry never comments\n   twice). The result is `{ok, dry_run, engagement, issues: [{issue,\n   transitioned, commented}], skipped[], notes[], written: [], retry?}`.\n   Refusals end `blocked` by name: `EngagementNotFound`, `NoIssue`,\n   `BadIssue`, `ReasonMissing`, the agent seat's gaps, `NoTransition` and\n   `JiraRefused: \u003Ckey>: \u003Cerror>` (any other non-transient Jira error); a\n   temporary failure ends `partial` with `ok: false` and `retry: true`. It\n   holds the engagement's `track.lock` as `close` does.\n\n   The `reply` verb (Story 31.6) is the owner's answer from the web's\n   Issue screen; only the supervisor's `POST \u002Finbox\u002F{key}\u002Freply` calls it,\n   on the host:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" reply --directory \u003Cproject-root> \\\n     --issue \u003CKEY> (--body-file \u003Cpath> | --done) [--dry-run]\n   ```\n\n   It posts the comment (`--done` writes `Done.`; the body comes from a\n   UTF-8 file, never the command line) with the **owner** seat's client\n   (`TK_STUDIO_ATLASSIAN_OWNER_EMAIL` and `TK_STUDIO_ATLASSIAN_OWNER_TOKEN`,\n   host-only — outside the `TK_ATLASSIAN_` prefix the sandbox receives —\n   through the `TK_ATLASSIAN_CLOUD_ID` gateway, REST v2, plain text), then\n   moves the issue by name to `Block Resolved`; an issue already there\n   skips the transition, named in `skipped[]`. The comment is skipped — named in `skipped[]`, with `commented: true` and the existing `comment_id` — only when an owner comment with the same body already stands after the issue's newest agent comment (read live); a reply to a later re-block always posts. The result is `{ok, dry_run,\n   issue, commented, comment_id, transitioned, notes[], skipped[],\n   written[], retry?}` with the status block last; no local file is written\n   and nothing else is promoted. Payload mapping: `issue` maps to\n   `--issue`; `body` is written to a temp file passed as `--body-file`;\n   `done: true` adds `--done`; `dry_run` adds `--dry-run`. Its refusals\n   (exit 2), each named: an absent owner variable or cloud id, or no\n   `planning.jira` binding (no Atlassian call made — the agent's token never\n   stands in); no recorded `planning.jira.owner_account_id`; a created\n   comment whose author is not the owner (another seat's token: nothing\n   moved); no transition reaching `Block Resolved` (the comment stays,\n   `commented: true`). A temporary failure ends `partial` with `ok: false`\n   and `retry: true`.\n\n   The `validate` verb (Story 31.7, §6.9 items 4 and 5) checks a\n   resolution before the story continues, with the **agent** seat only\n   (never the owner's variables, never `reply`):\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" validate --directory \u003Cproject-root> \\\n     --issue \u003CKEY> (--check | --pass --to \"In Progress\"|\"To Do\" \\\n       [--ruling-file \u003Cpath> --spec \u003Cpaused spec>] \\\n       | --fail --follow-up-file \u003Cpath>) [--dry-run]\n   ```\n\n   `--check` reads the changelog: `owner_resolved` is true only when the\n   newest transition into `Block Resolved` carries the recorded owner\n   accountId (an older owner transition never validates a later one; an\n   absent or empty id never matches). It reads `reason_comment` and\n   `replies[]` (the owner's comments after the newest agent comment;\n   `Done.` is a valid answer) from local memory only, and marks the inbox\n   item `validating`. The result is `{ok, dry_run, issue, mode, entity,\n   story, owner_resolved, transition, reason_comment, replies[], notes[],\n   written[], retry?}` with the status block last. `--pass` refuses when\n   `owner_resolved` is false; it checks a transition reaches `--to` before\n   writing anything, writes the ruling when given (`**Ruling (owner, \u003CKEY>\n   comment \u003Cid>, \u003Cdate>):** \u003Ctext>` just before the spec's\n   `\u003C\u002Fintent-contract>` line, else `\u003C\u002Ffrozen-after-approval>`; never\n   `status:`), then moves the issue by name to `--to`. `--fail` (whatever\n   `owner_resolved` says) re-blocks the issue with the follow-up as the\n   reason (a new `Blocked:` agent comment — back in the Inbox); a retried\n   `--fail` whose move into `Blocked - Awaiting User` already landed posts\n   the missing comment without moving the issue again, and a retried\n   `--pass` whose move into `--to` already landed (newer than the owner's\n   newest move into `Block Resolved`) writes the ruling (idempotent) and\n   the line without moving it again. A ruling the pass wrote is taken back\n   out when the pass then ends refused. Each\n   outcome appends one line to `_bmad-output\u002Ftracking\u002F\u003CKEY>\u002Fvalidation.jsonl`\n   (the supervisor's evidence for the resume), named in `written[]`.\n   Refusals (exit 2), each named: a bad key; no `planning.jira` binding or\n   an absent agent variable (no Atlassian call made); no recorded owner\n   accountId; a live status other than `Block Resolved`; a `--pass` when\n   `owner_resolved` is false; no transition reaching the target; an absent spec or block for the ruling. A temporary\n   failure ends `partial` with `ok: false` and `retry: true`, appends no\n   line — call again.\n\n   **Session procedure for `payload.verb: validate`** (the shipped\n   `validate-resolution` job; the payload is `{verb, project_root,\n   issues:[{issue, story, paused_run, spec}]}`): for each payload issue,\n   run `--check` with `--directory \u003Cproject_root>`. On\n   `owner_resolved: false`, take `--fail` with a follow-up asking the owner\n   to resolve it from the Inbox. Otherwise read `reason_comment` and\n   `replies[]`: when the reason names something runnable on the host — a\n   command, a path, a sign-in — test it; else judge whether the replies are\n   enough. Pass → `--pass --to \"In Progress\"` (`--to \"To Do\"` when the\n   story's run never started), adding `--ruling-file \u003Ca temp file holding\n   the reply verbatim> --spec \u003Cspec>` when the reply carries a ruling (the\n   block's resolves text names \"a ruling in the spec\") and the payload\n   names a `spec`. Fail → `--fail --follow-up-file \u003Ca temp file>` saying\n   what is still missing. On `retry: true`, call the same verb again. End\n   on the status block; commit nothing.\n\n2. Blocking conditions of `block` (exit 2), each named in `reason`:\n   - no `planning.jira` binding (run `plansync.py setup` first), or an\n     absent agent-seat variable (`TK_ATLASSIAN_AGENT_EMAIL`,\n     `TK_ATLASSIAN_AGENT_TOKEN`, `TK_ATLASSIAN_CLOUD_ID`) — no Atlassian\n     call made;\n   - no recorded `planning.jira.owner_account_id` — the mention needs it;\n     an absent or empty accountId never matches;\n   - a story with no plan entity, or never promoted (no\n     `external.jira.key`);\n   - no transition reaching `Blocked - Awaiting User` on the story's issue,\n     or its block comment refused by name after the transition (the entry\n     keeps `transitioned: true` and names `block comment refused`);\n   - the 401\u002F403 split of the adapter (the token, its email, its expiry or\n     the org toggle; the sandbox proxy's network approval step; a missing\n     scope).\n\n   A temporary failure (an exhausted 429, a 5xx, the site unreachable) on\n   the story's own issue, or on a dependent's after the story's issue was\n   blocked (the other dependents still run), is no refusal: it ends\n   `partial` with `ok: false` and `retry: true` (exit 0), and the caller\n   calls again on its next pass. A dependent that fails otherwise while the\n   story's issue was blocked ends `partial`, naming it; a dry run that would move or comment ends\n   `partial` too.\n\n3. Report:\n   - **Attended:** show each issue moved or skipped and the comment text.\n   - **Headless:** no prompts (AD-11). Relay the core's `status`, `reason`\n     and `artifacts` as they are — never infer the status from the exit\n     code (`partial` exits 0):\n\n     ```json\n     {\"status\": \"complete\", \"intent\": \"tk-studio-track\", \"artifacts\": [], \"reason\": null}\n     ```\n\nIf the deterministic core is unrunnable — a tool call denied by permissions, `uv`\u002Fpython unavailable — end `blocked` with the status block naming the unrunnable core as `reason`: never a question, never a headless run that ends without the block (AD-11).\n\n## Rules\n\n- No skill other than this one and `tk-studio-plan-sync` writes to Jira,\n  and both only through `lib\u002Fjirabackend.py` (AD-4).\n- Only the status move and the block comment (or the owner's reply) are written; entity fields\n  stay canonical (AD-5, D9) — the sync-back pulls the new status back.\n- All paths resolve through `${CLAUDE_PLUGIN_ROOT}`.\n","# tk-studio-track\n\nThe owner's one inbox starts here (§6.9 items 1 and 2, §7.4): a story the\nloop cannot progress — a dev session's `blocked` end, a `paused` run\nawaiting a ruling, an engagement question — sits in Jira's\n`Blocked - Awaiting User` with its reason, and so does every story that\ndepends on it. The sync-back then derives `_bmad-output\u002Ftracking\u002Finbox.json`\nfrom those statuses. Every Atlassian write goes through the planning adapter\n(`lib\u002Fjirabackend.py`, AD-4); nothing else is promoted (D9).\n\n## Behavior (both modes)\n\n1. Run the verb through the plugin root:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" block --directory \u003Cproject-root> \\\n     --story \u003CST-NNN | N-M-slug> --reason \"\u003Cwhy it cannot progress>\" \\\n     --resolves \"\u003Cwhat resolves it>\" [--source run|engagement] [--dry-run]\n   ```\n\n   `block` resolves the story's plan entity (an entity id, or a loop story\n   key `N-M-…` matched by the entity's `source: epics.md#story-N.M`) and its\n   `external.jira.key`, plus each dependent story — the story entities whose\n   `depends_on` names it (a dependent already `done` or `dropped` is\n   skipped, named in `notes[]`). Each issue moves to `Blocked - Awaiting User` by\n   the transition's target **name** (never its category: `Block Resolved`\n   shares it), skipped for an issue already there, and gets one agent\n   comment (REST v2 wiki mention):\n\n   ```text\n   [~accountid:\u003Cowner>] Blocked: \u003Creason>\n   Resolves when: \u003Cresolves>\n   Blocked by: \u003Cstory's key>        (a dependent's comment only)\n   ```\n\n   The comment is skipped only when the issue was already in\n   `Blocked - Awaiting User` before the run (no transition made) and its\n   newest agent comment (read live, `GET \u002Fissue\u002F\u003CKEY>\u002Fcomment`; agent is\n   anyone not the owner) has the same body — a re-block after `Block\n   Resolved` always comments. The\n   result is `{ok, dry_run, story, source, issues:[{issue, entity, role:\n   story|dependent, transitioned, commented, skipped?, error?}], notes[],\n   written[], retry?}` with the status block last; no local file is written.\n   `--source engagement` fixes the shape for the engagement loop's questions\n   (EP-32); the supervisor's paused-run pass calls it with `--source run`.\n\n   An engagement's questions (Story 32.3) block one issue per engagement\n   instead of a story's; the supervisor's questions pass calls both verbs on\n   the host:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" block --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> [--dry-run]\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" close --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> [--dry-run]\n   ```\n\n   `--engagement` is exclusive with `--story`, implies `--source\n   engagement` and takes no `--reason` or `--resolves`. `block` finds the\n   issue by its exact summary `ENG-NNN: questions` in\n   `planning.jira.project_key` (`GET \u002Fsearch\u002Fjql`) or creates it (`POST\n   \u002Fissue`, type `Task`, label `tk-studio-engagement`, the request's first\n   line as its description), moves it to `Blocked - Awaiting User` by name\n   with the skip rules above, and comments the record's open questions:\n\n   ```text\n   [~accountid:\u003Cowner>] Blocked: ENG-NNN needs \u003Cn> answer(s)\n   Resolves when: reply with one \"\u003Cid>: \u003Coption>\" line per question\n\n   \u003Cid>: \u003Ctext>\n   Impact: \u003Cimpact>\n   - \u003Clabel> (default)\n   - \u003Clabel>\n   ```\n\n   The result is `{ok, dry_run, story: null, engagement, source:\n   \"engagement\", issues: [{issue, created, transitioned, commented,\n   skipped?}], notes[], written: [], retry?}`; its refusals add\n   `EngagementNotFound`, `NoOpenQuestions`, `IssueCreateRefused` (a 400\n   on the create, e.g. no `Task` type) and `EngagementLockUnwritable`. `close` (the agent seat) moves the\n   issue by name to `Block Resolved` (unless it is there or at `Done`), then\n   to `Done` — no validation leg: the planner's next turn judges the\n   answers; a move already in place is named in `skipped[]`, an absent issue\n   is `NoIssue`, a move no transition makes `NoTransition` (an issue never\n   blocked, e.g. at `To Do`, skips `Block Resolved` when no transition\n   reaches it, named in `skipped[]`); the result is `{ok, dry_run, engagement, issue, moved[],\n   skipped[], notes[], written: [], retry?}`.\n   Both verbs hold `_bmad-output\u002Fengagements\u002F\u003Cid>\u002Ftrack.lock` (exclusive\n   create, stale after 300 s; a dry run takes none): a lock held ends\n   `partial` with `retry: true`, so two callers never create two issues. A\n   re-block after an owner reply that answered nothing posts the comment\n   again, even with the same body.\n\n   Story 32.6 adds the engagement block's kind; the supervisor's\n   engagement-run pass calls it on the host:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" block --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> [--kind questions|cap|ask] [--reason \"\u003Ctext>\"] [--dry-run]\n   ```\n\n   `--kind questions` (the default) is the block above and takes no\n   `--reason`. `--kind cap` (the engagement reached its quote's cap) and\n   `--kind ask` (a story landed under `ask` autonomy) need `--reason` and no\n   open questions; they block the same `ENG-NNN: questions` issue with the\n   31.5 prefix kept — `[~accountid:\u003Cowner>] Blocked: ENG-NNN \u003Cthe reason's\n   first line>`, then ``Resolves when: reply with `go {cap}` from the web or\n   the API, or `cancel` `` (cap) or ``Resolves when: reply `continue` to run\n   the next story`` (ask), then the reason's other lines (the figures). The\n   result carries `kind`; more refusals by name before any call:\n   `UnknownKind` and `ReasonMissing`, and `--kind` is refused with\n   `--story`.\n   Payload mapping: `story`, `reason`, `resolves` map to their flags;\n   `kind` (with `engagement`) adds `--kind \u003Ckind>`;\n   `source` adds `--source \u003Csource>`; `dry_run` adds `--dry-run` (reads\n   only, writes nothing).\n\n   The `unblock` verb (Story 36.3) closes a block that was resolved by the\n   operator's resume, or by the owner's `go` or `continue`; the supervisor\n   calls it on the host, with the agent seat:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" unblock --directory \u003Cproject-root> \\\n     --story \u003CST-NNN | N-M-slug> --reason \"\u003Ctext>\" [--source run] [--dry-run]\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" unblock --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> --kind cap|ask --reason \"\u003Ctext>\" [--dry-run]\n   ```\n\n   An issue in `Blocked - Awaiting User` gets one `Resolved: \u003Creason>` agent\n   comment with no account mention and moves by name to `Block Resolved`; any\n   other status is named in `skipped[]`, with no move and no comment. `--story`\n   also moves the dependents `block` moved (one blocked by something else is\n   skipped). `--engagement` moves its issue to `Block Resolved`, never `Done`.\n   The result is `{ok, dry_run, story, source, issues: [{issue, entity, role,\n   from, to, transitioned, commented, skipped?, error?}], skipped[], notes[],\n   written: [], retry?}`, plus `engagement` and `kind` on the engagement form.\n   Refusals end `blocked` as `block`'s do, with `NoIssue`, `UnknownKind` and\n   `ReasonMissing` by name; a temporary failure ends `partial` with `ok: false`\n   and `retry: true`.\n\n   The `drop` verb (DW-51) retires the issues a superseded plan promoted;\n   the supervisor's approve pass calls it on the host, with the agent seat,\n   when an engagement is re-approved after `revise` with its earlier approve\n   PR still open:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" drop --directory \u003Cproject-root> \\\n     --engagement \u003CENG-NNN> --issue \u003CKEY> [--issue \u003CKEY> …] --reason \"\u003Ctext>\" [--dry-run]\n   ```\n\n   Before any write, every issue not already dropped must have a transition\n   reaching `DNI`, else `NoTransition` with nothing moved or commented. Each\n   issue then moves by name to `DNI` and gets one `Dropped: \u003Creason>` agent\n   comment with no account mention; an issue already in a dropped status is\n   not moved, and gets the comment only when its newest agent comment is not\n   that same body, else it is named in `skipped[]` (a retry never comments\n   twice). The result is `{ok, dry_run, engagement, issues: [{issue,\n   transitioned, commented}], skipped[], notes[], written: [], retry?}`.\n   Refusals end `blocked` by name: `EngagementNotFound`, `NoIssue`,\n   `BadIssue`, `ReasonMissing`, the agent seat's gaps, `NoTransition` and\n   `JiraRefused: \u003Ckey>: \u003Cerror>` (any other non-transient Jira error); a\n   temporary failure ends `partial` with `ok: false` and `retry: true`. It\n   holds the engagement's `track.lock` as `close` does.\n\n   The `reply` verb (Story 31.6) is the owner's answer from the web's\n   Issue screen; only the supervisor's `POST \u002Finbox\u002F{key}\u002Freply` calls it,\n   on the host:\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" reply --directory \u003Cproject-root> \\\n     --issue \u003CKEY> (--body-file \u003Cpath> | --done) [--dry-run]\n   ```\n\n   It posts the comment (`--done` writes `Done.`; the body comes from a\n   UTF-8 file, never the command line) with the **owner** seat's client\n   (`TK_STUDIO_ATLASSIAN_OWNER_EMAIL` and `TK_STUDIO_ATLASSIAN_OWNER_TOKEN`,\n   host-only — outside the `TK_ATLASSIAN_` prefix the sandbox receives —\n   through the `TK_ATLASSIAN_CLOUD_ID` gateway, REST v2, plain text), then\n   moves the issue by name to `Block Resolved`; an issue already there\n   skips the transition, named in `skipped[]`. The comment is skipped — named in `skipped[]`, with `commented: true` and the existing `comment_id` — only when an owner comment with the same body already stands after the issue's newest agent comment (read live); a reply to a later re-block always posts. The result is `{ok, dry_run,\n   issue, commented, comment_id, transitioned, notes[], skipped[],\n   written[], retry?}` with the status block last; no local file is written\n   and nothing else is promoted. Payload mapping: `issue` maps to\n   `--issue`; `body` is written to a temp file passed as `--body-file`;\n   `done: true` adds `--done`; `dry_run` adds `--dry-run`. Its refusals\n   (exit 2), each named: an absent owner variable or cloud id, or no\n   `planning.jira` binding (no Atlassian call made — the agent's token never\n   stands in); no recorded `planning.jira.owner_account_id`; a created\n   comment whose author is not the owner (another seat's token: nothing\n   moved); no transition reaching `Block Resolved` (the comment stays,\n   `commented: true`). A temporary failure ends `partial` with `ok: false`\n   and `retry: true`.\n\n   The `validate` verb (Story 31.7, §6.9 items 4 and 5) checks a\n   resolution before the story continues, with the **agent** seat only\n   (never the owner's variables, never `reply`):\n\n   ```bash\n   uv run \"${CLAUDE_PLUGIN_ROOT}\u002Flib\u002Ftrack.py\" validate --directory \u003Cproject-root> \\\n     --issue \u003CKEY> (--check | --pass --to \"In Progress\"|\"To Do\" \\\n       [--ruling-file \u003Cpath> --spec \u003Cpaused spec>] \\\n       | --fail --follow-up-file \u003Cpath>) [--dry-run]\n   ```\n\n   `--check` reads the changelog: `owner_resolved` is true only when the\n   newest transition into `Block Resolved` carries the recorded owner\n   accountId (an older owner transition never validates a later one; an\n   absent or empty id never matches). It reads `reason_comment` and\n   `replies[]` (the owner's comments after the newest agent comment;\n   `Done.` is a valid answer) from local memory only, and marks the inbox\n   item `validating`. The result is `{ok, dry_run, issue, mode, entity,\n   story, owner_resolved, transition, reason_comment, replies[], notes[],\n   written[], retry?}` with the status block last. `--pass` refuses when\n   `owner_resolved` is false; it checks a transition reaches `--to` before\n   writing anything, writes the ruling when given (`**Ruling (owner, \u003CKEY>\n   comment \u003Cid>, \u003Cdate>):** \u003Ctext>` just before the spec's\n   `\u003C\u002Fintent-contract>` line, else `\u003C\u002Ffrozen-after-approval>`; never\n   `status:`), then moves the issue by name to `--to`. `--fail` (whatever\n   `owner_resolved` says) re-blocks the issue with the follow-up as the\n   reason (a new `Blocked:` agent comment — back in the Inbox); a retried\n   `--fail` whose move into `Blocked - Awaiting User` already landed posts\n   the missing comment without moving the issue again, and a retried\n   `--pass` whose move into `--to` already landed (newer than the owner's\n   newest move into `Block Resolved`) writes the ruling (idempotent) and\n   the line without moving it again. A ruling the pass wrote is taken back\n   out when the pass then ends refused. Each\n   outcome appends one line to `_bmad-output\u002Ftracking\u002F\u003CKEY>\u002Fvalidation.jsonl`\n   (the supervisor's evidence for the resume), named in `written[]`.\n   Refusals (exit 2), each named: a bad key; no `planning.jira` binding or\n   an absent agent variable (no Atlassian call made); no recorded owner\n   accountId; a live status other than `Block Resolved`; a `--pass` when\n   `owner_resolved` is false; no transition reaching the target; an absent spec or block for the ruling. A temporary\n   failure ends `partial` with `ok: false` and `retry: true`, appends no\n   line — call again.\n\n   **Session procedure for `payload.verb: validate`** (the shipped\n   `validate-resolution` job; the payload is `{verb, project_root,\n   issues:[{issue, story, paused_run, spec}]}`): for each payload issue,\n   run `--check` with `--directory \u003Cproject_root>`. On\n   `owner_resolved: false`, take `--fail` with a follow-up asking the owner\n   to resolve it from the Inbox. Otherwise read `reason_comment` and\n   `replies[]`: when the reason names something runnable on the host — a\n   command, a path, a sign-in — test it; else judge whether the replies are\n   enough. Pass → `--pass --to \"In Progress\"` (`--to \"To Do\"` when the\n   story's run never started), adding `--ruling-file \u003Ca temp file holding\n   the reply verbatim> --spec \u003Cspec>` when the reply carries a ruling (the\n   block's resolves text names \"a ruling in the spec\") and the payload\n   names a `spec`. Fail → `--fail --follow-up-file \u003Ca temp file>` saying\n   what is still missing. On `retry: true`, call the same verb again. End\n   on the status block; commit nothing.\n\n2. Blocking conditions of `block` (exit 2), each named in `reason`:\n   - no `planning.jira` binding (run `plansync.py setup` first), or an\n     absent agent-seat variable (`TK_ATLASSIAN_AGENT_EMAIL`,\n     `TK_ATLASSIAN_AGENT_TOKEN`, `TK_ATLASSIAN_CLOUD_ID`) — no Atlassian\n     call made;\n   - no recorded `planning.jira.owner_account_id` — the mention needs it;\n     an absent or empty accountId never matches;\n   - a story with no plan entity, or never promoted (no\n     `external.jira.key`);\n   - no transition reaching `Blocked - Awaiting User` on the story's issue,\n     or its block comment refused by name after the transition (the entry\n     keeps `transitioned: true` and names `block comment refused`);\n   - the 401\u002F403 split of the adapter (the token, its email, its expiry or\n     the org toggle; the sandbox proxy's network approval step; a missing\n     scope).\n\n   A temporary failure (an exhausted 429, a 5xx, the site unreachable) on\n   the story's own issue, or on a dependent's after the story's issue was\n   blocked (the other dependents still run), is no refusal: it ends\n   `partial` with `ok: false` and `retry: true` (exit 0), and the caller\n   calls again on its next pass. A dependent that fails otherwise while the\n   story's issue was blocked ends `partial`, naming it; a dry run that would move or comment ends\n   `partial` too.\n\n3. Report:\n   - **Attended:** show each issue moved or skipped and the comment text.\n   - **Headless:** no prompts (AD-11). Relay the core's `status`, `reason`\n     and `artifacts` as they are — never infer the status from the exit\n     code (`partial` exits 0):\n\n     ```json\n     {\"status\": \"complete\", \"intent\": \"tk-studio-track\", \"artifacts\": [], \"reason\": null}\n     ```\n\nIf the deterministic core is unrunnable — a tool call denied by permissions, `uv`\u002Fpython unavailable — end `blocked` with the status block naming the unrunnable core as `reason`: never a question, never a headless run that ends without the block (AD-11).\n\n## Rules\n\n- No skill other than this one and `tk-studio-plan-sync` writes to Jira,\n  and both only through `lib\u002Fjirabackend.py` (AD-4).\n- Only the status move and the block comment (or the owner's reply) are written; entity fields\n  stay canonical (AD-5, D9) — the sync-back pulls the new status back.\n- All paths resolve through `${CLAUDE_PLUGIN_ROOT}`.\n",{"name":5,"description":7},"main","ecd23b3c0458cd511812f643d9111e3d7c8235f4","plugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-track","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio","https:\u002F\u002Fgithub.com\u002Fthkennedy","https:\u002F\u002Ftimothyhkennedy.com","Just a guy who loves making tools, automation and games.","2026-11-10 20:38:16",{"source":8,"skillName":5,"commands":33,"prompt":43,"zipAvailable":44},[34,37,40],{"tool":35,"command":36},"npx","npx skills add thkennedy\u002Ftk-studio --skill tk-studio-track",{"tool":38,"command":39},"bunx","bunx skills add thkennedy\u002Ftk-studio --skill tk-studio-track",{"tool":41,"command":42},"pnpm","pnpm dlx skills add thkennedy\u002Ftk-studio --skill tk-studio-track","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-track\u002FSKILL.md",true,[],[47,54,61,68,75,82],{"skillNo":48,"name":49,"nameZh":6,"description":50,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":51,"categories":52,"keywords":53,"publishedAt":19},"01M79RYMVE0031EV5B64MBGD8V","tk-studio-session","Session discipline as a surface — land a boundary handoff (optionally carrying knowledge deltas) and end the session, or resume a fresh session from a run workspace alone. Use when the user says \"hand off this run\", \"tk session\", \"resume the run\", or a boundary\u002Fbudget trigger says the session must split.","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-session\u002FSKILL.md",[],[],{"skillNo":55,"name":56,"nameZh":6,"description":57,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":58,"categories":59,"keywords":60,"publishedAt":19},"01M79RYFM9ATWHM6HAXJ3D19WV","tk-studio-research","Run a chartered research pass — survey the topics a charter scopes, grade every finding by evidence, land the findings in the run workspace, and emit observation events for recommendation-worthy ones. Use when the user says \"run the ecosystem watch\", \"tk research\", or a research job's invoke-skill directive names this skill.","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-research\u002FSKILL.md",[],[],{"skillNo":62,"name":63,"nameZh":6,"description":64,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":65,"categories":66,"keywords":67,"publishedAt":19},"01M79RYA1AWZZF83KBHGR6JDN3","tk-studio-report","One-verb human defect report — \"the skill did the wrong thing\" — recorded as a report event in the local measurement ledger. Use when the user says \"tk report\", \"report a defect\", \"that skill misbehaved\", or \"log a studio issue\".","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-report\u002FSKILL.md",[],[],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":74,"publishedAt":19},"01M79RY33VMYR5YNPVFKWRNW9S","tk-studio-playable-judge","The playable-verify leg's screenshot judge (Story 34.3) — reads only a playable run's report.json and its frames, answers the fixed rubric (scene loaded, player visible and framed, no magenta or error overlay, HUD reads, the named moments shown) with pass or fail and findings each naming a frame and a rubric line. Driven headless by the supervisor's host leg on the project's sandbox tier; use when the payload names a playable `media` folder, or the user says \"judge the playable frames\".","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-playable-judge\u002FSKILL.md",[],[],{"skillNo":76,"name":77,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":81,"publishedAt":19},"01M79RXY4AM8JM6ZK2MAR3W3X2","tk-studio-plan-sync","Planning adapter — canonicalize BMad-native planning artifacts (normalize pass, sole id authority) and sync them with the bound planning backend. Use when the user says \"sync the plan\", \"normalize planning artifacts\", \"tk plan sync\", or after stock BMad planning flows produce or change epics\u002Fstories.","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-plan-sync\u002FSKILL.md",[],[],{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":19},"01M79RXRDV34SQ7160973XEZ1A","tk-studio-orchestrator","The studio's front door — resolve who you are (role, per-user store), what this project needs (confirmed working_set.\u003Crole>), and route to resources by name, statelessly. Use when the user says \"enter the studio\", \"tk studio\", \"orchestrate\", \"convene the council\", or asks what to work on through the studio.","https:\u002F\u002Fgithub.com\u002Fthkennedy\u002Ftk-studio\u002Fblob\u002Fmain\u002Fplugins\u002Ftk-studio\u002Fskills\u002Ftk-studio-orchestrator\u002FSKILL.md",[],[]]