// 9-phase lifecycle
Ship
/ttm-ship
Ship phase: generate launch checklist confirming tracking, UTMs, funnel testing, and asset finalization. Use when assets are approved and ready.
Overview
ttm-ship runs the publish step of the takeToMarket lifecycle (phase 7). It packages an approved asset for its target channel, generates a dynamic launch checklist confirming tracking, UTMs, funnel testing, and asset finalization, then records the outcome so the measurement phase knows what to read later. Invoke it with ttm-ship once your assets are approved and ready.
The skill builds a checklist tailored to the campaign's channel mix and asset types (decision D-09), auto-checks every verifiable item, then presents the results with checkboxes for human confirmation (D-10). Per-asset ship status enables staggered launches (D-11), and only ship-ready assets are allowed to ship. Think of it as git tag plus deployment: the moment the asset goes live and starts producing data.
It is backed by the helper script ${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs, which handles campaign state reads/writes, timestamps, first-run tracking, and audit suggestions.
How it works
The faithfully converted workflow from ${CLAUDE_PLUGIN_ROOT}/workflows/lifecycle/ship.md.
Step 0: First-run inline education
Read .taketomarket/CONFIG.md. Parse first_run_seen (object) and inline_education (boolean, default true).
If inline_education is false: skip this step. Else if first_run_seen.ttm-ship is not true, print the explainer below verbatim, then mark this skill as seen:
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" first-run mark ttm-shipUse this exact check (bash) to decide whether to print: node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" first-run check ttm-ship --raw -- the JSON seen field is true once the explainer has run before.
Explainer for /ttm-ship
/ttm-ship is the publish step. It packages the approved asset for its target channel, writes the canonical version into the campaign's shipped/ directory, updates STATE.md to shipped, and records the outcome metric hooks so /ttm-measure knows what to read later.
Why it matters: shipping in marketing is when a campaign becomes a measurable unit. Without the structured ship step, you've got files in a folder and no way to attribute results back to the brief. Treat this like git tag plus deployment: the moment the asset goes live and starts producing data.
(Canonical source: references/inline-education-blurbs.md. Embedded verbatim because workflows do not @-resolve files at runtime.)
Purpose
Ship workflow for /ttm-ship. Generates a dynamic launch checklist per campaign based on channel mix and asset types (D-09). AI auto-checks verifiable items, presents results with checkboxes for human confirmation (D-10). Per-asset ship status allows staggered launches (D-11). Only ship-ready assets can ship.
Required reading
${CLAUDE_PLUGIN_ROOT}/references/context-loading.md${CLAUDE_PLUGIN_ROOT}/references/ship-checklist-items.md
Constraints: POSITIONING.md is READ-ONLY
Process
Text-Mode Detection
Text mode (--text flag): Set TEXT_MODE=true if --text is present in $ARGUMENTS or if the runtime is not Claude Code. When TEXT_MODE is active, replace every AskUserQuestion call with a plain-text numbered list.
Detection:
if echo "$ARGUMENTS" | grep -q -- '--text'; then TEXT_MODE=true; fiIf AskUserQuestion tool is not available in the current runtime, set TEXT_MODE=true.
When TEXT_MODE is active, replace each AskUserQuestion with a plain-text numbered list:
[HEADER]
[QUESTION]
1. [OPTION_1_LABEL] -- [OPTION_1_DESCRIPTION]
2. [OPTION_2_LABEL] -- [OPTION_2_DESCRIPTION]
...
Type the number of your choice:Step 1: Load Context
takeToMarket > LOADING CONTEXTExtract SLUG from $ARGUMENTS (strip --text flag if present):
SLUG=$(echo "$ARGUMENTS" | sed 's/--text//g' | xargs)If SLUG is empty, error: "Usage: /ttm-ship [campaign-slug]. Provide a campaign slug." Exit.
Load Tier 1 summaries from all 9 reference files (lines 1 to <!-- END_SUMMARY -->):
.taketomarket/POSITIONING.md.taketomarket/BRAND.md.taketomarket/ICP.md.taketomarket/CHANNELS.md.taketomarket/STATE.md(frontmatter only).taketomarket/CALENDAR.md.taketomarket/COMPETITORS.md.taketomarket/METRICS.md.taketomarket/LEARNINGS.md
Load Tier 2 (full content) for ship checklist generation:
.taketomarket/CHANNELS.md(needed for UTM schema and channel-specific details)
Load campaign-specific files (always full-load per context-loading.md rule 4):
.taketomarket/CAMPAIGNS/${SLUG}/STATE.md.taketomarket/CAMPAIGNS/${SLUG}/BRIEF.md
Load MANIFEST.json:
MANIFEST_PATH=".taketomarket/CAMPAIGNS/${SLUG}/MANIFEST.json"Read .taketomarket/CAMPAIGNS/${SLUG}/MANIFEST.json. If the file does not exist, error: "No production manifest found for campaign '${SLUG}'. Run /ttm-produce first." Exit.
Load VERIFICATION.md:
Read .taketomarket/CAMPAIGNS/${SLUG}/VERIFICATION.md. If the file does not exist, error: "No verification report found for campaign '${SLUG}'. Run /ttm-verify first." Exit.
Step 2: Validate Campaign State
takeToMarket > VALIDATING CAMPAIGNCheck campaign exists:
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign state "${SLUG}" --rawIf result shows exists: false: Tell the user the campaign does not exist and suggest running /ttm-new-campaign first. Exit.
Check review completion:
Read review.overall_result from campaign state.
- If
review.overall_resultis null: "Campaign has not been reviewed. Run /ttm-review first." Exit. - If
review.overall_resultisneeds-fixormixed: Readfix.overall_resultfrom campaign state. Iffix.overall_resultis null, warn: "Some assets need fixing. Run /ttm-fix first, or proceed to ship only approved assets?" and use AskUserQuestion (or text-mode numbered list).
Some assets have not been fixed yet.
1. Proceed -- ship only approved/ship-ready assets
2. Fix first -- exit and run /ttm-fixIf "Fix first": exit workflow.
Step 3: Identify Ship-Ready Assets
takeToMarket > CHECKING SHIP READINESSParse MANIFEST.json. Collect hero and all derivative assets. For each asset, read its review_status and ship_status fields. Categorize:
- Ship-ready:
review_statusis"approved"or"ship-ready"ANDship_statusis NOT"shipped" - Not ready:
review_statusis"needs-fix"or"needs-human-fix"-- display warning - Already shipped:
ship_statusis"shipped"-- skip - Rejected:
review_statusis"rejected"-- excluded, already final
If no assets are eligible for shipping: "No ship-ready assets found. All assets are either awaiting fix, rejected, or already shipped." Exit.
Display ship readiness summary:
takeToMarket > SHIP READINESS
Ship-ready assets: ${COUNT}
${FOR EACH SHIP-READY ASSET:}
- ${ASSET_NAME} (${ASSET_TYPE}, ${CHANNEL})
${END FOR}
${IF ANY NOT READY:}
Not ready (excluded from this ship run):
${FOR EACH NOT-READY ASSET:}
- ${ASSET_NAME}: ${STATUS} -- ${REASON}
${END FOR}
${IF ANY ALREADY SHIPPED:}
Already shipped:
${FOR EACH SHIPPED ASSET:}
- ${ASSET_NAME} (shipped at ${SHIPPED_AT})
${END FOR}Step 4: Generate Dynamic Checklist (D-09)
Read ${CLAUDE_PLUGIN_ROOT}/references/ship-checklist-items.md for the checklist item definitions.
From BRIEF.md, identify the campaign's channel mix. From the ship-ready asset list, identify the unique asset types present (e.g., blog-post, email, linkedin-post, social-post, twitter-post, landing-page, video, youtube-video, paid-ad).
Build the checklist by combining:
- Universal section -- always included for all campaigns. Load items from the "Universal (all campaigns)" section of ship-checklist-items.md.
- Channel-specific sections -- for each unique asset type in the ship-ready list:
blog-post-> load "Blog / SEO" sectionemail-> load "Email" sectionlinkedin-post-> load "LinkedIn" sectionsocial-postortwitter-post-> load "Social / Twitter/X" sectionlanding-page-> load "Landing Page" sectionvideooryoutube-video-> load "Video / YouTube" sectionpaid-ad-> load "Paid Ads" section- Any unmatched type -> load "Default" section
Only include sections that match asset types in the ship-ready list. Do NOT include sections for asset types that are not present -- the checklist must be relevant, not exhaustive (D-09).
Store the assembled checklist as a structured list:
CHECKLIST = [
{ section: "Universal", items: [
{ name: "UTM parameters valid...", tag: "AI", result: null },
{ name: "Tracking/analytics configured...", tag: "HUMAN", result: null },
...
]},
{ section: "Blog / SEO", items: [...] },
...
]Step 5: Run AI Auto-Checks (D-10)
For each [AI]-tagged checklist item, attempt automated verification by reading the ship-ready asset files and campaign data.
Universal AI Checks
UTM parameter validity:
Scan each ship-ready asset file for URLs containing utm_ parameters.
- Check format:
utm_source,utm_medium,utm_campaignmust all be present in each tracked URL. - Cross-reference against
.taketomarket/CHANNELS.mdUTM schema if available. - Result: PASS (valid UTMs found on all trackable links), WARN (no trackable links found in assets -- may be expected for some asset types), FAIL (malformed UTMs -- missing required parameters or inconsistent naming).
Draft marker detection:
Scan each ship-ready asset file for strings: TODO, PLACEHOLDER, TBD, DRAFT, [INSERT, XXX.
- Result: PASS (none found across all assets), FAIL (markers found -- list file and location for each).
Verification status:
Read VERIFICATION.md frontmatter overall_result.
- Result: PASS if
passoraccepted, FAIL iffailorwarnwithout acceptance.
Review status:
Check MANIFEST.json -- verify all ship-ready assets have review_status of approved or ship-ready.
- Result: PASS (all ship-ready assets confirmed), FAIL (any asset has unexpected status).
Channel-Specific AI Checks
For each channel-specific section in the assembled checklist, run the applicable AI checks against the corresponding asset files:
Blog / SEO:
- Meta title: Check for first H1 or frontmatter
titlefield. PASS if found. - Meta description: Check for frontmatter
descriptionfield or first paragraph under 160 chars. PASS if present, WARN if over 160 chars. - H1 with keyword: Check that first H1 contains primary keyword from BRIEF.md or positioning anchor from POSITIONING.md summary. PASS/WARN.
- Orphaned links: Scan for markdown links
[text](url)-- flag any with empty or clearly broken targets. PASS/WARN.
Email:
- Subject line length: Read frontmatter
subjectfield, check length < 60 chars. PASS/FAIL. - Preview text length: Read frontmatter
previeworpreview_textfield, check < 90 chars. PASS/WARN. - Unsubscribe: Scan for string "unsubscribe" (case-insensitive). PASS/FAIL.
- Physical address: Scan for a postal/mailing address pattern. PASS/WARN.
- Spam trigger words: Check subject for common spam triggers (FREE, URGENT, ACT NOW, LIMITED TIME, GUARANTEED). PASS/WARN.
LinkedIn:
- Post length: Count characters. PASS if under 3000, FAIL if over.
- Opening line: Check if first non-empty line starts with "I". WARN if yes.
- External URL in first line: Check if first line contains http(s) URL. WARN if yes.
Social / Twitter/X:
- Character count: Count total post characters. PASS if under 280 (Twitter/X) or platform-appropriate limit, FAIL if over.
- Rhetorical questions: Scan for question marks in sentences that start with "Why", "How", "What if", "Have you". WARN if found.
- Hashtag count: Count
#tags. PASS if 0-3, WARN if more.
Landing Page:
- CTA presence: Scan for
[CTA], button-like markup (<button,[Button]), or strong action verbs (Get, Start, Join, Try, Download, Request, Schedule). PASS/FAIL. - Form fields: Check for form-like content matching brief's conversion goal. PASS/WARN.
- UTM capture: Check for form fields or hidden inputs referencing UTM parameters. PASS/WARN.
Video / YouTube:
- Title length: Check frontmatter
titlefield < 60 chars. PASS/FAIL. - Description keyword: Check that description's first 2 lines contain primary keyword. PASS/WARN.
- Timestamps: If content references a video longer than 5 minutes, check for timestamp markers. PASS/WARN/N/A.
Paid Ads:
- Character limits: Check headline and description against platform limits. PASS/FAIL.
- Headline and description: Verify both are present. PASS/FAIL.
- Display URL: Check for a formatted display URL. PASS/WARN.
For each AI check, record:
{ item_name, result: "PASS"|"WARN"|"FAIL", detail: "what was found or why it failed" }Step 6: Present Checklist with Results (D-10)
Display the complete checklist organized by section. For each item:
[AI]items: Show result (PASS/WARN/FAIL) with detail[HUMAN]items: Show as unchecked checkbox for user to confirm
takeToMarket > LAUNCH CHECKLIST: ${SLUG}
## Universal Checks
- [PASS] UTM parameters valid on all trackable links
- [PASS] All asset files finalized (no draft markers)
- [PASS] VERIFICATION.md shows pass/accepted
- [PASS] All ship-ready assets have approved review status
- [ ] Tracking/analytics configured for outcome metric -- CONFIRM? (yes/no)
- [ ] Monitoring and alerts set up -- CONFIRM? (yes/no)
- [ ] Team notified of launch timeline -- CONFIRM? (yes/no)
${IF BLOG/SEO SECTION:}
## Blog / SEO Checks
- [PASS] Meta title present
- [WARN] Meta description: 180 chars (recommend under 160)
- [ ] Schema markup deployed -- CONFIRM? (yes/no)
- [ ] Internal links added -- CONFIRM? (yes/no)
...
${END IF}
${OTHER CHANNEL SECTIONS AS APPLICABLE}Collect Human Confirmations
For each [HUMAN] item, use AskUserQuestion (or text-mode) to collect confirmation:
Confirm: [CHECKLIST_ITEM]
1. Yes -- confirmed
2. No -- not ready yet
3. N/A -- not applicable to this campaignTrack which items are confirmed (yes), not ready (no), or not applicable (N/A).
Step 7: Determine Ship Decision
After all items processed:
All clear: If all [AI] checks PASS (or WARN) and all [HUMAN] items confirmed (yes or N/A):
takeToMarket > ALL CLEAR TO SHIP
All checklist items passed or confirmed. Ready to proceed with shipping.Blockers found: If any [AI] checks FAIL or any [HUMAN] items answered "no":
Blockers preventing ship:
${FOR EACH BLOCKER:}
- ${ITEM_NAME}: ${REASON}
${END FOR}
Resolve these items and re-run /ttm-ship ${SLUG}Ask user using AskUserQuestion (or text-mode numbered list):
Ship decision:
1. Ship anyway -- proceed with documented exceptions
2. Resolve first -- exit and fix blockersIf "Ship anyway": proceed to Step 8. Record all blockers as exceptions. If "Resolve first": exit workflow.
Step 8: Update Per-Asset Ship Status (D-11)
For each ship-ready asset being shipped:
Read MANIFEST.json. Update each shipped asset entry:
- Set
ship_status: "shipped" - Set
shipped_at: "[ISO_TIMESTAMP]"(current timestamp)
TIMESTAMP=$(node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" timestamp --raw)Assets NOT being shipped (not ready, user deferred) retain their current status. Write updated MANIFEST.json back to disk.
Step 9: Update Campaign State
Determine ship status:
shipped-- all ship-ready assets shipped (none deferred or blocked)partial-- some assets shipped, others still pending
TIMESTAMP=$(node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" timestamp --raw)
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign update "${SLUG}" ship.status "[shipped|partial]"
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign update "${SLUG}" ship.shipped_at "$TIMESTAMP"
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign update "${SLUG}" ship.checklist_result "[all-clear|exceptions]"ship.status logic:
shipped-- all ship-ready assets shippedpartial-- some assets shipped, others deferred or blocked
ship.checklist_result logic:
all-clear-- all AI checks passed and all human items confirmedexceptions-- shipped with documented blockers/warnings
Only update campaign phase to "shipped" if at least one asset was shipped:
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign update "${SLUG}" phase shipped
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign update "${SLUG}" phase.shipped "$TIMESTAMP"Step 10: Display Completion
takeToMarket > SHIP COMPLETE
Date: ${ISO_DATE}
Assets shipped: ${SHIPPED_COUNT}/${TOTAL_SHIP_READY}
Shipped:
${FOR EACH SHIPPED ASSET:}
- ${ASSET_NAME} (shipped at ${TIMESTAMP})
${END FOR}
${IF PARTIAL:}
Deferred (not shipped yet):
${FOR EACH DEFERRED ASSET:}
- ${ASSET_NAME}: ${REASON}
${END FOR}
${IF EXCEPTIONS:}
Shipped with exceptions:
${FOR EACH EXCEPTION:}
- ${EXCEPTION_ITEM}: ${JUSTIFICATION}
${END FOR}
Checklist result: ${CHECKLIST_RESULT}
Next: Run /ttm-measure ${SLUG} when measurement window begins
(or /ttm-ship ${SLUG} again to ship deferred assets)Step 11: Positioning Check Auto-Suggest (D-02)
Check if a positioning audit is recommended:
SHIPPED_JSON=$(node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign list --shipped-since-last-audit --raw)
SHIPPED_COUNT=$(echo "$SHIPPED_JSON" | node -e "process.stdin.resume();let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.parse(d).count))")If SHIPPED_COUNT >= 3, display:
takeToMarket > POSITIONING CHECK SUGGESTED
${SHIPPED_COUNT} campaigns have shipped since your last positioning audit.
Consider running /ttm-positioning-check to verify positioning consistency
across recent assets.
This is a suggestion, not a requirement. Run it when convenient.If SHIPPED_COUNT < 3, do not display anything.
Success criteria
- Campaign state validated (exists, has been reviewed)
- Ship-ready assets identified from MANIFEST.json review_status
- Dynamic checklist generated from campaign channel mix (D-09)
- AI auto-checks run for all [AI]-tagged items (UTM, draft markers, verification, review status)
- Channel-specific AI checks run for applicable asset types
- Human confirmation collected for all [HUMAN]-tagged items (D-10)
- Ship decision made (all clear or ship with exceptions)
- Per-asset ship_status and shipped_at updated in MANIFEST.json (D-11)
- Campaign state updated: ship.status, ship.shipped_at, ship.checklist_result
- Campaign phase advanced to "shipped" (if at least one asset shipped)
- User directed to /ttm-measure as next step
Output
.taketomarket/CAMPAIGNS/${SLUG}/MANIFEST.json(updated with ship_status and shipped_at per asset)
Step: Deploy landing/pSEO assets (conditional)
If any asset in this campaign is a landing-page or pSEO asset:
- Invoke
/ttm-deployvia Skill tool. - Confirm a deploy URL was produced.
- Confirm
/ttm-deploywrotelast_deploy_urlto.taketomarket/CONFIG.md(its Step 4).CONFIG.mdis the single source of truth -- read back by/ttm-verify. Do not duplicate into the campaign's STATE.md.
What if this doesn't fit?
Looks like /ttm-ship can't do that yet.
- Want a new skill?
/ttm-request-skill - Existing skill needs work?
/ttm-improve-skill