skills/tt-workflow-run/SKILL.md
--- name: tt-workflow-run description: Tasktracker-native autonomous build-loop orchestrator. Drives a first-class `workflow_run` end-to-end — create the run (Gate 1 lifecycle completeness + Gate 2 zero-defects-in), then loop while `getNextReadyTask(projectId)` returns a slice — `setActiveTask` → record a pre-slice `scanArchitectureDrift` baseline → delegate the slice to `/tt-implement-phase` (which does the code work, registers the architecture delta in-slice, and auto-logs defects/learnings/fr
npx skillsauth add mhylle/claude-skills-collection skills/tt-workflow-runInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A thin, tasktracker-native orchestrator that turns an autonomous Claude Code build loop into a visible, auditable, resumable, projectable workflow_run. It is the session layer between the loop (which executes) and the tasktracker spine (project → phase → task → requirement+AC → time-log → insight).
It does not merely record the loop — it enforces the lifecycle through three backend gates that already exist (shipped in P99–P101), measures real build-time per slice, auto-logs insights, verifies architecture is followed each slice, and re-projects remaining effort from measured time only as an append-only versioned series.
Design source of truth: brainstorm
92898896→ "Solution Description (v1)" docba161e43-7697-4167-9fc7-f314ca68c3af. The four forks are LOCKED (A: new entity; B: loose coupling; C: lean v1 dogfooded, UI v2; D: getTimeLog preview folded in). Re-read it before changing this skill.
| | /tt-implement-plan | /tt-workflow-run (this skill) |
|---|---|---|
| Unit driven | the plan's phase tasks, in author order | a first-class workflow_run over the next-ready backlog |
| Slice selection | next pending phase | tasktracker_getNextReadyTask(projectId) — dependency-free, ranked |
| Pre-conditions | readiness plan row satisfied | Gate 1 (lifecycle completeness) + Gate 2 (zero-defects-in), enforced server-side as 412 |
| Per-slice gates | none | Gate 3 architecture-followed (drift hash-diff) + Gate 2 defect-delta, every slice |
| Time / projection | none | getTimeSummary measured ms → workflow_recordIteration appends a by-layer projection version |
| Run entity | none | workflow_run row with status machine + projection series; resumable across sessions |
| Insight logging | per-phase, via /tt-implement-phase | per-phase plus the run's OWN insights (no-data friction, patterns, new-defect) |
If you just want to execute phases in order with no measured run, use /tt-implement-plan. If you want the run entity + measured projection + per-slice gates on top, use this skill. Both delegate the actual code work to /tt-implement-phase.
THIS SESSION IS THE RUN ORCHESTRATOR + the SINGLE writer of run state. IT NEVER IMPLEMENTS CODE DIRECTLY — it delegates each slice to
/tt-implement-phase. That holds in every environment. One level down,/tt-implement-phasewrites code via subagents when a subagent-dispatch tool exists, or in-context itself when none does (its graceful-degradation mode). Run-state writes (workflow_* MCP calls) are always yours and always main-context, regardless of mode.
tt-workflow-run (this session — RUN ORCHESTRATOR + the SINGLE writer of run state)
│ ⛔ NEVER writes code ⛔ NEVER uses Write/Edit (in EVERY mode)
└── /tt-implement-phase (per-slice executor)
├── orchestrated mode → Subagents write code / create files / run tests
└── in-context mode → tt-implement-phase does it directly (no subagent tool)
| DO (this session) | DO NOT |
|---|---|
| Create + drive the workflow_run (start/record/pause/complete) | Write code or edit files |
| setActiveTask / pauseActiveTask / clearActiveTask | Edit a locked phase body (HTTP 422) |
| Pick the next slice via getNextReadyTask | Invent a new backend Workflow API (none exists — Fork B) |
| Delegate each slice to /tt-implement-phase | Estimate a slice's duration (principle #11) |
| Run the two start gates + three per-slice gates | Advance past a blocked gate without resolving it |
| Log the run's own defects/learnings/frictions | Bury insights in chat narrative |
This skill is main-loop-only: it calls sibling skills via the Skill tool (which stays in the main loop), never via an Agent dispatch. The tasktracker_workflow_* MCP tools are module-cached at MCP boot — if one isn't visible, the MCP needs a restart (mcp-server/CLAUDE.md), not a workaround. This restart caveat applies ONLY to those MCP tools. The built-in Workflow and Agent tools are top-level main-loop tools — never MCP, never in ToolSearch, never affected by an MCP restart; don't conflate the two.
Exact tool names — note the tasktracker_ prefix + workflow_ infix:
| Tool | Role in the loop | Active-task |
|---|---|---|
| tasktracker_workflow_startRun | Create + start a run; runs Gate 1 + Gate 2; 412 on failure (run persists as enforcing). acknowledgeBaseline: true pins the current open-defect count as the run baseline (bootstrap fallback only). | EXEMPT (first-contact, like createProject) |
| tasktracker_getNextReadyTask | Pick the next dependency-free slice. null ⇒ backlog drained ⇒ complete. | EXEMPT (read) |
| tasktracker_workflow_setCurrentTask | Point the run at the picked slice (no iteration recorded). | requires active task (heartbeats) |
| tasktracker_scanArchitectureDrift | Baseline (slice start) + re-scan (slice end). Returns bucketed missing / stale / orphaned. | EXEMPT (read) |
| tasktracker_getDefectStats | Re-read .open for the Gate 2 delta vs baseline. | EXEMPT (read) |
| tasktracker_getTimeSummary | Measured (stopped-segment-only) ms for the slice task. | EXEMPT (read) |
| tasktracker_workflow_recordIteration | Runs the advance gates (Gate 3 reqs-link + architecture-followed, Gate 2 defect-delta), records the iteration, appends a projection version, and auto-logs the "no data" friction when hadHeartbeatData: false. 412 on a blocked advance (run → blocked_on_gate). | requires active task (heartbeats) |
| tasktracker_workflow_pauseRun / resumeRun | Pause before a human gate / resume after. | requires active task |
| tasktracker_workflow_completeRun / abandonRun | Terminal. | requires active task |
| tasktracker_workflow_getRun / listRuns | Inspect run + projection series. | EXEMPT (read) |
recordIteration key inputs (get these right — they drive the gates + projection):
runId, taskId, layer (required) — layer ∈ backend|frontend|fullstack|docs|infra (project-estimate.py taxonomy).measuredMs — from getTimeSummary (bigint → pass as a string for large values; defaults to 0).measurementSource — 'timer' (summed stopped segments) or 'wallclock' (manual fallback).hadHeartbeatData — false when the slice produced no heartbeat-backed time-log data → triggers the run's own "no data" friction (R3 / principle #11).baselineDrift / drift — the slice-START and slice-END scanArchitectureDrift outputs as {missing:[file...], stale:["componentId:file"...], orphaned:["componentId:file"...]} key arrays. Net-new at end vs baseline blocks the advance + logs a defect.remainingByLayer — per-layer count of the run's remaining pending dependency-free slices: the projection multiplier (projected = Σ_layer remaining×mean). Omit the whole object on the final slice and the projection trends to ~0.projectionTrigger — defaults 'slice_complete'; use 'scope_change' / 'defect_baseline_shift' for re-projections not driven by a finished slice.1. tasktracker_listProjects({search}) — or use the argument hint → projectId.
2. tasktracker_getProject({projectId}) — confirm solutionDescription is non-null (Gate 1 input).
3. tasktracker_getProjectReadiness({projectId}) — the requirements row must read `satisfied`
and unlinkedTaskCount must be 0 for the run's subtree (Gate 1).
4. tasktracker_getDefectStats({projectId}) — note `.open`. This is the Gate 2 input AND the run baseline.
Do not try to pre-satisfy a gate by editing data behind the run's back. If Gate 1 will fail (no solution description, requirements not satisfied, unlinked tasks), STOP — the project needs /tt-create-plan / requirement linking first. If Gate 2 will fail (open defects), see the Bootstrap nuance below — the strict posture refuses to start; the documented fallback is acknowledgeBaseline.
tasktracker_workflow_startRun({
projectId,
name: "<run name>",
acknowledgeBaseline: <true ONLY for the documented bootstrap case — see below>,
brainstormId?: <originating brainstorm>,
sessionId?: <current session>,
})
running with baselineDefectCount = 0. Strict zero-defects-in now holds for the whole run.enforcing so you can inspect the structured body (unlinked task ids / open-defect count). Fix the cause (link tasks, resolve defects, or acknowledge the baseline) and retry — do not route around the gate.startRun is active-task-EXEMPT (it CREATES the run). Right after a successful start, the first setActiveTask (Step 2) begins heartbeating.
A project that already has an open defect cannot pass strict Gate 2 — but that open defect is often exactly the first thing the loop should fix. The documented bootstrap path:
acknowledgeBaseline: true. The run pins baselineDefectCount = N (the current open count). Only NEW defects above N block advance; the N pre-existing ones don't.tasktracker_createFixTask({defectInsightId}) — stamps metadata.fixesInsightId so the fix-time rolls into getDefectStats.timeOnDefects). Running it resolves the defect, dropping .open back toward 0.acknowledgeBaseline is a one-time bootstrap concession, not a routine knob. Once the backlog is clean, future runs start strict (baseline 0).
Loop while getNextReadyTask returns a slice. Each iteration is the brainstorm's per-slice protocol, one step per call:
loop:
slice = tasktracker_getNextReadyTask({projectId})
if slice == null: break # backlog drained → Step 3
# 2a. Focus + point the run at the slice
tasktracker_setActiveTask(slice.id) # heartbeats start; digest carries principles + body
tasktracker_workflow_setCurrentTask({runId, taskId: slice.id})
# 2b. Pre-slice drift baseline (Gate 3 input — baseline-subtracted so only the slice's delta is charged)
baselineDrift = tasktracker_scanArchitectureDrift({projectId}) # capture missing/stale/orphaned key arrays
# 2c. Delegate the actual work to /tt-implement-phase
Skill(skill="tt-implement-phase"): Execute the slice.
Context: project, phase/slice task id = slice.id, sub-tasks (getTask), linked requirements + ACs,
principles from the setActiveTask digest.
/tt-implement-phase: does the code (via subagents), registers the architecture delta in the SAME
slice (principle #8 — so the slice's own drift re-scan stays clean), runs verification-loop +
code-review + ADR compliance, and auto-logs defects/learnings/frictions with relatedTaskId = slice.id.
# 2d. Slice-end gate inputs
endDrift = tasktracker_scanArchitectureDrift({projectId}) # Gate 3 delta
defects = tasktracker_getDefectStats({projectId}) # Gate 2 delta (.open vs run baseline)
time = tasktracker_getTimeSummary({taskId: slice.id}) # measured (stopped-only) ms
layer = classify(slice) # backend|frontend|fullstack|docs|infra
# 2e. Record the iteration → appends a projection version (runs the advance gates server-side)
tasktracker_workflow_recordIteration({
runId, taskId: slice.id, layer,
measuredMs: time.totalMs (as string if large),
measurementSource: time had data ? 'timer' : 'wallclock',
hadHeartbeatData: <true iff getTimeSummary returned measured data>,
baselineDrift, drift: endDrift,
remainingByLayer: <per-layer count of remaining pending dependency-free slices>, # omit on last slice
})
# 412 here ⇒ the advance was BLOCKED (Gate 3 net-new drift / missing req link, or Gate 2 new defect).
# The run is now blocked_on_gate. Resolve the cause (re-register the arch delta, link the requirement,
# or fix the new defect) within /tt-implement-phase, then re-record. Do NOT skip the slice.
# 2f. Pause before any human gate
tasktracker_pauseActiveTask() # before any message that waits on the human (their think-time isn't work)
/tt-implement-phase logs the slice's insights. ON TOP of that, this skill logs the run's own:
| Condition | Tool | Notes |
|---|---|---|
| The slice produced no heartbeat-backed time data | recordIteration({hadHeartbeatData:false}) auto-logs it, OR tasktracker_logFriction | "no data" — never estimate the duration (principle #11). The auto-log stamps run_id + iteration_id. |
| A notable cross-slice pattern emerged | tasktracker_logLearning | E.g., "drift baseline-subtraction means an unrelated stale component never blocks an unrelated slice." |
| .open rose above the run baseline | tasktracker_logDefect | The new error is "ours" (principle #14). recordIteration also blocks the advance. |
Per R3, auto-logged slice insights carry relatedTaskId = slice.id AND their metadata is stamped with run_id + iteration_id (no new column). When you log the run's own insight manually, set relatedTaskId to the slice and reference the run/iteration in the body.
Map the slice to the project-estimate.py taxonomy: frontend (Angular templates/components/styles), backend (NestJS services/controllers/entities), fullstack (both in one slice), docs (markdown/skill files), infra (CI, compose, migrations-only). When a slice touches both backend + frontend, classify fullstack (don't split the iteration).
When getNextReadyTask returns null:
tasktracker_workflow_completeRun({runId}) # terminal; sets ended_at
tasktracker_workflow_getRun({runId}) # confirm the projection series is populated
tasktracker_getDefectStats({projectId}) # confirm .open == baseline (no nets introduced)
tasktracker_clearActiveTask()
Then emit the closing summary (counts, the projection series, insights logged this run, manual verification still owed) in chat — not as a file. If the run ended deliberately short, use abandonRun instead and say why.
Loose coupling (Fork B) means /loop is orthogonal: you can wrap the whole skill in /loop <interval> /tt-workflow-run <project> for an unattended cadence, but the run does not depend on it. The run entity + gates + projection are pure tasktracker data; /loop just re-invokes the skill, which resumes the same run (the active-task pointer + run row persist across sessions).
A 412 is the system working, not a failure to route around.
| Where | Cause | Resolution |
|---|---|---|
| startRun 412 | Gate 1: no solution description / requirements not satisfied / unlinked tasks | Fix the lifecycle (/tt-create-plan, link requirements), then retry. |
| startRun 412 | Gate 2: open defects, no ack | Resolve them, OR start with acknowledgeBaseline: true and clear them as inaugural slices. |
| recordIteration 412 | Gate 3: net-new drift (a new file no component cites; an edited component whose driftHash wasn't refreshed) | Register/refresh the architecture component in-slice (principle #8), then re-record. |
| recordIteration 412 | Gate 3: the slice task has no requirement link in its hierarchy | Link it to a requirement, then re-record. |
| recordIteration 412 | Gate 2: .open rose above baseline | The new defect is ours — fix it (a follow-up slice), then re-record. |
On any 412 the run sits in enforcing (start) or blocked_on_gate (advance). pauseActiveTask before surfacing the blocker to the user; resume from the blocked step, not from Step 0.
/tt-implement-phase.measuredMs.setActiveTask before a slice — no heartbeats, no measured time, the projection goes blind.acknowledgeBaseline: true as a routine convenience. It is a one-time bootstrap concession; once clean, start strict.setActiveTask) is the locked contract.logDefect/logLearning/logFriction.Agent-dispatched subagent — invoke it in the main loop; call siblings via Skill..open) noted.startRun ran the gates; strict by default; acknowledgeBaseline used ONLY for the documented bootstrap case, with the inaugural slice resolving a baseline defect.setActiveTask + setCurrentTask, captured a pre-slice drift baseline, delegated to /tt-implement-phase, re-scanned drift + re-read defect stats, read getTimeSummary, and recordIteration'd with the right layer + drift arrays + remainingByLayer.relatedTaskId + run_id/iteration_id; the run logged its OWN "no data" friction whenever heartbeating was off.pauseActiveTask before every human-gate message.completeRun; projection series populated; .open back at baseline; clearActiveTask./tt-implement-phase (via its subagents, or its in-context mode when no subagent tool exists).This skill reuses the tasktracker references already shipped with the sibling skills:
/tt-implement-phase references/insight-cookbook.md — when/how to logDefect / logLearning / logFriction./tt-workflow-audit references/workflow-tasktracker-contract.md — the shared tt-workflow-* contract (parent owns writes, no Date/RNG in any workflow script, MCP reachability, prod safety). This skill is sequential (single active task) — it does NOT use the parallel Workflow tool — but the active-task / locked-body / prod-write rules in that contract still apply. Namespace note: the tasktracker_workflow_* MCP tools this skill uses (workflow_startRun, workflow_recordIteration, …) are run-TRACKING tools — NOT the built-in Workflow orchestration tool; they only share the word "workflow". The siblings /tt-workflow-audit and /tt-workflow-build are the ones that invoke the built-in Workflow tool./tt-implement-phase — per-slice executor. This skill delegates every slice to it./tt-implement-plan — the no-run-entity alternative: executes phases in order without projection or per-slice gates./tt-create-plan — upstream: produces the requirements + linked tasks Gate 1 checks for./loop — optional outer cadence; orthogonal to the run./code-review, /verification-loop, /adr — quality gates inside /tt-implement-phase./tt-implement-phase (→ subagents) does the code; this session drives the run./loop optional.getTimeSummary (stopped segments); no heartbeat data ⇒ "no data" friction, never an estimate.acknowledgeBaseline is a one-time bootstrap.setActiveTask per slice, pauseActiveTask before human waits, clearActiveTask on completion.tools
--- name: tt-workflow-build description: Tasktracker-native trigger for a PARALLEL build via the Claude Code Workflow tool. Thin by design — it does two things, then drives to done: (1) ensure a tasktracker project exists (use the existing one, or create one), then (2) start a dynamic `Workflow` that builds it, tracking the work in tasktracker and using the build + verify skills. It does NOT analyze parallelism up front, ask the user to choose a mode, hand back, or fall back to a sequential skil
tools
--- name: grumpy-reviewer description: A single grumpy, nitpicky structural code reviewer that runs as an isolated subagent and treats the code as third-party work submitted by a junior programmer for validation. It cares about exactly one thing — maintainability — judged through separation of concerns, service-oriented design, helper-method extraction, small files, and the rule of 7 (as any grouping nears 7 members, it pushes for sub-groupings). It is deliberately kept OUT of the implementation
tools
Tasktracker-native project-wide parallel audit using the Claude Code Workflow tool (dynamic workflows). Partitions a repo / backlog / architecture and fans out read-only agents (one per partition) that return schema-checked findings, aggregates them into a deduplicated, ranked risk register, and OPTIONALLY writes fixes back as tasks under a Bug Fix phase — with all tasktracker writes done by the PARENT, never the parallel agents (single global active-task pointer). Journaled and resumable, so a rate-limit or crash mid-audit resumes without re-running completed partitions. Use for large, embarrassingly-parallel, read/analyze-heavy jobs where each unit is self-contained and the output aggregates — audit every file/component for risk, find all architecture drift (scanArchitectureDrift) or duplicate tasks (detectDuplicates/auditDuplicates), per-file tech-debt sweep, test-coverage or security-surface scan across a whole project. Triggers on "/tt-workflow-audit", "audit the whole repo", "parallel audit", "scan every file/component", "find all drift/duplicates", "tech-debt sweep (tasktracker)", or any whole-project analyze-at-scale request inside a session with a tasktracker project. Prefer this over /codebase-audit or /code-quality-audit when the project is tracked in tasktracker AND you want the findings written back as tasks; prefer it over team-* modes when the units don't need to negotiate live (they just report).
tools
Tasktracker-native plan orchestrator. Drives execution of a plan whose phases are stored as tasktracker phase tasks (NOT a docs/plans/*.md file). Honours active-task discipline (setActiveTask before every artifact-producing call, pauseActiveTask before user waits, clearActiveTask on phase done), respects the locked phase body (design notes go to sub-tasks, never to the phase description), logs defects/learnings/frictions through tasktracker insights as they surface, and delegates the actual implementation to /implement-phase. Use whenever the user wants to "implement the plan", "run the plan", or "work through the phases" AND the plan was created with /tt-create-plan (or otherwise lives in tasktracker). Triggers on "tt implement plan", "tt implement", "implement plan (tasktracker)", "/tt-implement-plan", "execute the tasktracker plan", or any plan-execution request inside a session that already has a tasktracker active task or project. Prefer this over the plain /implement-plan skill whenever a tasktracker MCP is available — it correctly handles the locked phase body, requirement linking, insight logging, and lifecycle gating that the file-based variant ignores.