Skip to main content

Markdown Task File to ClickUp SOP

1. Purpose & Scope

Convert a task markdown file living in Client_Delivery/<client>/work-in-progress/YYYY-MM/ into a ClickUp task in that client's active-work list under the Client Delivery space.

In scope:

  • Task files authored per the Google Drive Organization Standard naming pattern YYYY-MM-task-[assignee]-[description].md
  • One-task-per-file creation and subsequent updates

Out of scope:

  • SC-internal operations tasks -- use the SC Operations space per SC Task Management Workspace Setup SOP §6
  • Bulk/batch conversion of an entire monthly folder -- reserved for the deferred symphony-flow workflow (Section 7)

2. Prerequisites

  • Client has ClickUp List ID populated in their client profile at 09-clients/<client-slug>/<client-slug>-client-profile.md. If marked *(TBD)*, populate it before proceeding (see Section 7).
  • Task markdown file exists with YAML frontmatter containing at minimum title, assignee, priority, status.
  • ClickUp MCP access (for interactive runs) OR CLICKUP_API_KEY env var (for symphony-flow runs).

3. Field Mapping

ClickUp fieldSource in markdownConversion rule
list_idClient profile ClickUp List IDCopy literally
name# H1 of markdown bodyPrefer H1 over frontmatter title; H1 is the user-visible task title
markdown_descriptionFull body (from first ## Section onwards)Append a footer: **Source file:** <absolute path> and **Source call/context:** <frontmatter.source>
prioritypriority frontmatterMap: urgent -> urgent, high -> high, medium/normal -> normal, low -> low. Uses string values (not 1-4 integer)
assigneesassignee frontmatterExactly one assignee per ticket — see §3d. Pass as a single-element array. Resolve via clickup_resolve_assignees. For the authenticated user, "me" works; named users may need email.
statusstatus frontmatterMap: open → omit (ClickUp default to do); in_progressin progress; blockedblocked; completecomplete; cancelledcancelled. See SC Task Data Standard §1 for the canonical status model.
due_datedue_date frontmatter (if present)Format YYYY-MM-DD
custom_fieldsClientClient slug (matches the 09-clients/<slug>/ directory and the client's folder name in ClickUp)Client Delivery tasks only — required on every ticket. SC Operations tasks: skip (the field doesn't exist there; use the client/<slug> tag instead — see the tags row). First run: call clickup_get_custom_fields against the target list to discover the Client field's id and the option-value UUID for the client; cache thereafter (see Appendix A2). Set via the custom_fields parameter on clickup_create_task, or via clickup_update_task post-creation.
tagsDerived: domain tags implied by content; client/<slug> only when the target list does not encode the client (see rule below)Tags must pre-exist in the space -- see Section 5. For client/<slug> specifically: required on SC Operations tasks and Client Delivery shared lists (client-delivery-operations, reference); optional on per-client lists where the Client custom field is the canonical identifier. Tag–field correspondence: when both are populated on the same task, the tag slug must equal the Client field value. See SC ClickUp Tag Taxonomy §Client identity.

3a. Multi-doc handoffs -- doc precedence

When a task is supported by more than one document in the same work-in-progress/YYYY-MM/ folder (e.g., a build spec, a copy doc, and a strategy/brief doc), the contractor or assignee may not know which one is authoritative. Disagreements between docs default to:

spec  >  copy  >  strategy/brief
  • Spec (build spec, technical spec, layout/block-mapping doc) is authoritative for how the work is done. If spec says "no custom code", and copy says "add a custom carousel", the spec wins -- copy must be adapted.
  • Copy (the actual text/words to be used) is authoritative for what content goes where, within constraints set by the spec.
  • Strategy / brief (engagement brief, project plan, strategy doc) is authoritative for why the work matters and what success looks like -- but it is the lowest-precedence source for execution details. Use it to resolve ambiguity, not to overrule the spec.

When creating the ClickUp task description for a multi-doc handoff, link to all supporting docs and explicitly state which is the spec. Example:

**Supporting docs (in precedence order):**
- Spec: <drive-url-of-block-mapping-doc>
- Copy: <drive-url-of-copy-doc>
- Strategy: <drive-url-of-engagement-brief>

**Spec wins.** If copy disagrees with spec, escalate before implementing.

This convention was added after the rotary-sports build (2026-05) where a contractor read the copy doc without seeing the spec and proposed custom code that the spec had explicitly forbidden.

3b. Thin-ticket archetype

A well-formed ticket is a pointer, not a copy of the spec. It should fit on one ClickUp screen (~1,200 characters / ~200 words) and contain only what the assignee needs that does NOT already live in a referenced doc.

Canonical skeleton — see ClickUp Thin Ticket Template:

## Summary
<one or two sentences what is being done and why>

## Acceptance Criteria
- [ ] <verifiable bullet 1, maps to a phase / step of the linked SOP>
- [ ] <verifiable bullet 2>
- [ ] <up to ~6 bullets>
- [ ] Evidence: <screenshot / URL / log link the assignee will attach on completion — see [validation-evidence-rule](/reference/standards/sc-contractor-task-quality-pre-work-standard.md#validation-evidence-rule)>

## Reference docs
- Playbook: <SOP link team.symphonycore.com URL preferred; Drive PDF fallback only if the doc hasn't published to the wiki yet>
- Validation: <checklist link, if applicable>
- Client context: <client profile PDF link in Drive client-specific docs are NOT on the wiki>

## Inline values (only when not in any doc)
- <per-task one-off invoice number, specific URL, decision input>

---
**Source:** <markdown task-file path or URL>

Target size when populated: 600-1,000 characters. If a draft is heading past 1,500, stop and ask: "Which paragraph belongs in the SOP I'm linking to?" Move it there; link to it; keep going.

3c. Client-facing deliverables require a QA-Review subtask

For any ticket whose primary output is client-facing (published copy, brand assets, anything the client will see), the Creator adds a subtask named qa-review-<parent-slug> at ticket creation time. The subtask follows the standard flow (to doin progresscomplete). The QA Reviewer (default: the Account Manager — see ClickUp Ways of Working §Roles) is the assignee. The parent ticket cannot move to complete until the QA-Review subtask is closed.

This is a per-ticket pattern, decided manually by the Creator at creation. There is no automation that adds the subtask from a tag, frontmatter field, or other signal. Default = no QA-Review subtask; add it only when the output is client-facing.

Legacy compatibility: before 2026-06-14 this pattern was named am-review-<parent-slug> (assuming the Account Manager was the only QA Reviewer). Existing am-review-* subtasks remain valid as-is — do not rename them retroactively. New tickets use qa-review-*. Views that surface this queue must match BOTH patterns during the transition (see Views Guide §6.5).

Why: Replaces the workflow-wide review status (retired 2026-05). The synchronous review step had become a bottleneck — no dedicated reviewers meant tickets sat unreviewed for weeks. The QA-Review subtask localizes the QC step to the cases that genuinely need pre-release human checking (client-facing output), while internal/non-client work flows TODO → IN PROGRESS → COMPLETE on contractor self-attestation backed by the Evidence bullet (see validation-evidence-rule). Post-hoc sampling (see Review Standard §Post-hoc sampling) catches escaped defects across both paths.

What counts as client-facing: published website copy, brand assets, contracts/proposals, anything sent to the client by email, GHL workflows that touch the client's contacts, anything that lands in production where the client will see it. When in doubt, add the subtask.

What does NOT count: internal documentation, SOP edits, configuration tasks that don't change client-visible behavior, research spikes, prep work for a future ticket.

3d. One assignee per ticket (and per sub-task)

Every ClickUp ticket and every sub-task has exactly one assignee. No multi-assignee tickets, anywhere.

Why: Ownership and time-tracking accountability. When multiple people are on a ticket, it's not easy to see who logged what time, who's responsible for the next move, or who owns the close-out. The Timesheet Approval flow (see ClickUp Timesheet Approval SOP) gets ambiguous when entries on the same ticket come from two contributors without clear delineation.

How to apply:

  • Markdown task file assignee: frontmatter holds one name (not a list).
  • When creating via MCP, pass assignees as a single-element array.
  • When the work is genuinely shared (pair-work, two specialists on the same deliverable), split into either (a) sibling tickets, one per contributor, or (b) a parent + one sub-task per contributor. The parent ticket itself still has a single assignee — usually the AM or the primary owner.
  • The QA-Review subtask pattern from §3c is consistent with this rule: the subtask is a separate ticket with the QA Reviewer (default: AM) as its single assignee.
  • Visibility is solved by Watchers, not by multi-assignment. If other people need to follow the ticket without owning it, add them as Watchers.

Workspace-level enforcement (target state): the "Multiple Assignees" ClickApp should be disabled at the workspace level so multi-assignee tickets are mechanically prevented. Until that admin change lands, this rule is enforced by author discipline.

4. Content hygiene checks (mandatory pre-flight)

Run these checks on the fully-composed name and markdown_description before calling clickup_create_task or clickup_update_task. The assignee reads the task in ClickUp — anything that doesn't work in that context is a defect.

  1. No local filesystem paths. Scan the description for C:\, H:\, `[LOCAL_PATH_REMOVED]). Get it by opening the file in Drive and clicking Share → Copy link.

    • If no shareable URL exists: inline the content the assignee actually needs (e.g., paste the color hex, the short SOP steps, the snippet of text) and drop the path entirely.
    • Narrow exception: keep the local path in a clearly-labeled Source: footer for provenance only, when it duplicates information that is already actionable elsewhere in the task.
  2. No unverified URLs. For every non-Google, non-GHL URL in the description, confirm the page exists before pasting.

    • Default: link to team.symphonycore.com (the live internal wiki, served via Docusaurus + Cloudflare Access). It is the canonical reference surface for SOPs, standards, checklists, and templates. URL pattern: strip the leading numeric prefix from the repo path and drop .md. Examples: 04-operations/sops/<name>.mdhttps://team.symphonycore.com/operations/sops/<name>; 08-reference/standards/<name>.mdhttps://team.symphonycore.com/reference/standards/<name>; 06-team-training/platform-training/<name>.mdhttps://team.symphonycore.com/team-training/platform-training/<name>.
    • Publishing pipeline can stall. If a recently-added doc isn't yet on the wiki, the team URL will 404 until the next publish cycle. Acceptable fallbacks: (a) link to the Drive PDF in My Drive/symphony-core-documents-storage/... if one exists; (b) inline the smallest unit that lets the assignee execute the next step. A ticket is a pointer, not a copy.
    • GitHub blob URLs are NOT acceptable in tickets. They require repo access (contractors don't have it) and break the abstraction the wiki provides. Use them only as a private note to yourself, never in a description an assignee will read.
    • Client-specific content (under 09-clients/<slug>/ in the repo) is NOT published to the wiki per the client isolation rule. Reference it by name in the ticket, or link to the corresponding Drive PDF in Client_Delivery/<slug>/client-reference/ if one exists.
  3. Assignee can execute without leaving ClickUp (or with only known-good links). After composing, re-read the description as the assignee: is every instruction actionable? Every link clickable? Every referenced value present?

  4. Task title is the H1, not the frontmatter title. The frontmatter title is often a filename-friendly slug; the H1 is the sentence form a human will read in the list view. Use the H1.

These checks apply identically to creation (§5) and updates (§6).

4.5. Anti-patterns (do NOT inline these)

The 2026-05-15 audit of 86ahg97mk found ~7,000 characters of inlined content that duplicated source-of-truth docs. These are the patterns to refuse:

  • Full validation checklists. Link to the checklist (e.g., wordpress-post-go-live-validation-checklist.md) — never paste the 30+ checkbox lines into the description.
  • Attendees / contact tables. Use the client profile PDF link. One source of truth per client; updated once, every future ticket inherits.
  • Rollback / escalation procedures that already live in the referenced SOP. Decision trees belong in SOPs; tickets surface state and acceptance criteria.
  • Step-by-step instructions copied from a linked SOP. The whole point of linking the SOP is so the assignee opens it. If you copy the steps in, the link is decoration.
  • Source-of-truth tables (DNS records, GHL location IDs, brand colors, plan IDs, NAICS codes). These live in client profile, SOPs, or PDFs. Reference them by where they live.
  • Multi-paragraph background context that belongs in the engagement brief or a project memory.

The one allowed inline: per-task one-off values that exist nowhere else — an invoice number for this specific job, a customer-quoted price for this specific deal, a one-time URL like a meeting link. These have no canonical home, so the ticket is the home.

5. Steps - Creating a task

⚠️ Run the §4 Content hygiene checks on the composed name and description before step 4 below.

  1. Resolve prerequisites. Open the client profile. Confirm ClickUp List ID is populated. If *(TBD)*, stop and do Section 7 first.

  2. Read the task file. Note the frontmatter and H1.

  3. Resolve assignee. Call mcp__claude_ai_ClickUp__clickup_resolve_assignees with the assignee name/email. If the assignee is the authenticated user, "me" resolves faster than a name lookup.

  4. Create the task. Call mcp__claude_ai_ClickUp__clickup_create_task with the fields from Section 3. Use markdown_description (not description) to preserve formatting.

  5. Set the Client custom field (Client Delivery tasks only — skip for SC Operations). Resolve the field UUID and the client's option UUID via clickup_get_custom_fields against the target list (cache after first call — see Appendix A2). Either include the field in step 4's clickup_create_task custom_fields parameter, or set it post-creation via clickup_update_task with custom_fields=[{"id": "<field-uuid>", "value": "<option-uuid>"}]. SC Operations: use the client/<slug> tag in step 7 instead.

  6. Write back the cross-reference. Edit the source markdown frontmatter to add:

    clickup_task_id: <task_id from response>
    clickup_url: <task_url from response>
  7. Tags. Call clickup_add_tag_to_task for each tag. Tags MUST come from the SC ClickUp Tag Taxonomy. New tags require a PR amending that standard before being added to a task — do not create ad-hoc tags at space level. If a needed tag is missing from the taxonomy, either pick the closest canonical equivalent or pause to propose the addition. For client/<slug> specifically, see the field-vs-tag scoping rule in §3 and the Tag Taxonomy §Client identity.

  8. Commit. Include the frontmatter update in the next monthly task-folder commit.

6. Steps - Updating an existing task

⚠️ Run the §4 Content hygiene checks on any new or replacement description content before calling clickup_update_task. The same local-path and unverified-URL risks apply.

The task file's clickup_task_id is the pointer.

  • Status/assignee changes made in the ClickUp UI are authoritative for operational state. Do not overwrite from the markdown.
  • Description or step changes in the markdown sync to ClickUp via clickup_update_task using task_id = frontmatter.clickup_task_id and markdown_description = new body.
  • Completing the task: mark the checkbox list done in ClickUp. Do not overwrite the markdown status: open field unless the file is being archived out of the monthly folder.

7. Populating a missing ClickUp List ID

Only needed when the client profile shows *(TBD)*.

  1. Open ClickUp, navigate to Client Delivery space.

  2. Locate or create a folder for the client inside Client Delivery. Convention (observed 2026-04 from the Kraneworks list): each client has a folder named after the client slug (e.g., Kraneworks) containing an active-work list. Use this pattern for consistency.

  3. Copy the list URL from the ClickUp UI. The list ID is the trailing numeric segment: https://app.clickup.com/<workspace>/v/l/li/<list_id>.

  4. Edit the client profile:

    - **ClickUp List URL:** <full URL>
    - **ClickUp List ID:** <numeric list ID>
    - **ClickUp Space:** Client Delivery > active-work

    Replace the old ClickUp Folder URL: *(TBD)* line. Bump version and last_updated in the profile frontmatter.

8. Reference

9. Deferred - symphony-flow automation

A workflow config/workflows/markdown_task_to_clickup.yaml can replace the manual steps in Section 4 when volume justifies it (roughly: processing a full month's task folder at once, or more than ~5 new task files per week). The workflow would:

  • Accept markdown_file_path as input (optional list_id override)
  • Read the file, parse frontmatter (reuse the pattern from client_data_extraction.yaml)
  • Resolve the list ID from the client slug using the client profile
  • Call the ClickUp adapter's create_task action
  • Write clickup_task_id back to the file

Triggered via npm run cli -- trigger markdown_task_to_clickup --input '{...}' or the HTTP API. Not built yet; the field mapping and frontmatter conventions in this SOP are the contract that workflow will implement.

Appendix A - Agent Quick Reference

For Claude Code / LLM agents executing this SOP. Humans can skim.

A1. Loading tools

ClickUp MCP tools are deferred. Load only what you need:

ToolSearch(query="select:mcp__claude_ai_ClickUp__clickup_create_task,mcp__claude_ai_ClickUp__clickup_resolve_assignees")

Tool reference (use the mcp__claude_ai_ClickUp__ prefix when calling):

ToolWhen to use
clickup_create_taskCreate a new task. Required: list_id, name.
clickup_update_taskUpdate an existing task. Required: task_id.
clickup_get_taskRead a task by ID. Good for verification after create.
clickup_resolve_assigneesConvert name/email/"me" to ClickUp user IDs.
clickup_get_listLook up a list by ID or name; confirms space/folder path.
clickup_add_tag_to_taskAdd a tag. Tag must pre-exist in the space.
clickup_get_workspace_hierarchyPull the full spaces/folders/lists tree. Useful for audits.
clickup_create_folderCreate a folder in a space.
clickup_create_list_in_folderCreate a list inside a folder (4x to replicate the standard client layout).
clickup_searchFind tasks by keyword across the workspace.

A2. Invocation gotchas (observed in real runs)

  • Use markdown_description (not description) when the description contains markdown. description treats the input as plain text.
  • Priority is a string: 'urgent' | 'high' | 'normal' | 'low'. Not the 1-4 integer from the ClickUp UI.
  • Tags must pre-exist at the space level. clickup_add_tag_to_task fails with "tag does not exist" otherwise. Either skip, or seed the standard tag set once in the ClickUp UI.
  • clickup_resolve_assignees(["Full Name"]) often returns null for team members. Retry with their email, or use "me" for the authenticated user.
  • workspace_id is auto-detected from the authenticated session. Only pass it to override.
  • Status: omit for to do (ClickUp default). Pass explicit status only for non-default states. See SC Task Data Standard §1 for the canonical model.
  • due_date format is YYYY-MM-DD or YYYY-MM-DD HH:MM. No timezone suffix.
  • Creating many lists back-to-back: expect occasional transient 502 Bad Gateway from the MCP proxy. One retry has been sufficient.
  • §4 Content hygiene is not optional for agents. Copy-pasting a markdown body into markdown_description without running the hygiene checks has already produced one ClickUp task with unreachable Windows paths and a dead team.symphonycore.com link (moots-law, 2026-04-20). Run the checks every time.
  • Custom field IDs are space-scoped and stable. After first lookup via clickup_get_custom_fields on a Client Delivery list, cache the Client field's UUID and each client's option UUID — they don't change between task creations. Avoids re-fetching the schema on every call. Refresh the cache only when a new client is added (new option) or the field schema is intentionally changed.

A3. Known workspace layout (as of 2026-04-20)

  • Workspace: 9009191338
  • Client Delivery space: 901310638175
  • Each active client has its own folder with the 2-list structure: active-work (every task in every status — the Closed-group hide on complete keeps finished work out of default views) and reference (standing reference items only). The completed-work and archive lists were retired 2026-06-08 — see SC Task Management Workspace Setup SOP §5.2. Cold storage uses ClickUp's native task archive on a monthly UI sweep (§5.6 of the same SOP).
  • Per-client list IDs are stored in 09-clients/<slug>/<slug>-client-profile.md. Read the profile before calling create_task — do not hard-code list IDs.
  • SC-internal work folder (inside Client Delivery): Symphony-Core, active-work list 901320525397. Under review for move to a dedicated SC Operations space.

A4. Standard execution for a new task file

Minimal tool sequence for the common case "markdown task file → ClickUp task":

  1. Read the task file. Extract H1 (for name), frontmatter (for priority, assignee, due_date, status), body (for description).
  2. Read 09-clients/<slug>/<slug>-client-profile.md. Extract ClickUp List ID.
  3. clickup_resolve_assignees(["<assignee name or email>"]). If null and you have no email, ask the user.
  4. clickup_create_task(list_id=..., name=..., markdown_description=..., priority=..., due_date=..., assignees=[...]). Capture task_id and task_url from the response.
  5. Edit the task markdown file to append clickup_task_id and clickup_url to the frontmatter.
  6. Optional: clickup_add_tag_to_task per tag (expect failures if tags not seeded).
  7. git add/commit the client profile or markdown file edits if they live in the repo. Task markdown files in Google Drive are not committed.