CheddahBot/system-design-spec.md

8.0 KiB

ClickUp + Claude Code Automation System -- Design Spec

Overview

This document describes a system that polls ClickUp for tasks triggered by a checkbox, routes them to the correct Claude Code skill based on task type and stage, runs Claude Code in headless mode, posts results back to ClickUp, and advances the task through its lifecycle.

The user has existing code that should be refactored or replaced to match this design. Review the existing code and determine what can be reused vs rewritten.


1. ClickUp Status System (replaces the current 14-status setup)

The old statuses are confusing because they encode WHAT is happening instead of WHO OWNS THE TASK. The new system has 7 statuses organized by ownership:

Status Owner Replaces
To Do Nobody To Do
In Progress Human In Progress, After Client Feedback
Needs Input Human (blocked) Needs Input
AI Working Claude Code Automation Underway, Running CORA
Review Human Outline Review, Outline Approved, PR Needs Review, Internal Review
Client Review Client Client Review
Complete Nobody Ready to Use, Complete

Key decisions:

  • "After Client Feedback" is just "In Progress" again -- same owner, same state.
  • "Error" is NOT a status. It becomes a checkbox custom field that can be flagged on any status.
  • "Running CORA" and "Automation Underway" collapse into "AI Working" -- the WHAT is tracked by the Stage field, not the status.

2. Custom Fields Required

These custom fields need to exist in ClickUp:

  • Run Claude (checkbox) -- when checked, the poller picks up the task and spawns a Claude Code session. Gets unchecked after processing so it does not re-trigger.
  • Stage (dropdown) -- tracks where in the content lifecycle the task is. Values: report, outline, draft, final. This is independent of status. "Review" + Stage:Outline = the old "Outline Review". "AI Working" + Stage:Draft = AI is writing the draft.
  • Error (checkbox) -- flagged when Claude Code errors out. Can happen at any status. Not a status itself.

3. Skill Routing (task type + stage -> skill)

When a task hits "AI Working", the script looks at the task type AND the current stage to decide which skill to load. The skill file contents get passed to Claude Code via --append-system-prompt. After AI finishes, the stage advances and the task moves to the appropriate next status.

The routing is defined in a SKILL_MAP dictionary:

task_type -> stage -> {
    skill_file:   path to SKILL.md
    next_stage:   what stage to set after AI finishes
    next_status:  where the task goes after AI finishes
    tools:        which Claude Code tools to allow
}

Example for "content" task type:

content:
  report  -> skill: make_outline   -> next: stage=outline, status=review
  outline -> skill: make_content   -> next: stage=draft,   status=review
  draft   -> skill: finalize       -> next: stage=final,   status=review or client review

The user has multiple task types (content, data_report, client_deliverable). Each has its own stage progression and skill chain.

Skills are NOT invoked via slash commands (those only work in interactive mode). Instead, the SKILL.md file is read from disk and passed as instructions:

claude -p "Task prompt here" \
  --append-system-prompt "$(cat /path/to/SKILL.md)" \
  --output-format json \
  --allowedTools "Read,Edit,Bash" \
  --max-turns 10

4. Multi-List Polling

The user has 20+ folders in one ClickUp space, each containing a list named "Overall". The script needs to poll across all of them.

The approach is a hardcoded dictionary mapping folder names to list IDs:

LIST_IDS = {
    "folder_a": "list_id_1",
    "folder_b": "list_id_2",
    # ... 20+ entries
}

The poller iterates through all lists each cycle, collecting any tasks where the "Run Claude" checkbox is checked. Each task gets tagged with its source folder name for logging. If one list fails to poll, the error is logged and the poller continues with the remaining lists.

At 60-second intervals with 20+ lists, this is roughly 20 requests per minute, well within ClickUp's 100/min rate limit.


5. Claude Code Headless Configuration

Key flags and decisions:

  • Do NOT use --bare -- the runner operates on the user's own machine, so CLAUDE.md project instructions, MCP servers from .mcp.json, and local config should all load automatically.
  • Use --mcp-config if specific MCP servers are needed per-task beyond what is in the project config.
  • Use -p with the task prompt for non-interactive mode.
  • Use --append-system-prompt to inject skill instructions.
  • Use --output-format json to get structured results with session metadata.
  • Use --max-turns to cap cost and runtime (default 10).
  • Use --allowedTools scoped per stage. Available built-in tools: Read, Edit, MultiEdit, Write, Bash, Glob, Grep, WebFetch, WebSearch, NotebookEdit, TodoWrite.
  • Bash can be scoped: Bash(git:), Bash(python:), Bash(npm test), etc.
  • MCP tools use the naming pattern: mcp__{server_name}{tool_name} Allow all from a server with: mcp__servername*

6. Workflow: What Happens When a Task Is Triggered

  1. Poller finds a task with "Run Claude" checkbox checked.
  2. Script reads the task type and current stage.
  3. Looks up the skill routing in SKILL_MAP.
  4. If no mapping found, posts a warning comment and unchecks the box.
  5. Sets task status to "AI Working".
  6. Loads the skill file from disk.
  7. Builds a prompt from the task name and description.
  8. Runs Claude Code headless with the skill as system prompt.
  9. On success:
    • Advances the Stage to the next value.
    • Sets the status to the next value (usually "Review").
    • Posts the result as a task comment.
  10. On error:
    • Flags the Error checkbox.
    • Sets status to "Review" (so the human sees it).
    • Posts the error as a task comment.
  11. Always unchecks "Run Claude" so it does not re-trigger.

7. The Typical Content Workflow End-to-End

[To Do]
  |
  v
[In Progress] -- human runs CORA report (java program, already handled)
  |
  | (human checks "Run Claude", sets stage to "report")
  v
[AI Working] -- skill: make_outline, stage: report
  |
  | (AI finishes, stage -> outline, status -> review)
  v
[Review] -- human checks the outline
  |
  | (human approves: checks "Run Claude")
  v
[AI Working] -- skill: make_content, stage: outline
  |
  | (AI finishes, stage -> draft, status -> review)
  v
[Review] -- human checks the draft
  |
  | (human approves: checks "Run Claude" OR does manual edits first)
  v
[AI Working] -- skill: finalize, stage: draft
  |
  | (AI finishes, stage -> final, status -> client review or complete)
  v
[Client Review] or [Complete]
  |
  | (if client sends revisions)
  v
[In Progress] -- human works on revisions (this is NOT "After Client Feedback",
                  it is just In Progress again)

8. Terminal Compatibility

All output strings must be pure ASCII. No emojis, no unicode arrows, no em dashes. Use [OK], [ERROR], [WARNING] as prefixes in comments and logs. Use -> instead of arrows. Use -- instead of em dashes.


9. File Structure

The reference implementation is in clickup_claude_runner.py. Key sections:

  • CONFIG: tokens, list IDs, custom field IDs, repo path
  • SKILL_MAP: task type + stage -> skill routing
  • ClickUp API helpers: get tasks, update status, set fields, post comments
  • Claude Code runner: load skill, build command, run subprocess, parse output
  • Main loop: poll, process, sleep

The user's existing code may have different structure. Evaluate what can be kept vs what should be replaced to match this design.