Rishikesh Ranjan — rishikeshranjan.com

// reference management

Positioning Check

/ttm-positioning-check

Sample recent assets and report positioning drift percentage, types, and bleeding analysis. Auto-triggers when potential positioning drift is detected.

auto-invocable
allowed-tools:ReadBashGlobGrep

Overview

ttm-positioning-check audits a campaign's brief and assets against the positioning invariant declared in POSITIONING.md. It samples recent campaign assets across all active campaigns within a configurable time window (default 30 days), compares each asset to the positioning invariant, calculates a positioning drift percentage, categorizes the drift types, and performs a bleeding analysis (where positioning leaks into the wrong territory).

The output is a drift report: which claims diverge, by how much, and whether the drift is small enough to correct in place or large enough to require a positioning shift. Positioning drift is the single most common cause of inconsistent marketing across a year, so this skill acts as the linter for it. Run it after producing if anything feels off, and run it before shipping a campaign that hits a new channel or audience.

Invoke it with ttm-positioning-check. It auto-triggers when potential positioning drift is detected, and can also be auto-suggested by the ship workflow after every 3rd campaign ships since the last audit. Each run reports findings with three options: Correct, Accept + log, or Escalate to a positioning shift.

How it works

This skill follows the workflow at ${CLAUDE_PLUGIN_ROOT}/workflows/reference-mgmt/positioning-check.md. It is a positioning drift audit workflow: it samples recent assets across all campaigns within a configurable time window, evaluates each against the GATE-01 positioning drift checks, generates a structured audit report, and appends the results to .taketomarket/DRIFT-LOG.md.

Required reading for the workflow: ${CLAUDE_PLUGIN_ROOT}/references/context-loading.md, ${CLAUDE_PLUGIN_ROOT}/gates/gate-evaluation.md, and ${CLAUDE_PLUGIN_ROOT}/references/positioning-check-report.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-positioning-check 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-positioning-check

Use this exact check (bash) to decide whether to print: node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" first-run check ttm-positioning-check --raw -- the JSON seen field is true once the explainer has run before.

Explainer for /ttm-positioning-check

/ttm-positioning-check audits a campaign's brief and assets against the positioning invariant declared in POSITIONING.md. Output is a drift report: which claims diverge, by how much, and whether the drift is small enough to correct in-place or large enough to require a positioning shift.

Why it matters: positioning drift is the single most common cause of inconsistent marketing across a year. This skill is the linter for that -- run it after producing if anything feels off, and absolutely run it before shipping a campaign that hits a new channel or audience.

(Canonical source: references/inline-education-blurbs.md. Embedded verbatim because workflows do not @-resolve files at runtime.)

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; fi

If the 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 CONTEXT FOR POSITIONING AUDIT

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 drift evaluation:

  • .taketomarket/POSITIONING.md (needed for all 3 GATE-01 checks)

If .taketomarket/POSITIONING.md does not exist: Error: "No POSITIONING.md found. Run /ttm-init first to set up your marketing workspace." Exit.

Step 2: Parse time window

Parse optional --since argument from $ARGUMENTS. Default: 30d (per D-01).

WINDOW=$(echo "$ARGUMENTS" | grep -oP '(?<=--since\s)\S+' || echo "30d")

Extract number and unit:

  • Format: Nd where N is a number and d = days
  • Example: 30d = last 30 days, 90d = last 90 days, 7d = last 7 days
  • Calculate cutoff date from current date minus N days

Display:

takeToMarket > AUDIT WINDOW: last ${WINDOW}

Step 3: Enumerate campaigns and assets

takeToMarket > ENUMERATING CAMPAIGNS

Run campaign enumeration via CLI:

CAMPAIGNS_JSON=$(node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign list --since ${WINDOW} --raw)

Parse the JSON result. Expected format: { "campaigns": [...], "count": N }. If count is 0:

takeToMarket > NO CAMPAIGNS FOUND

No campaigns with assets in the last ${WINDOW}. Nothing to audit.

Exit. Per Pitfall 2: the CLI already excludes archived campaigns from the results.

