From e0388b1f6f884367fc74e684ae9c3867c87ae784 Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Thu, 9 Apr 2026 10:17:52 -0500 Subject: [PATCH] Fix --tier1-count CLI flag + set Stage on task creation + update docs - Fix --tier-1 -> --tier1-count in create_task_set.py and create-task-set.md skill - Set initial Stage field when creating tasks (run_cora for most, draft for PR) - Add Quick Reference section to clickup_runner README with stage tables - Add Stage field docs to clickup-task-creation.md - Remove stale xlsx_urls tests from test_claude_runner.py Co-Authored-By: Claude Opus 4.6 (1M context) --- clickup_runner/README.md | 38 +++++++++++++++++++ docs/clickup-task-creation.md | 26 ++++++++++++- scripts/create_task_set.py | 12 +++++- skills/create-task-set.md | 10 +++-- .../test_clickup_runner/test_claude_runner.py | 13 ------- 5 files changed, 80 insertions(+), 19 deletions(-) diff --git a/clickup_runner/README.md b/clickup_runner/README.md index 7ecfe99..7a6d68e 100644 --- a/clickup_runner/README.md +++ b/clickup_runner/README.md @@ -16,6 +16,44 @@ export CLICKUP_SPACE_ID="..." uv run python -m clickup_runner ``` +## Quick Reference: Running a Task + +To delegate a task to the runner, it needs **all three fields** set or it will be skipped: + +1. **Work Category** -- which task type (dropdown) +2. **Stage** -- which pipeline step to run (dropdown) +3. **Delegate to Claude** -- checked (checkbox) + +Plus the task must have a **due date <= today** and be in an **"Overall" list**. + +### Initial Stage by Task Type + +| Task Type | First Stage | What it does | Full pipeline | +|-----------|-------------|--------------|---------------| +| Content Creation | `run_cora` | Submits Cora analysis | `run_cora` -> `outline` -> `draft` | +| On Page Optimization | `run_cora` | Submits Cora analysis | `run_cora` -> `outline` -> `draft` -> `hidden div` | +| Press Release | `draft` | Writes full PR + schemas | `draft` (single stage) | +| Link Building | `run_cora` | Submits Cora analysis | `run_cora` -> `build` | + +### After Each Stage + +The runner **unchecks** "Delegate to Claude" and sets status to **Review** so you can check the output. To continue to the next stage, review the output, then re-check "Delegate to Claude". The Stage field is advanced automatically. + +### Required Custom Fields by Task Type + +| Task Type | Required Fields | +|-----------|----------------| +| Content Creation | Keyword, IMSURL (for run_cora) | +| On Page Optimization | Keyword, IMSURL (for run_cora) | +| Press Release | Keyword, IMSURL | +| Link Building | Keyword, IMSURL, CLIFlags (`--tier1-count N`) | + +### If Something Goes Wrong + +The runner sets the **Error** checkbox, posts a comment explaining what failed and how to fix it, unchecks "Delegate to Claude", and sets status to **Review**. Fix the issue, then re-check "Delegate to Claude" to retry. + +--- + ## How It Works 1. Every 720 seconds, polls all "Overall" lists in the ClickUp space diff --git a/docs/clickup-task-creation.md b/docs/clickup-task-creation.md index 8aed5fd..02532c0 100644 --- a/docs/clickup-task-creation.md +++ b/docs/clickup-task-creation.md @@ -5,7 +5,8 @@ ```bash uv run python scripts/create_clickup_task.py --name "LINKS - keyword" --client "Client Name" \ --category "Link Building" --due-date 2026-03-18 --tag mar26 --time-estimate 2h \ - --field "Keyword=keyword" --field "IMSURL=https://example.com" --field "LB Method=Cora Backlinks" + --field "Keyword=keyword" --field "IMSURL=https://example.com" --field "LB Method=Cora Backlinks" \ + --dependency TASK_ID ``` ## Defaults @@ -20,11 +21,23 @@ uv run python scripts/create_clickup_task.py --name "LINKS - keyword" --client " Any field can be set via `--field "Name=Value"`. Dropdowns are auto-resolved by name (case-insensitive). +## Stage Field (Required for ClickUp Runner) + +Every task must have a **Stage** set or the runner will skip it. Set Stage to the first stage for the task type: + +| Task Type | Initial Stage | +|-----------|---------------| +| Content Creation | `run_cora` | +| On Page Optimization | `run_cora` | +| Press Release | `draft` | +| Link Building | `run_cora` | + ## Task Types ### Link Building - **Prefix**: `LINKS - {keyword}` - **Work Category**: "Link Building" +- **Stage**: `run_cora` - **Required fields**: Keyword, IMSURL - **LB Method**: default "Cora Backlinks" - **CLIFlags**: only add `--tier1-count N` when count is specified @@ -35,6 +48,7 @@ Any field can be set via `--field "Name=Value"`. Dropdowns are auto-resolved by ### On Page Optimization - **Prefix**: `OPT - {keyword}` - **Work Category**: "On Page Optimization" +- **Stage**: `run_cora` - **Required fields**: Keyword, IMSURL - **time estimate**: 3h - @@ -42,13 +56,15 @@ Any field can be set via `--field "Name=Value"`. Dropdowns are auto-resolved by ### Content Creation - **Prefix**: `CREATE - {keyword}` - **Work Category**: "Content Creation" +- **Stage**: `run_cora` - **Required fields**: Keyword - **time estimate**: 4h ### Press Release - **Prefix**: `PR - {keyword}` -- **Required fields**: Keyword, IMSURL - **Work Category**: "Press Release" +- **Stage**: `draft` +- **Required fields**: Keyword, IMSURL - **PR Topic**: if not provided, ask if there is a topic. it can be blank if they respond with none. - **time estimate**: 1.5h - **Headline tone trigger words**: By default, the PR writer assumes the company already offers the capability (awareness tone). To get announcement-style headlines (Announces, Launches, Introduces), include one of these words in the task name or PR Topic: @@ -61,6 +77,12 @@ Any field can be set via `--field "Name=Value"`. Dropdowns are auto-resolved by The `clickup_create_task` tool provides the same capabilities via CheddahBot UI. Arbitrary custom fields are passed as JSON via `custom_fields_json`. +## Dependencies and Due Dates + +When a CREATE (NEW) task exists, LINKS and PR tasks that need the new page URL should be **blocked by** the CREATE task using `--dependency`. Their due date should be **one week after** the CREATE task's due date. For example, if CREATE is due 4/9, the LINKS and PR tasks should be due 4/16. + +OPT tasks already have a URL, so LINKS paired with an OPT does **not** need a dependency -- just the +1 week due date offset. + ## Client Folder Lookup Tasks are created in the "Overall" list inside the client's folder. Folder name is matched case-insensitively. diff --git a/scripts/create_task_set.py b/scripts/create_task_set.py index b9873d8..6d4c628 100644 --- a/scripts/create_task_set.py +++ b/scripts/create_task_set.py @@ -50,6 +50,13 @@ WORK_CATEGORIES = { "PR": "Press Release", } +INITIAL_STAGES = { + "NEW": "run_cora", + "OPT": "run_cora", + "LINKS": "run_cora", + "PR": "draft", +} + def _date_to_unix_ms(date_str: str) -> int: """Convert YYYY-MM-DD to Unix milliseconds (noon UTC).""" @@ -199,6 +206,9 @@ def main(): client.set_custom_field_smart( task_id, list_id, "Work Category", WORK_CATEGORIES[task_type] ) + client.set_custom_field_smart( + task_id, list_id, "Stage", INITIAL_STAGES[task_type] + ) client.set_custom_field_smart(task_id, list_id, "Delegate to Claude", "true") # IMSURL @@ -211,7 +221,7 @@ def main(): client.set_custom_field_smart(task_id, list_id, "BrandedPlusRatio", "0.80") if args.articles: client.set_custom_field_smart( - task_id, list_id, "CLIFlags", f"--tier-1 {args.articles}" + task_id, list_id, "CLIFlags", f"--tier1-count {args.articles}" ) if args.anchors: client.set_custom_field_smart( diff --git a/skills/create-task-set.md b/skills/create-task-set.md index 17b1771..14db94b 100644 --- a/skills/create-task-set.md +++ b/skills/create-task-set.md @@ -33,7 +33,7 @@ Creates a coordinated set of ClickUp tasks for a client keyword. The user specif ## Optional -- **articles**: Number of tier-1 articles for LINKS task. Sets CLIFlags to `--tier-1 {N}`. +- **articles**: Number of tier-1 articles for LINKS task. Sets CLIFlags to `--tier1-count {N}`. - **anchors**: Custom anchor texts for LINKS task. Comma-delimited string goes into CustomAnchors field. - **priority**: Default is 2 (High). - **assignee**: Default is 10765627 (Bryan Bigari). @@ -68,26 +68,30 @@ When a URL is provided (or OPT is used instead of NEW): ### LINKS (Link Building) - Work Category: "Link Building" +- Stage: "run_cora" - LB Method: "Cora Backlinks" - BrandedPlusRatio: "0.80" - IMSURL: the url (if available) -- CLIFlags: `--tier-1 {N}` (only if articles specified) +- CLIFlags: `--tier1-count {N}` (only if articles specified) - CustomAnchors: comma-delimited anchors (only if provided) - Time estimate: 9000000 ms ### PR (Press Release) - Work Category: "Press Release" +- Stage: "draft" - PR Topic: the pr_topic value - IMSURL: the url (if available) - Time estimate: 5400000 ms ### NEW (Content Creation) - Work Category: "Content Creation" +- Stage: "run_cora" - IMSURL: not set (URL does not exist yet) - Time estimate: 14400000 ms ### OPT (On Page Optimization) - Work Category: "On Page Optimization" +- Stage: "run_cora" - IMSURL: the url (required) - Time estimate: 7200000 ms @@ -128,4 +132,4 @@ User: "Create OPT, LINKS for RPM Rubber, keyword rubber seals, url https://rpmru User: "Create LINKS, PR, NEW for RPM Rubber, keyword rubber gaskets, due April 15, tag apr26, pr topic RPM launches gasket line, 4 articles, anchors rubber gaskets,custom rubber gaskets,industrial rubber gaskets" --> Same as first example but LINKS also gets CLIFlags=`--tier-1 4` and CustomAnchors=`rubber gaskets,custom rubber gaskets,industrial rubber gaskets` +-> Same as first example but LINKS also gets CLIFlags=`--tier1-count 4` and CustomAnchors=`rubber gaskets,custom rubber gaskets,industrial rubber gaskets` diff --git a/tests/test_clickup_runner/test_claude_runner.py b/tests/test_clickup_runner/test_claude_runner.py index 0fdc1ce..2423b91 100644 --- a/tests/test_clickup_runner/test_claude_runner.py +++ b/tests/test_clickup_runner/test_claude_runner.py @@ -104,19 +104,6 @@ class TestBuildPrompt: prompt = build_prompt(task, route, "skill content") assert "Write about blue widgets" in prompt - def test_includes_xlsx_urls(self): - task = _make_task() - route = _make_route() - urls = ["https://cdn.clickup.com/report.xlsx"] - prompt = build_prompt(task, route, "skill", xlsx_urls=urls) - assert "https://cdn.clickup.com/report.xlsx" in prompt - assert "Cora Reports" in prompt - - def test_no_xlsx_section_when_none(self): - task = _make_task() - route = _make_route() - prompt = build_prompt(task, route, "skill") - assert "Cora Reports" not in prompt def test_no_customer_when_missing(self): task = _make_task(custom_fields={})