// reference management
Positioning Shift
/ttm-positioning-shift
Controlled positioning change with reasoning, migration plan, deprecation schedule, and approval gate. Use when positioning needs intentional evolution.
Overview
Performs a controlled, deliberate change to your product's positioning. It requires explicit reasoning for the change, generates a migration plan for in-flight campaigns, creates a deprecation schedule for previously shipped assets, presents the full before/after diff for mandatory human approval, and only then atomically updates POSITIONING.md (archiving the old position in a History table). Use it when positioning needs intentional evolution — a market shift, customer feedback, or competitive pressure — rather than ad-hoc edits.
Invoke it with ttm-positioning-shift. Along with ttm-init, it is one of only two skills authorized to modify .taketomarket/POSITIONING.md. A positioning change behaves like a schema migration: editing positioning ad-hoc silently invalidates every shipped asset with no audit trail, so this workflow makes the change explicit, auditable, and propagates its consequences across active campaigns.
How it works
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-shift 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-shiftUse this exact check (bash) to decide whether to print: node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" first-run check ttm-positioning-shift --raw — the JSON seen field is true once the explainer has run before.
This workflow is backed by the ${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs script, which provides the first-run, campaign list, and drift-log sub-commands used throughout.
Explainer for /ttm-positioning-shift
/ttm-positioning-shift is the only skill (besides init) that may modify POSITIONING.md. It walks you through a deliberate positioning change: documents the old position, captures the rationale, writes the new one, and flags every existing campaign that needs re-verification under the new invariant.
Why it matters: positioning changes are schema migrations. If you edit POSITIONING.md ad-hoc, you've silently invalidated every shipped asset and there's no audit trail. This skill makes the change explicit, auditable, and propagates the consequences across active campaigns.
(Canonical source: references/inline-education-blurbs.md. Embedded verbatim because workflows do not @-resolve files at runtime.)
Purpose
Controlled positioning shift workflow for /ttm-positioning-shift. Requires explicit reasoning for the change, collects new positioning fields, generates a migration plan for active campaigns, sets a deprecation schedule for shipped assets, presents a before/after diff for mandatory human approval, and atomically updates POSITIONING.md with History table archival. Logs all changes to .taketomarket/DRIFT-LOG.md.
Required reading
${CLAUDE_PLUGIN_ROOT}/references/context-loading.md${CLAUDE_PLUGIN_ROOT}/templates/reference-files/positioning.md${CLAUDE_PLUGIN_ROOT}/templates/migration-plan.md
Constraints
This workflow WRITES to POSITIONING.md
This is one of only two workflows authorized to modify .taketomarket/POSITIONING.md (the other is /ttm-init). All writes are gated behind mandatory human approval in Step 4. Never write to POSITIONING.md without explicit "Approve" from the user.
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 CONTEXTLoad 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 the before/after diff:
.taketomarket/POSITIONING.md(full — needed for field comparison and History table)
If .taketomarket/POSITIONING.md does not exist, error: "POSITIONING.md not found. Run /ttm-init first to set up your marketing system." Exit.
Parse the current POSITIONING.md into structured fields:
CURRENT_CATEGORY— from**Category:**lineCURRENT_DIFFERENTIATOR— from**Primary differentiator:**lineCURRENT_AUDIENCE— from**Target audience:**lineCURRENT_PROOF_POINTS— from## Proof Point Librarytable rowsCURRENT_MUST_NOT_SAY— from## Must-Not-Say Termstable rowsCURRENT_HISTORY— from### Positioning Historytable rows
Step 2: Check for Active Campaigns (per D-08, D-09, D-10)
ACTIVE_JSON=$(node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign list --active --raw)
ACTIVE_COUNT=$(echo "$ACTIVE_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 ACTIVE_COUNT > 0: Display warning:
takeToMarket > ACTIVE CAMPAIGNS DETECTED
${ACTIVE_COUNT} campaign(s) have active assets that may conflict with
a positioning change. A migration plan will be generated.
Campaigns: [list campaign slugs and phases from ACTIVE_JSON]If ACTIVE_COUNT == 0: Display:
takeToMarket > NO ACTIVE CAMPAIGNS
No active campaigns detected. Shift will apply to all future campaigns.
A migration plan is not required, but a deprecation schedule for
previously shipped assets may still be needed.Step 3: Collect Shift Information (per D-06)
3a: Reasoning
Using AskUserQuestion (text-mode fallback):
- header: "Positioning Shift Reasoning"
- question: "Why is this positioning change needed? Provide specific reasoning (market shift, customer feedback, competitive pressure, etc.)"
- type: free text
Record the reasoning as SHIFT_REASONING.
3b: New Positioning Fields
Collect ALL structured positioning fields (matching the POSITIONING.md template). For each field, show the CURRENT value and ask for the NEW value.
Field 1: Primary Differentiator
Using AskUserQuestion (text-mode fallback):
- header: "Primary Differentiator"
- question: "Current: ${CURRENT_DIFFERENTIATOR}\n\nEnter the NEW primary differentiator phrase (or type 'keep' to keep current):"
- type: free text
Record as NEW_DIFFERENTIATOR. If "keep", use CURRENT_DIFFERENTIATOR.
Field 2: Category
Using AskUserQuestion (text-mode fallback):
- header: "Market Category"
- question: "Current: ${CURRENT_CATEGORY}\n\nEnter the NEW market category (or type 'keep' to keep current):"
- type: free text
Record as NEW_CATEGORY. If "keep", use CURRENT_CATEGORY.
Field 3: Target Audience
Using AskUserQuestion (text-mode fallback):
- header: "Target Audience"
- question: "Current: ${CURRENT_AUDIENCE}\n\nEnter the NEW target audience (or type 'keep' to keep current):"
- type: free text
Record as NEW_AUDIENCE. If "keep", use CURRENT_AUDIENCE.
Field 4: Proof Points
Display current proof points table. Ask:
- header: "Proof Points"
- question: "Current proof points shown above.\n\nProvide the NEW proof points as a numbered list. Each item: claim | source\n(or type 'keep' to keep current):"
- type: free text
Record as NEW_PROOF_POINTS. If "keep", use CURRENT_PROOF_POINTS.
Field 5: Must-Not-Say Terms
Display current must-not-say terms table. Ask:
- header: "Must-Not-Say Terms"
- question: "Current terms shown above.\n\nProvide the NEW must-not-say terms. Each item: term | reason\n(or type 'keep' to keep current):"
- type: free text
Record as NEW_MUST_NOT_SAY. If "keep", use CURRENT_MUST_NOT_SAY.
Validate at least one field changed. If ALL fields are "keep", display: "No fields changed. Nothing to shift. Exiting." Exit.
3c: Migration Plan for Active Campaigns (per D-04, only if ACTIVE_COUNT > 0)
For each active campaign from ACTIVE_JSON:
- List all assets in
.taketomarket/CAMPAIGNS/<slug>/ASSETS/or from MANIFEST.json - Quick-evaluate each asset against the NEW positioning using GATE-01 3-check logic:
- Does the asset align with the NEW differentiator?
- Are claims backed by NEW proof points?
- Does the asset avoid NEW must-not-say terms?
- For each asset, recommend one of:
- re-verify: Asset may still pass under new positioning (minor drift)
- re-produce: Asset fundamentally conflicts with new positioning
- accept-as-is: Asset is channel/format where positioning is less critical
Present the migration plan per campaign using the templates/migration-plan.md format.
Ask the user to confirm or override each recommendation. Using AskUserQuestion (text-mode fallback):
- header: "Migration Plan Review"
- question: "Review the migration plan above. Do you accept these recommendations?"
- options:
- Accept all — Keep all recommendations as-is
- Override — Change specific recommendations (will prompt per asset)
- Cancel — Abandon the shift entirely
- On "Override": For each asset, ask the user to select an action (re-verify / re-produce / accept-as-is).
- On "Cancel": Display "Positioning shift cancelled. No changes made." Exit.
- On "Accept all": Continue.
3d: Deprecation Schedule (per D-05)
Ask the user to set a deprecation timeline for shipped assets. Using AskUserQuestion (text-mode fallback):
- header: "Deprecation Schedule"
- question: "Set a deadline for updating old-positioning assets (e.g., 90 days from now, or a specific date like 2025-06-01). Type 'skip' if no shipped assets need updating:"
- type: free text
If not "skip": Record DEPRECATION_DEADLINE.
For each campaign with shipped assets (from campaign list):
- List shipped assets
- Record deprecation entry: asset, campaign, old positioning element, required update, deadline
Step 4: Present Before/After Diff and Approval Gate (per D-06, D-07)
Build a summary of all changed fields. Only show fields that actually changed. Display the complete change for review:
takeToMarket > POSITIONING SHIFT APPROVAL
## Current Positioning
**Category:** [CURRENT_CATEGORY]
**Primary Differentiator:** [CURRENT_DIFFERENTIATOR]
**Target Audience:** [CURRENT_AUDIENCE]
**Proof Points:** [current count] points
**Must-Not-Say:** [current count] terms
## Proposed Positioning
**Category:** [NEW_CATEGORY]
**Primary Differentiator:** [NEW_DIFFERENTIATOR]
**Target Audience:** [NEW_AUDIENCE]
**Proof Points:** [new count] points
**Must-Not-Say:** [new count] terms
## What Changed
[field-by-field diff showing only changed fields]
[For each changed field: "- [FIELD]: [old value] -> [new value]"]
## Reasoning
[SHIFT_REASONING]
## Impact
- Active campaigns affected: [ACTIVE_COUNT]
- Migration actions needed: [count of re-verify + re-produce actions]
- Deprecation items: [count]
- Deprecation deadline: [DEPRECATION_DEADLINE or "N/A"]Using AskUserQuestion (text-mode fallback):
- header: "Approve Positioning Shift"
- question: "Review the changes above. This will update POSITIONING.md and affect all future campaigns."
- options:
- Approve — Apply the new positioning
- Revise — Go back and modify the proposed positioning
- Cancel — Abandon the shift entirely
- On "Revise": Loop back to Step 3b to re-collect fields.
- On "Cancel": Display "Positioning shift cancelled. No changes made." Exit.
- On "Approve": Proceed to Step 5.
Step 5: Apply Changes (per D-07)
5a: Archive Old Positioning in History Table
Read the current POSITIONING.md. Find the ### Positioning History table. Append a new row:
| [ISO date] | [summary of what changed] | [user's reasoning, sanitized to 100 chars] |Sanitization (per T-06-09, Pitfall 3): Strip pipes (|), backticks, newlines from the reasoning text. Replace with spaces. Limit to 100 characters. This prevents Markdown table delimiter breakage.
5b: Update POSITIONING.md Fields
- Update the
<!-- _SUMMARY -->block with new Category, Primary Differentiator, Target Audience. - Update the
## Proof Point Librarytable with new proof points. - Update the
## Must-Not-Say Termstable with new terms.
CRITICAL: Preserve the <!-- _SUMMARY --> and <!-- END_SUMMARY --> delimiters exactly as they are. All other workflows depend on these markers for Tier 1 context loading.
Write the updated POSITIONING.md atomically via the Write tool.
5c: Log Shift to DRIFT-LOG.md (per T-06-10)
CHANGED_FIELDS="[list of field names that changed, e.g., differentiator, category]"
SANITIZED_REASONING=$(echo "${SHIFT_REASONING}" | head -c 100 | tr '|`\n' ' ')
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" drift-log append \
--event-type shift \
--source "/ttm-positioning-shift" \
--details "Changed: ${CHANGED_FIELDS}. Reasoning: ${SANITIZED_REASONING}" \
--affected ${ACTIVE_COUNT}5d: Log Deprecation Entries
For each deprecation item collected in Step 3d:
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" drift-log deprecation \
--asset "[asset name]" \
--campaign "[campaign slug]" \
--old-element "[changed positioning element]" \
--required-update "[what needs updating]" \
--deadline "[ISO date]"If no deprecation items were collected (user typed "skip" in 3d), skip this step.
Step 6: Completion Banner
takeToMarket > POSITIONING SHIFT COMPLETE
Old positioning archived in POSITIONING.md History table.
New positioning is now active for all future campaigns.
Changes logged: .taketomarket/DRIFT-LOG.md
Deprecation items: [count] (deadline: [DEPRECATION_DEADLINE or "N/A"])
Active campaigns: [ACTIVE_COUNT] -- review migration plan above
Next steps:
[If active campaigns]: Re-run /ttm-verify <slug> for campaigns marked "re-verify"
[If deprecation items]: Update shipped assets before [DEPRECATION_DEADLINE]
[Always]: Run /ttm-positioning-check periodically to monitor driftSuccess criteria
- User provided explicit reasoning for the positioning change
- All 5 structured positioning fields collected (differentiator, category, audience, proof points, must-not-say)
- Active campaigns detected and migration plan generated (if any exist)
- Deprecation schedule set for shipped assets (if any exist)
- Before/after diff presented showing all changed fields
- Human approval gate passed (Approve/Revise/Cancel with Approve required)
- Old positioning archived in POSITIONING.md History table before update
- History table entry sanitized (no pipes, backticks, newlines; max 100 chars)
- POSITIONING.md _SUMMARY and END_SUMMARY delimiters preserved
- Shift event appended to DRIFT-LOG.md via CLI
- Deprecation entries appended to DRIFT-LOG.md via CLI
- Completion banner displayed with next steps
Output
.taketomarket/POSITIONING.md(updated with new positioning and History table entry).taketomarket/DRIFT-LOG.md(shift event appended + deprecation entries)
What if this doesn't fit?
Looks like /ttm-positioning-shift can't do that yet.
- Want a new skill? /ttm-request-skill
- Existing skill needs work? /ttm-improve-skill