For each campaign in the result:

  1. Read the campaign's ASSETS/ directory:
    • Glob: .taketomarket/CAMPAIGNS/${SLUG}/ASSETS/*
  2. Collect all asset files found
  3. Also read .taketomarket/CAMPAIGNS/${SLUG}/DEVIATIONS.md if it exists
    • Filter for rows where Gate = positioning_drift (GATE-01 deviations per D-13)
    • Store these as accepted deviations for the cross-reference section

Display:

takeToMarket > FOUND ${ASSET_COUNT} assets across ${CAMPAIGN_COUNT} campaigns

Step 4: Evaluate each asset against GATE-01

takeToMarket > EVALUATING POSITIONING DRIFT

For each collected asset:

  1. Read the full asset content from disk.
  2. Evaluate against GATE-01's 3 checks (from @gate-evaluation.md), described below.
  3. Record per-check result: PASS / WARN / FAIL with evidence.
  4. Calculate per-asset drift %: (WARN count + FAIL count) / 3 * 100.

Check 1: Differentiator Alignment

Does the asset restate or naturally extend the primary differentiator from POSITIONING.md? Or does it introduce a different claim?

  • PASS: Asset reinforces the primary differentiator
  • WARN: Asset is neutral -- neither reinforces nor contradicts
  • FAIL: Asset introduces a different/competing claim

Check 2: Proof Point Sourcing

Are all factual claims in the asset backed by proof points in the POSITIONING.md proof point library?

  • PASS: All claims backed by POSITIONING.md proof points
  • WARN: Some claims present but not all sourced
  • FAIL: Claims contradict or fabricate proof points

Check 3: Must-Not-Say Compliance

Does the asset contain any terms from the POSITIONING.md must-not-say list?

  • PASS: No must-not-say terms found
  • WARN: Terms appear in non-customer-facing context
  • FAIL: Must-not-say terms in customer-facing copy

Display progress for each asset:

  [${INDEX}/${TOTAL}] ${ASSET_NAME} (${CAMPAIGN_SLUG}): ${DRIFT_PCT}% drift

Step 5: Calculate aggregates

After evaluating all assets:

  • Total assets evaluated: count of all sampled assets
  • Aggregate drift %: total (WARN + FAIL results) / (total assets * 3) * 100
  • Count by drift category:
    • Differentiator Alignment: count of WARN + FAIL on Check 1 across all assets
    • Proof Point Sourcing: count of WARN + FAIL on Check 2 across all assets
    • Must-Not-Say Violations: count of WARN + FAIL on Check 3 across all assets
  • Cross-reference accepted deviations from DEVIATIONS.md files collected in Step 3 (per D-13)

Step 5b: Bleeding analysis

takeToMarket > ANALYZING MUST-NOT-SAY BLEEDING

From the per-asset evaluation results collected in Step 4, extract all assets where Check 3 (Must-Not-Say Compliance) returned WARN or FAIL. For each must-not-say violation:

  1. Classify the asset as customer-facing or non-customer-facing:
    • Customer-facing: landing pages, blog posts, ad copy, social posts, email campaigns, any asset intended for external audience consumption
    • Non-customer-facing: internal briefs, research docs, planning notes, strategy docs
    • When ambiguous, classify as customer-facing (conservative default per @positioning-check-report.md Bleeding Analysis rules)
  2. Determine bleeding status:
    • Check 3 = FAIL (customer-facing asset): Mark as BLEEDING (Critical)
    • Check 3 = WARN (non-customer-facing asset): Mark as NOT BLEEDING (Advisory)
  3. Extract the specific must-not-say term and surrounding context (10 words before and after the term occurrence) for inclusion in the Bleeding Analysis report section.

Calculate bleeding metrics:

  • BLEEDING_COUNT: Number of assets with Check 3 = FAIL (customer-facing violations)
  • MNS_VIOLATION_COUNT: Total assets with Check 3 = WARN or FAIL
  • BLEEDING_RATE: BLEEDING_COUNT / MNS_VIOLATION_COUNT * 100 (0% if no violations)

Display:

  Bleeding: ${BLEEDING_COUNT} of ${MNS_VIOLATION_COUNT} must-not-say violations in customer-facing materials

These values feed into the Bleeding Analysis section of the report generated in Step 7.

Step 6: Trend comparison

Read .taketomarket/DRIFT-LOG.md. Find the most recent entry with Event = audit.

If a prior audit entry exists (per D-03):

  1. Parse the Details column to extract the prior aggregate drift percentage
    • Expected format: "Nd audit: X% drift, Y findings across Z assets"
    • Extract X as the prior drift percentage
  2. Calculate delta: current aggregate drift % - prior aggregate drift %
  3. Determine trend:
    • Delta > +5%: UP (drift increasing -- positioning adherence worsening)
    • Delta < -5%: DOWN (drift decreasing -- positioning adherence improving)
    • Delta between -5% and +5%: STABLE (no significant change)
  4. Set TREND_ARROW and TREND_DETAIL for the report

If no prior audit entry exists:

  • Set TREND_ARROW = ""
  • Set TREND_DETAIL = "First audit -- no trend data available."

Step 7: Generate report

Follow the report format from @positioning-check-report.md. Generate the full audit report including:

  • Header with audit window and asset count
  • Aggregate drift percentage with trend arrow
  • Per-asset results table (asset x check matrix with drift %)
  • Drift type breakdown (count per category)
  • Bleeding analysis (must-not-say violations classified by asset type with bleeding count and rate)
  • Findings detail for every WARN and FAIL result
  • Accepted deviations cross-reference
  • Trend comparison (if prior audit exists)
  • Recommendations based on aggregate drift thresholds

Display the report to stdout (per A1 -- this is a cross-campaign audit, not scoped to a single campaign directory).

Step 8: Log to DRIFT-LOG.md

Append the audit results to .taketomarket/DRIFT-LOG.md via CLI:

node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" drift-log append \
  --event-type audit \
  --source "/ttm-positioning-check" \
  --details "${WINDOW} audit: ${AGGREGATE_DRIFT}% drift, ${DRIFT_COUNT} findings across ${ASSET_COUNT} assets" \
  --affected ${ASSET_COUNT}

Display:

takeToMarket > AUDIT LOGGED TO .taketomarket/DRIFT-LOG.md

Step 9: Completion banner

========================================
takeToMarket > POSITIONING AUDIT COMPLETE
========================================

Window: last ${WINDOW} | Assets audited: ${ASSET_COUNT}
Aggregate drift: ${AGGREGATE_DRIFT}% ${TREND_ARROW}
Campaigns covered: ${CAMPAIGN_COUNT}

Drift logged to: .taketomarket/DRIFT-LOG.md

Next steps:
- Run /ttm-fix [campaign-slug] to address specific FAIL results
- Run /ttm-positioning-shift if aggregate drift > 30% warrants a positioning change

Success criteria

  • All assets from campaigns within the time window evaluated against GATE-01's 3 checks
  • Per-asset drift percentage calculated (WARN+FAIL / 3 * 100)
  • Aggregate drift percentage calculated across all sampled assets
  • Drift categorized by type (differentiator, proof point, must-not-say)
  • Bleeding analysis classifies must-not-say violations by asset type (customer-facing vs non-customer-facing)
  • Accepted deviations cross-referenced from campaign DEVIATIONS.md files
  • Trend comparison shown if a prior audit exists in DRIFT-LOG.md
  • Report displayed to stdout in the standard format
  • Audit results appended to DRIFT-LOG.md via CLI
  • Recommendations provided based on aggregate drift thresholds

Output

  • Audit report displayed to stdout (cross-campaign, no file written)
  • .taketomarket/DRIFT-LOG.md updated with audit entry via CLI

The workflow steps that enumerate campaigns, append to the drift log, and check first-run state are backed by the bundled script ${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs.

What if this doesn't fit?

Looks like /ttm-positioning-check can't do that yet.

  • Want a new skill? /ttm-request-skill
  • Existing skill needs work? /ttm-improve-skill