
Internal Narrative
- 80 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Internal Narrative Builder is a Claude skill that builds one coherent company story and translates it per audience for all-hands, investor updates, and crisis communications.
About
Internal Narrative Builder is a Claude skill that builds and maintains one coherent company story across employees, investors, customers, candidates, and partners. It creates a single core narrative, translates the same facts per audience, and runs a contradiction-detection protocol before major communications. A founder or executive uses it to prepare all-hands meetings, investor updates, board presentations, recruiting narratives, or crisis communications.
- Builds one core narrative and translates it per audience
- Contradiction-detection protocol catches inconsistent messaging
- Templates for all-hands, investor updates, and crisis comms
Internal Narrative by the numbers
- 80 all-time installs (skills.sh)
- Ranked #1,450 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
internal-narrative capabilities & compatibility
Free; no API keys or external services required.
- Capabilities
- internal narrative · executive mentor · founder coach
- Use cases
- copywriting · planning
- Pricing
- Free
What internal-narrative says it does
Build and maintain one coherent company story across employees, investors,
One company. Many audiences. Same truth -- different lenses.
Different framing is not different facts.
npx skills add https://github.com/borghei/claude-skills --skill internal-narrativeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 80 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Craft one coherent company story and translate it for employees, investors, customers, and partners.
Who is it for?
Founders and executives preparing all-hands, investor updates, board presentations, recruiting narratives, or crisis communications.
Skip if: Product marketing copy, code, or external SEO content.
When should I use this skill?
You are preparing an all-hands, investor update, board presentation, recruiting narrative, pivot, or crisis communication.
What you get
One core narrative translated consistently per audience, checked for contradictions before it ships.
- Core company narrative paragraph
- Audience translation matrix and contradiction check
By the numbers
- 3 frameworks (narrative-construction, audience-translation, crisis-communication)
- 5 stakeholder audiences in the translation matrix
Files
Internal Narrative Builder
Tier: POWERFUL Category: C-Level Advisory Tags: company narrative, internal communications, all-hands, investor updates, crisis communication, change management
Overview
One company. Many audiences. Same truth -- different lenses. The Internal Narrative Builder creates and maintains coherent communication across every stakeholder group. Narrative inconsistency is trust erosion: when employees hear one story and investors hear another, it is not strategic framing -- it is a trust debt that compounds until someone catches it.
---
Core Principle
The same fact lands differently depending on who hears it and what they need.
"We are shifting resources from Product A to Product B" means:
- Employees: "Is my job safe? Why are we abandoning what I built?"
- Investors: "Smart capital allocation -- they are doubling down on the winner."
- Customers (Product A): "Are they abandoning us?"
- Candidates: "Decisive leadership -- exciting new focus."
- Partners: "Does this affect our integration?"
Same fact. Five narratives needed. The skill is maintaining truth while serving each audience's actual question.
---
Clarify First
Before generating, confirm these inputs. If any is unknown or vague, ASK — do not assume:
- [ ] The situation type (routine update, all-hands, investor update, pivot/reorg, or crisis) — determines which framework and template you use and the urgency of the cadence
- [ ] Which audiences must hear this (employees, investors, customers, candidates, partners) — each gets its own column in the translation matrix with a different frame of the same fact
- [ ] The core facts and current state (honest metrics, what changed, what's good and bad) — every audience narrative derives from one true core; vague facts produce hollow narrative
- [ ] Any prior public statements on this topic (last investor update, last all-hands) — needed to run contradiction detection before the new communication ships
Stop rule: ask only the 2-3 that most change the output. If the user says "just draft it," proceed and list your assumptions at the top of the artifact.
Framework
Step 1: Build the Core Narrative
One paragraph that every other communication derives from. This is the single source of truth.
Template:
[Company] exists to [mission -- present tense, specific].
We are building [what] because [the problem, stated concretely].
Our approach is [what makes your way different].
We are at [honest description of current state] and heading toward
[where you are going, in concrete measurable terms].Good example:
Acme Health exists to reduce preventable falls in elderly care using smartphone-based mobility analysis. We are building an AI diagnostic tool for care teams because current fall risk assessments are subjective, infrequent, and often wrong. Our approach -- using the phone camera during a 10-second walking test -- means no new hardware and no specialist required. We have 80 care facilities in DACH paying us EUR 800K ARR, and we are heading to EUR 3M ARR by demonstrating clinical value at scale before our Series B.
Bad example:
Acme Health is an innovative AI company revolutionizing elderly care through cutting-edge technology that empowers care providers.
The good version is usable. The bad version says nothing.
Step 2: Audience Translation Matrix
Take the core narrative and translate for each audience. Same truth, different frame.
| Fact | Employees | Investors | Customers | Candidates | Partners |
|---|---|---|---|---|---|
| 80 customers | "Your work matters -- we have proven the model" | "PMF signal, capital efficient growth" | "80 facilities trust us" | "Traction you would be joining" | "Growing ecosystem" |
| Pivoted from hardware | "We were honest enough to change course" | "Better unit economics, capital efficient" | "Faster, simpler way to serve you" | "Evidence-based decisions, not ego" | "More accessible integration" |
| Missed Q2 revenue | "Here is why, the plan, and how you can help" | "Revenue mix shifted, trailing indicators improving" | [Not shared unless relevant] | [Not shared externally] | [Not shared] |
| Hiring fast | "Team is growing, your network matters" | "Headcount aligned to growth plan" | [Only if affects service quality] | "Rocket ship moment" | "Expanding capacity" |
Critical rule: Different framing is not different facts. "We told investors growth and told employees efficiency" is a contradiction. People talk to each other.
Step 3: Contradiction Detection Protocol
Before any major communication, run this check:
Question 1: What did we tell investors last quarter about this topic? Question 2: What did we tell employees at the last all-hands? Question 3: Are these consistent? If not -- which version is true?
Common contradictions to catch:
| Contradiction | Audiences | Why Dangerous |
|---|---|---|
| "Efficient growth" to investors + "hiring aggressively" to candidates | Investors vs candidates | Candidates talk to investors at events |
| "Strong pipeline" to investors + "sales is struggling" at all-hands | Investors vs employees | Board members visit offices |
| "Customer-first culture" + decisions clearly prioritizing revenue | External vs internal | Employees see through performative values |
| "Stable and growing" to customers + "runway concerns" to board | Customers vs board | Customers hear rumors |
| "World-class team" to investors + high turnover internally | Investors vs reality | Due diligence reveals truth |
When you catch a contradiction: Fix the less accurate version. Communicate the correction explicitly. "Last month I said X. After more analysis, the clearer picture is Y." Correcting yourself before someone catches it builds more trust than being caught.
Step 4: Communication Cadence
| Audience | Format | Frequency | Owner | Key Content |
|---|---|---|---|---|
| All employees | All-hands meeting | Monthly | CEO | State of company, wins, challenges, Q&A |
| Teams | Team standup/update | Weekly | Team leads | Team-specific progress and blockers |
| Investors | Written update | Monthly | CEO + CFO | Metrics, narrative, asks |
| Board | Board meeting + memo | Quarterly | CEO | Strategy, financials, key decisions |
| Customers | Product updates | Per release | CPO / CS | What changed, what is coming |
| Candidates | Careers page + interviews | Ongoing | CHRO + Founders | Why join, culture, mission |
| Partners | Business review | Quarterly | BD / Partnerships | Joint metrics, roadmap alignment |
Step 5: All-Hands Design
The all-hands is the most important recurring internal communication. Most companies get it wrong.
Structure (60 minutes):
| Segment | Duration | Content |
|---|---|---|
| State of the company | 10 min | Honest assessment -- good and bad |
| Key metrics | 5 min | 3-5 metrics everyone should know, with context |
| Wins and recognition | 10 min | Connect team work to company outcomes |
| Challenges and plan | 10 min | What is not working and what we are doing about it |
| Strategic update | 10 min | Where we are headed and why |
| Open Q&A | 15 min | Unscreened, unfiltered questions |
All-Hands Principles: 1. Lead with honest state. No spin. Employees detect inauthenticity instantly. 2. Connect metrics to people: "Sarah's team shipped X, which drove Y." 3. Give people a reason to be proud of their choice to work here. 4. Leave real time for Q&A -- not curated questions, not "any quick questions?" 5. Follow up on unanswered questions within 48 hours.
All-Hands Failure Modes:
| Failure | Signal | Fix |
|---|---|---|
| CEO monologue | 55 of 60 minutes is one person talking | Max 30 min presentation, rest is interactive |
| Sunshine and rainbows | Only good news, every metric is "great" | Include one honest challenge and its plan |
| Metrics without context | "ARR grew 15%" with no benchmark | Always: metric + context + what it means |
| Deflected questions | "Great point, let's follow up" (never followed up) | Answer or commit to written follow-up by date |
| No employee voice | Leadership talks, employees listen | Include team demos, recognition, Q&A |
| Slides over substance | 50 slides of bullet points | Max 15 slides, mostly visuals and charts |
Step 6: Investor Update Template
Monthly investor updates should be concise, honest, and actionable.
Subject: [Company] Monthly Update - [Month Year]
TL;DR: [One sentence -- honest state of things]
METRICS:
MRR/ARR: $[X] ([+/-Y]% MoM)
Burn: $[X]/month
Runway: [X] months
Key Metric: [Your North Star] at [value]
WINS:
- [Specific win with context]
- [Specific win with context]
CHALLENGES:
- [Honest challenge with your plan to address it]
ASKS:
- [Specific ask: intro, hire, advice]
- [Specific ask]
NEXT MONTH FOCUS:
- [Priority 1]
- [Priority 2]Rules:
- Send on the same date every month (builds trust through consistency)
- Include challenges -- investors respect honesty, distrust all-good-news updates
- Make asks specific -- "intro to head of product at Stripe" not "intros to SaaS companies"
- Keep under 500 words -- investors read dozens of these
Step 7: Crisis Communication
When the narrative breaks -- someone leaves publicly, a product fails, a security breach, negative press.
The 4-Hour Rule: If something is public or about to be, communicate internally within 4 hours. Employees should never learn about company news from social media or press.
Crisis Communication Sequence:
Hour 0-4 (Internal First):
1. CEO or relevant leader sends internal message
2. Acknowledge what happened -- factual, no spin
3. State what you know and what you don't know yet
4. Tell people what you are doing about it
5. Tell people what to say if asked externally
6. Commit to next update by specific timeHour 4-24 (External If Needed):
1. External statement only if event is public
2. Consistent with internal message -- same facts, audience-appropriate framing
3. Legal review if any claims or liability involved
4. Single spokesperson designatedCrisis Internal Template:
Team,
Here is what happened: [factual description, no editorializing]
Here is what we know right now: [confirmed facts]
Here is what we don't know yet: [honest uncertainty]
Here is what we are doing: [specific actions with owners]
If you are asked about this externally: [specific guidance]
I will update you by [specific time] with more information.
[Name]Crisis Don'ts:
- Silence (vacuum fills with speculation)
- Spin (people detect it, trust dies)
- "No comment" (implies guilt)
- Blaming (your audience only cares what you are doing about it)
- Deleting social media comments (people screenshot, makes it worse)
- Humor (read the room)
---
Narrative Consistency Checklist
Run before any major external communication:
- [ ] Consistent with what we told investors last quarter?
- [ ] Consistent with what we told employees at last all-hands?
- [ ] Contradicts anything on website, careers page, or press releases?
- [ ] If an employee read this external communication, would they recognize the company described?
- [ ] If an investor read our internal all-hands deck, would they find inconsistencies?
- [ ] Are we describing current state accurately or projecting an aspiration as reality?
- [ ] Does the tone match the reality? (Celebratory tone with mediocre results is a contradiction.)
---
Change Communication Framework
When communicating significant changes (reorgs, pivots, layoffs, policy changes):
The ADKAR Model for Change
| Stage | Communication Need | Example |
|---|---|---|
| Awareness | Why is this change happening? | "Our market shifted. Here is the data." |
| Desire | Why should I support it? | "This protects our future. Here is how." |
| Knowledge | What do I need to know? | "Your role changes from X to Y." |
| Ability | Can I actually do this? | "Training starts Monday. Support available." |
| Reinforcement | Is this working? | "30 days in: here is the progress." |
Communication Sequence for Major Changes
Day -1: Brief leadership team (they need to be able to answer questions)
Day 0: All-hands announcement (CEO, with full context)
Day 0: Written follow-up (email with details, FAQ)
Day 1-3: Team-level discussions (managers address team-specific impact)
Day 7: Follow-up Q&A session (address questions that emerged)
Day 30: Progress update (what changed, what is working)---
Red Flags
Watch for these signs that narrative is fracturing:
- Different departments describe the company mission differently
- Investor narrative emphasizes growth while employee narrative emphasizes stability (or vice versa)
- All-hands presentations are mostly slides, mostly one-way
- Q&A questions are screened or consistently deflected
- Bad news reaches employees through Slack rumors before leadership communicates
- Careers page describes a culture employees do not recognize
- Executives give different answers to "what is our top priority?"
- "That is the external messaging" is said internally without irony
---
Integration with C-Suite Skills
| Situation | Collaborate With | Alignment Needed |
|---|---|---|
| Investor update prep | CFO Advisor | Financial narrative matches company narrative |
| Reorg / leadership change | CEO + CHRO | Employees hear first, then external |
| Product pivot | CPO / Product Team | Customer communication aligns with investor story |
| Crisis | All C-suite | Single voice, consistent story, internal first |
| Fundraise narrative | CEO + CFO | Growth story consistent with burn and metrics |
| Recruiting push | CHRO + CEO | Candidate narrative matches employee experience |
---
Related Skills
| Skill | Use When |
|---|---|
| ceo-advisor | Strategic decisions that need to be communicated |
| cfo-advisor | Financial narrative for investors and board |
| scenario-war-room | Crisis scenarios that may require communication plans |
| cs-onboard | Building the foundational company context that feeds all narratives |
---
Troubleshooting
| Problem | Likely Cause | Resolution |
|---|---|---|
| Employees describe the company mission differently across departments | Core narrative not established or not communicated with enough frequency | Rebuild core narrative using Step 1 template; communicate through 7+ channels; re-test with 5-person test after 2 weeks |
| Investor update and all-hands deck tell conflicting stories | Different authors without shared source-of-truth document | Create single core narrative document; all communications must derive from it; run contradiction detection before every external communication |
| All-hands Q&A produces only softball questions | Employees don't trust that honest questions are safe | Switch to anonymous question submission; answer the hardest question first; CEO models vulnerability by sharing a mistake |
| Crisis communication arrives after employees see it on social media | No 4-hour rule in place or no internal communication chain | Establish internal-first protocol with pre-drafted templates; designate single spokesperson; practice crisis drills quarterly |
| Change communication met with cynicism ("another reorganization") | Past changes communicated without follow-through on ADKAR reinforcement stage | Include 30-day progress update in every change plan; reference previous successful changes as evidence |
| Careers page describes a culture employees don't recognize | Marketing owns careers page without HR/employee input | Co-create careers content with current employees; include real employee stories; audit annually against engagement survey data |
| Stakeholder groups receiving inconsistent messaging about company priorities | No audience translation matrix maintained | Build and maintain the translation matrix from Step 2; review before every major communication cycle |
---
Success Criteria
- 5-person articulation test scores 8/10 or higher (4+ of 5 people give consistent answers about company priority)
- Contradiction detection protocol catches zero unresolved contradictions before major external communications
- All-hands open Q&A produces at least 5 unscreened questions per session with substantive CEO responses
- Investor updates sent on same date each month with < 500 words and at least one honest challenge included
- Crisis internal communication delivered within 4 hours of event becoming known, every time
- Change communication follows full ADKAR sequence with measurable reinforcement at 30 days
- Employee engagement survey shows "I understand company direction" scores above 80%
---
Scope & Limitations
- In scope: Core narrative construction, audience translation, contradiction detection, all-hands design, investor update templates, crisis communication frameworks, change communication using ADKAR, communication cadence design
- Out of scope: PR and media relations strategy (use CMO Advisor); legal review of external statements (use legal counsel); employer branding campaigns (use CHRO Advisor); social media content strategy
- Limitation: Narrative consistency requires ongoing maintenance; a one-time exercise degrades within 1-2 quarters without reinforcement
- Limitation: Crisis communication templates are starting points; legal review is always required for statements involving liability
- Limitation: Framework assumes good-faith leadership; narrative architecture cannot fix fundamentally dishonest communication
---
Integration Points
| Skill | Integration | Data Flow |
|---|---|---|
ceo-advisor | CEO strategic decisions require narrative communication | CEO decisions → Narrative framing for each audience |
cfo-advisor | Financial narrative for investors must align with company narrative | CFO metrics → Investor update narrative |
cmo-advisor | External marketing narrative must match internal story | Narrative core → Marketing messaging alignment |
chro-advisor | Recruiting narrative must reflect employee reality | Narrative careers content → CHRO validation |
scenario-war-room | Crisis scenarios require pre-built communication plans | War room scenarios → Crisis narrative templates |
strategic-alignment | Strategy cascade depends on clear narrative communication | Narrative clarity → Alignment articulation test |
change-management | Every change initiative requires narrative support | Change plan → Narrative ADKAR communication |
---
Python Tools
| Tool | Purpose | Usage |
|---|---|---|
scripts/narrative_consistency_checker.py | Check two or more communication texts for factual contradictions and tone mismatches | python scripts/narrative_consistency_checker.py --texts investor_update.txt allhands_deck.txt --json |
scripts/messaging_framework_generator.py | Generate an audience translation matrix from a core narrative statement | python scripts/messaging_framework_generator.py --narrative "We are shifting from product A to product B" --audiences employees,investors,customers --json |
scripts/stakeholder_mapper.py | Map stakeholders by influence, interest, and communication needs | python scripts/stakeholder_mapper.py add --name "Board of Directors" --influence high --interest high --frequency quarterly --json |
#!/usr/bin/env python3
"""Messaging Framework Generator - Generate audience translation matrix from core narrative.
Takes a core narrative statement and generates audience-specific messaging for
employees, investors, customers, candidates, and partners.
Usage:
python messaging_framework_generator.py --narrative "We are shifting from product A to product B"
python messaging_framework_generator.py --narrative "We missed Q2 targets by 15%" --audiences employees,investors,customers --json
"""
import argparse
import json
import sys
from datetime import datetime
AUDIENCE_LENSES = {
"employees": {
"name": "Employees",
"primary_question": "How does this affect my job, my team, and the company's future?",
"communication_principles": [
"Lead with honesty -- employees detect inauthenticity instantly",
"Explain the 'why' before the 'what'",
"Address job impact directly if relevant",
"Provide clear next steps and how they can contribute",
"Follow up within 48 hours with additional details"
],
"tone": "Direct, honest, empathetic",
"channel": "All-hands + written follow-up email",
"timing": "First audience to hear (internal-first rule)",
"frame_templates": {
"positive_change": "This positions us for [outcome]. Here is what it means for your work: [specific impact]. Your contribution to this is [role].",
"negative_change": "Here is what happened: [fact]. Here is why: [honest reason]. Here is the plan: [specific actions]. Here is how you can help: [contribution].",
"strategic_shift": "We are making this change because [evidence-based reason]. Your work on [previous] matters because [connection]. Going forward, [new direction].",
"uncertainty": "Here is what we know: [facts]. Here is what we do not know yet: [honest uncertainty]. I will update you by [specific date]."
}
},
"investors": {
"name": "Investors",
"primary_question": "What does this mean for the business trajectory and my investment?",
"communication_principles": [
"Lead with metrics and business impact",
"Frame in terms of capital efficiency and growth",
"Include challenges alongside wins (builds trust)",
"Make specific asks (intros, advice, decisions needed)",
"Keep concise -- under 500 words for updates"
],
"tone": "Data-driven, strategic, confident but honest",
"channel": "Monthly written update + board meeting",
"timing": "After employees, before external",
"frame_templates": {
"positive_change": "[Metric impact]: This drives [financial outcome]. Strategic rationale: [evidence]. Expected timeline: [specific].",
"negative_change": "[Metric] came in at [actual] vs [target]. Root cause: [analysis]. Recovery plan: [specific actions with timeline]. Ask: [specific help needed].",
"strategic_shift": "Market signal: [data]. Our response: [strategic move]. Expected impact: [financial projection]. Investment needed: [amount/resources].",
"uncertainty": "Current position: [metrics]. Scenario range: [base/stress/severe]. Hedges in place: [specific]. Decision point: [date]."
}
},
"customers": {
"name": "Customers",
"primary_question": "How does this affect my experience, my product, and my relationship with this company?",
"communication_principles": [
"Only communicate what is relevant to their experience",
"Focus on value and continuity of service",
"Proactive communication prevents speculation",
"Provide clear point of contact for questions",
"Never share internal financial struggles unless directly relevant"
],
"tone": "Professional, reassuring, value-focused",
"channel": "Email + account manager outreach for key accounts",
"timing": "After internal alignment, before or alongside external",
"frame_templates": {
"positive_change": "We are [change] to better serve you. What this means for you: [specific benefit]. Your current [service/product] [continuity statement].",
"negative_change": "We are aware of [issue]. Impact to you: [honest assessment]. What we are doing: [fix with timeline]. Your account manager [name] is available for questions.",
"strategic_shift": "We are focusing on [new direction] because [customer benefit]. This means [positive impact]. Your [existing commitment] remains [unchanged/details].",
"uncertainty": "We want to keep you informed: [relevant fact]. Your service is [status]. We will update you by [date]. Contact [person] with questions."
}
},
"candidates": {
"name": "Candidates",
"primary_question": "Is this a company I want to join? Is it growing, stable, and exciting?",
"communication_principles": [
"Narrative must match employee reality (candidates will check)",
"Highlight decisive leadership and clear direction",
"Show both ambition and self-awareness",
"Connect to mission and growth opportunity",
"Be honest about challenges -- self-aware companies attract talent"
],
"tone": "Energetic, mission-driven, transparent",
"channel": "Careers page + interview conversations",
"timing": "Updated within 1 week of major changes",
"frame_templates": {
"positive_change": "We are [change] because we see [opportunity]. This creates roles in [areas]. Join us as we [exciting direction].",
"negative_change": "[Not typically shared externally unless public; if public:] We faced [challenge] and responded by [decisive action]. This is the kind of company that [positive framing of character].",
"strategic_shift": "We made a strategic decision to focus on [area] based on [evidence]. This is an exciting time to join because [opportunity for candidate].",
"uncertainty": "[Minimize; focus on vision and what is clear:] Our direction is [clear statement]. We are building [exciting thing] and looking for people who [traits]."
}
},
"partners": {
"name": "Partners",
"primary_question": "Does this affect our integration, our joint roadmap, or our business relationship?",
"communication_principles": [
"Proactive communication preserves trust",
"Focus on integration and joint value",
"Be specific about timeline and technical impacts",
"Provide clear escalation path for concerns",
"Schedule business review if change is significant"
],
"tone": "Professional, collaborative, specific",
"channel": "Direct communication from partnership lead",
"timing": "After internal, before public announcement",
"frame_templates": {
"positive_change": "We are [change]. Impact to our partnership: [specific]. Joint opportunity: [collaboration]. Next step: [meeting/review].",
"negative_change": "Change affecting our integration: [specific]. Mitigation: [plan]. Timeline: [dates]. Let's schedule a call to discuss: [proposed time].",
"strategic_shift": "Our strategic focus is shifting to [area]. For our partnership, this means: [impact]. Opportunities: [joint value]. Review meeting: [proposed date].",
"uncertainty": "We are evaluating [topic]. Potential impact to partnership: [range]. We will share more by [date]. Current commitments: [unchanged/details]."
}
}
}
def classify_narrative(narrative):
"""Classify the narrative type based on content signals."""
lower = narrative.lower()
if any(word in lower for word in ["missed", "failed", "loss", "decline", "below", "reduction", "layoff", "cut"]):
return "negative_change"
if any(word in lower for word in ["shifting", "pivot", "moving", "transitioning", "restructuring", "changing direction"]):
return "strategic_shift"
if any(word in lower for word in ["uncertain", "evaluating", "exploring", "don't know", "investigating"]):
return "uncertainty"
return "positive_change"
def generate_framework(narrative, audiences):
"""Generate messaging framework from core narrative."""
narrative_type = classify_narrative(narrative)
translations = []
for aud_key in audiences:
if aud_key not in AUDIENCE_LENSES:
continue
lens = AUDIENCE_LENSES[aud_key]
template = lens["frame_templates"].get(narrative_type, lens["frame_templates"]["positive_change"])
translations.append({
"audience": lens["name"],
"key": aud_key,
"primary_question": lens["primary_question"],
"recommended_tone": lens["tone"],
"channel": lens["channel"],
"timing": lens["timing"],
"message_template": template,
"principles": lens["communication_principles"]
})
# Consistency reminders
consistency_checks = [
"Verify: all audiences receive the same underlying facts",
"Verify: no audience learns about this from a different audience first",
"Verify: tone differences reflect audience needs, not different facts",
"Verify: metrics cited are consistent across all communications",
"Verify: timeline commitments are the same everywhere"
]
return {
"generated_date": datetime.now().strftime("%Y-%m-%d"),
"core_narrative": narrative,
"narrative_type": narrative_type,
"audiences": len(translations),
"translation_matrix": translations,
"consistency_checks": consistency_checks,
"communication_sequence": [
{"order": 1, "audience": "Employees", "timing": "First (internal-first rule)"},
{"order": 2, "audience": "Board/Investors", "timing": "Same day or next day"},
{"order": 3, "audience": "Partners", "timing": "Before public announcement"},
{"order": 4, "audience": "Customers", "timing": "Before or with public announcement"},
{"order": 5, "audience": "Candidates/Public", "timing": "After all stakeholders informed"}
]
}
def print_human(result):
print(f"\n{'='*70}")
print(f"MESSAGING FRAMEWORK")
print(f"Generated: {result['generated_date']}")
print(f"{'='*70}\n")
print(f"CORE NARRATIVE: {result['core_narrative']}")
print(f"TYPE: {result['narrative_type'].replace('_', ' ').title()}\n")
for t in result["translation_matrix"]:
print(f"\n--- {t['audience'].upper()} ---")
print(f" They ask: {t['primary_question']}")
print(f" Tone: {t['recommended_tone']}")
print(f" Channel: {t['channel']}")
print(f" Timing: {t['timing']}")
print(f"\n Message Template:")
print(f" {t['message_template']}")
print(f"\n Principles:")
for p in t["principles"]:
print(f" - {p}")
print(f"\nCOMMUNICATION SEQUENCE:")
for s in result["communication_sequence"]:
print(f" {s['order']}. {s['audience']} ({s['timing']})")
print(f"\nCONSISTENCY CHECKS:")
for c in result["consistency_checks"]:
print(f" [ ] {c}")
print()
def main():
parser = argparse.ArgumentParser(description="Generate audience translation matrix from core narrative")
parser.add_argument("--narrative", required=True, help="Core narrative statement")
parser.add_argument("--audiences", default="employees,investors,customers,candidates,partners",
help="Comma-separated audiences")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
audiences = [a.strip().lower() for a in args.audiences.split(",")]
result = generate_framework(args.narrative, audiences)
if args.json:
print(json.dumps(result, indent=2))
else:
print_human(result)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Narrative Consistency Checker - Check communications for contradictions and tone mismatches.
Analyzes two or more communication texts (e.g., investor update and all-hands deck)
for factual inconsistencies, tone mismatches, and narrative contradictions. Uses keyword
and phrase analysis to detect conflicting signals.
Usage:
python narrative_consistency_checker.py --texts investor_update.txt allhands_deck.txt
python narrative_consistency_checker.py --texts doc1.txt doc2.txt doc3.txt --json
python narrative_consistency_checker.py --text1 "We are growing efficiently" --text2 "We are hiring aggressively" --json
"""
import argparse
import json
import os
import re
import sys
from datetime import datetime
from collections import Counter
# Contradiction signal pairs - if one text uses signals from column A and another
# from column B on the same topic, flag as potential contradiction
CONTRADICTION_SIGNALS = [
{"topic": "Growth Strategy", "signal_a": ["efficient growth", "capital efficient", "lean", "disciplined spending", "cost reduction", "cutting costs"],
"signal_b": ["hiring aggressively", "aggressive expansion", "rapid growth", "scaling fast", "doubling the team", "massive investment"]},
{"topic": "Revenue Health", "signal_a": ["strong pipeline", "accelerating revenue", "exceeding targets", "beating plan", "record quarter"],
"signal_b": ["sales struggling", "missed targets", "pipeline thin", "below forecast", "revenue miss", "underperforming"]},
{"topic": "Team Stability", "signal_a": ["world-class team", "strong culture", "low turnover", "talent magnet", "best team"],
"signal_b": ["high turnover", "hiring challenges", "attrition", "retention issues", "people leaving", "morale issues"]},
{"topic": "Market Position", "signal_a": ["market leader", "dominant position", "winning", "category leader", "outpacing competitors"],
"signal_b": ["competitive threat", "losing deals", "market share declining", "competitive pressure", "behind competitors"]},
{"topic": "Financial Health", "signal_a": ["strong runway", "well capitalized", "financially healthy", "cash positive"],
"signal_b": ["runway concerns", "need to raise", "burn rate high", "cash tight", "bridge round", "extending runway"]},
{"topic": "Product Status", "signal_a": ["product-market fit", "customers love", "high NPS", "strong adoption", "product led"],
"signal_b": ["product issues", "churn increasing", "feature gaps", "product behind", "customers frustrated", "NPS declining"]},
]
# Tone categories
TONE_SIGNALS = {
"optimistic": ["excited", "thrilled", "incredible", "amazing", "outstanding", "breakthrough", "record", "transformative", "revolutionary"],
"measured": ["progressing", "improving", "on track", "steady", "developing", "building", "iterating"],
"cautious": ["challenging", "headwinds", "adjusting", "recalibrating", "learning", "pivoting"],
"urgent": ["critical", "must", "immediately", "crisis", "emergency", "urgent", "deadline", "risk"],
"transparent": ["missed", "failed", "mistake", "learned", "honest", "acknowledge", "fell short"],
}
def analyze_text(text, label):
"""Analyze a single text for signals and tone."""
text_lower = text.lower()
words = re.findall(r'\b\w+\b', text_lower)
word_count = len(words)
# Find contradiction signals present
signals_found = []
for pair in CONTRADICTION_SIGNALS:
for signal in pair["signal_a"]:
if signal in text_lower:
signals_found.append({"topic": pair["topic"], "signal": signal, "side": "a"})
for signal in pair["signal_b"]:
if signal in text_lower:
signals_found.append({"topic": pair["topic"], "signal": signal, "side": "b"})
# Detect tone
tone_scores = {}
for tone, keywords in TONE_SIGNALS.items():
count = sum(1 for kw in keywords if kw in text_lower)
if count > 0:
tone_scores[tone] = count
primary_tone = max(tone_scores, key=tone_scores.get) if tone_scores else "neutral"
# Extract numbers/metrics mentioned
metrics = re.findall(r'[\$€£]?\d+[\d,]*\.?\d*[%KMBx]?', text)
return {
"label": label,
"word_count": word_count,
"signals_found": signals_found,
"tone_scores": tone_scores,
"primary_tone": primary_tone,
"metrics_mentioned": metrics[:20] # Cap at 20
}
def find_contradictions(analyses):
"""Compare analyses across texts to find contradictions."""
contradictions = []
tone_mismatches = []
# Check for signal contradictions between texts
for i in range(len(analyses)):
for j in range(i + 1, len(analyses)):
a_signals = analyses[i]["signals_found"]
b_signals = analyses[j]["signals_found"]
for topic in set(s["topic"] for s in a_signals + b_signals):
a_sides = set(s["side"] for s in a_signals if s["topic"] == topic)
b_sides = set(s["side"] for s in b_signals if s["topic"] == topic)
if "a" in a_sides and "b" in b_sides:
a_terms = [s["signal"] for s in a_signals if s["topic"] == topic and s["side"] == "a"]
b_terms = [s["signal"] for s in b_signals if s["topic"] == topic and s["side"] == "b"]
contradictions.append({
"topic": topic,
"text_a": analyses[i]["label"],
"text_a_says": a_terms,
"text_b": analyses[j]["label"],
"text_b_says": b_terms,
"severity": "HIGH",
"recommendation": f"Resolve {topic} narrative: {analyses[i]['label']} signals positive while {analyses[j]['label']} signals negative"
})
elif "b" in a_sides and "a" in b_sides:
a_terms = [s["signal"] for s in a_signals if s["topic"] == topic and s["side"] == "b"]
b_terms = [s["signal"] for s in b_signals if s["topic"] == topic and s["side"] == "a"]
contradictions.append({
"topic": topic,
"text_a": analyses[i]["label"],
"text_a_says": a_terms,
"text_b": analyses[j]["label"],
"text_b_says": b_terms,
"severity": "HIGH",
"recommendation": f"Resolve {topic} narrative: {analyses[i]['label']} signals negative while {analyses[j]['label']} signals positive"
})
# Check tone mismatch
tone_a = analyses[i]["primary_tone"]
tone_b = analyses[j]["primary_tone"]
if tone_a != tone_b:
severity = "LOW"
if (tone_a in ["optimistic", "measured"] and tone_b in ["urgent", "cautious"]) or \
(tone_b in ["optimistic", "measured"] and tone_a in ["urgent", "cautious"]):
severity = "MEDIUM"
tone_mismatches.append({
"text_a": analyses[i]["label"],
"tone_a": tone_a,
"text_b": analyses[j]["label"],
"tone_b": tone_b,
"severity": severity,
"recommendation": f"Tone gap between {analyses[i]['label']} ({tone_a}) and {analyses[j]['label']} ({tone_b})"
})
return contradictions, tone_mismatches
def check_consistency(texts_with_labels):
"""Main analysis function."""
analyses = []
for label, text in texts_with_labels:
analyses.append(analyze_text(text, label))
contradictions, tone_mismatches = find_contradictions(analyses)
# Overall consistency score
contradiction_penalty = len(contradictions) * 15
tone_penalty = sum(5 if t["severity"] == "MEDIUM" else 2 for t in tone_mismatches)
consistency_score = max(0, 100 - contradiction_penalty - tone_penalty)
return {
"check_date": datetime.now().strftime("%Y-%m-%d"),
"documents_analyzed": len(analyses),
"consistency_score": consistency_score,
"consistency_rating": "CONSISTENT" if consistency_score >= 80 else "CONCERNS" if consistency_score >= 50 else "CONTRADICTIONS FOUND",
"contradictions": contradictions,
"tone_mismatches": tone_mismatches,
"document_summaries": [
{"label": a["label"], "word_count": a["word_count"], "primary_tone": a["primary_tone"],
"signals_count": len(a["signals_found"]), "metrics_count": len(a["metrics_mentioned"])}
for a in analyses
],
"recommendations": [c["recommendation"] for c in contradictions] +
[t["recommendation"] for t in tone_mismatches if t["severity"] != "LOW"]
}
def print_human(result):
print(f"\n{'='*70}")
print(f"NARRATIVE CONSISTENCY CHECK")
print(f"Date: {result['check_date']}")
print(f"Documents: {result['documents_analyzed']}")
print(f"{'='*70}\n")
print(f"CONSISTENCY SCORE: {result['consistency_score']}/100 ({result['consistency_rating']})\n")
print("DOCUMENT SUMMARIES:")
print("-" * 50)
for d in result["document_summaries"]:
print(f" {d['label']}: {d['word_count']} words, tone: {d['primary_tone']}, {d['signals_count']} signals")
if result["contradictions"]:
print(f"\nCONTRADICTIONS FOUND ({len(result['contradictions'])}):")
print("-" * 50)
for c in result["contradictions"]:
print(f" [{c['severity']}] {c['topic']}")
print(f" {c['text_a']} says: {', '.join(c['text_a_says'])}")
print(f" {c['text_b']} says: {', '.join(c['text_b_says'])}")
if result["tone_mismatches"]:
print(f"\nTONE MISMATCHES ({len(result['tone_mismatches'])}):")
for t in result["tone_mismatches"]:
print(f" [{t['severity']}] {t['text_a']} ({t['tone_a']}) vs {t['text_b']} ({t['tone_b']})")
if result["recommendations"]:
print(f"\nRECOMMENDATIONS:")
for r in result["recommendations"]:
print(f" -> {r}")
else:
print(f"\nNo contradictions or significant tone mismatches detected.")
print()
def main():
parser = argparse.ArgumentParser(description="Check communications for narrative contradictions")
parser.add_argument("--texts", nargs="+", help="File paths to check (2+)")
parser.add_argument("--text1", help="First text string (alternative to files)")
parser.add_argument("--text2", help="Second text string (alternative to files)")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
texts_with_labels = []
if args.texts:
for path in args.texts:
if not os.path.exists(path):
print(f"Error: File not found: {path}", file=sys.stderr)
sys.exit(1)
with open(path, "r") as f:
texts_with_labels.append((os.path.basename(path), f.read()))
elif args.text1 and args.text2:
texts_with_labels.append(("Text 1", args.text1))
texts_with_labels.append(("Text 2", args.text2))
else:
print("Error: Provide either --texts with file paths or --text1 and --text2", file=sys.stderr)
sys.exit(1)
if len(texts_with_labels) < 2:
print("Error: At least 2 texts required for comparison", file=sys.stderr)
sys.exit(1)
result = check_consistency(texts_with_labels)
if args.json:
print(json.dumps(result, indent=2))
else:
print_human(result)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Stakeholder Mapper - Map stakeholders by influence, interest, and communication needs.
Build and maintain a stakeholder map with influence/interest scoring, communication
frequency recommendations, and engagement strategy. Stored in local JSON for persistence.
Usage:
python stakeholder_mapper.py add --name "Board of Directors" --influence high --interest high --frequency quarterly
python stakeholder_mapper.py list
python stakeholder_mapper.py matrix
python stakeholder_mapper.py report --json
"""
import argparse
import json
import os
import sys
from datetime import datetime
DEFAULT_STORE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "stakeholder_data.json")
INFLUENCE_LEVELS = {"low": 1, "medium": 2, "high": 3}
INTEREST_LEVELS = {"low": 1, "medium": 2, "high": 3}
QUADRANT_STRATEGIES = {
"manage_closely": {
"quadrant": "High Influence / High Interest",
"strategy": "Manage Closely - key players who need regular, detailed communication",
"actions": [
"Engage frequently and proactively",
"Involve in decision-making where appropriate",
"Anticipate their concerns and address preemptively",
"Use face-to-face or video communication"
],
"recommended_frequency": "Weekly or bi-weekly"
},
"keep_satisfied": {
"quadrant": "High Influence / Low Interest",
"strategy": "Keep Satisfied - powerful but not deeply engaged; keep informed without overwhelming",
"actions": [
"Provide executive summaries, not detailed reports",
"Engage on decisions that affect their domain",
"Anticipate their needs and proactively address",
"Minimize effort required from them"
],
"recommended_frequency": "Monthly"
},
"keep_informed": {
"quadrant": "Low Influence / High Interest",
"strategy": "Keep Informed - engaged supporters who need updates to maintain confidence",
"actions": [
"Regular updates through scalable channels",
"Invite feedback and input",
"Leverage their enthusiasm for internal advocacy",
"Provide channels for questions"
],
"recommended_frequency": "Bi-weekly to monthly"
},
"monitor": {
"quadrant": "Low Influence / Low Interest",
"strategy": "Monitor - minimal effort; keep on radar without over-investing",
"actions": [
"Include in broad communications (all-hands, company updates)",
"Do not require their engagement",
"Watch for changes in influence or interest",
"Periodic check-in to reassess positioning"
],
"recommended_frequency": "Quarterly"
}
}
def get_quadrant(influence, interest):
inf = INFLUENCE_LEVELS.get(influence, 2)
int_ = INTEREST_LEVELS.get(interest, 2)
if inf >= 2 and int_ >= 2:
return "manage_closely"
elif inf >= 2 and int_ < 2:
return "keep_satisfied"
elif inf < 2 and int_ >= 2:
return "keep_informed"
return "monitor"
def load_data(store_path):
if os.path.exists(store_path):
with open(store_path, "r") as f:
return json.load(f)
return {"stakeholders": [], "next_id": 1}
def save_data(data, store_path):
with open(store_path, "w") as f:
json.dump(data, f, indent=2)
def add_stakeholder(data, name, influence, interest, frequency, role="", notes=""):
quadrant = get_quadrant(influence, interest)
strategy = QUADRANT_STRATEGIES[quadrant]
stakeholder = {
"id": data["next_id"],
"name": name,
"role": role,
"influence": influence,
"interest": interest,
"frequency": frequency,
"quadrant": quadrant,
"quadrant_label": strategy["quadrant"],
"strategy": strategy["strategy"],
"notes": notes,
"created": datetime.now().strftime("%Y-%m-%d"),
"last_communication": None
}
data["stakeholders"].append(stakeholder)
data["next_id"] += 1
return stakeholder
def generate_matrix(data):
"""Generate stakeholder matrix visualization."""
matrix = {q: [] for q in QUADRANT_STRATEGIES}
for s in data["stakeholders"]:
matrix[s["quadrant"]].append({"name": s["name"], "role": s["role"]})
return matrix
def generate_report(data):
stakeholders = data["stakeholders"]
matrix = generate_matrix(data)
by_quadrant = {}
for q, names in matrix.items():
by_quadrant[q] = {
"label": QUADRANT_STRATEGIES[q]["quadrant"],
"count": len(names),
"stakeholders": names,
"strategy": QUADRANT_STRATEGIES[q]["strategy"],
"actions": QUADRANT_STRATEGIES[q]["actions"]
}
return {
"report_date": datetime.now().strftime("%Y-%m-%d"),
"total_stakeholders": len(stakeholders),
"by_quadrant": by_quadrant,
"stakeholders": stakeholders,
"communication_plan": [
{
"name": s["name"],
"frequency": s["frequency"],
"quadrant": s["quadrant_label"],
"last_communication": s["last_communication"] or "Never"
}
for s in sorted(stakeholders, key=lambda x: INFLUENCE_LEVELS.get(x["influence"], 0), reverse=True)
]
}
def print_matrix_human(matrix):
print(f"\n{'='*70}")
print(f"STAKEHOLDER MATRIX")
print(f"{'='*70}\n")
print(f" HIGH INTEREST LOW INTEREST")
print(f" +-----------------------+-----------------------+")
manage = [s["name"] for s in matrix.get("manage_closely", [])]
satisfy = [s["name"] for s in matrix.get("keep_satisfied", [])]
print(f" HIGH INFLUENCE | MANAGE CLOSELY | KEEP SATISFIED |")
for i in range(max(len(manage), len(satisfy), 1)):
m = manage[i] if i < len(manage) else ""
s = satisfy[i] if i < len(satisfy) else ""
print(f" | {m:<21s} | {s:<21s} |")
print(f" +-----------------------+-----------------------+")
inform = [s["name"] for s in matrix.get("keep_informed", [])]
monitor = [s["name"] for s in matrix.get("monitor", [])]
print(f" LOW INFLUENCE | KEEP INFORMED | MONITOR |")
for i in range(max(len(inform), len(monitor), 1)):
inf = inform[i] if i < len(inform) else ""
mon = monitor[i] if i < len(monitor) else ""
print(f" | {inf:<21s} | {mon:<21s} |")
print(f" +-----------------------+-----------------------+")
print()
def print_report_human(report):
print(f"\n{'='*70}")
print(f"STAKEHOLDER MAP REPORT - {report['report_date']}")
print(f"Total Stakeholders: {report['total_stakeholders']}")
print(f"{'='*70}\n")
for q_key, q_data in report["by_quadrant"].items():
if q_data["count"] > 0:
print(f"\n{q_data['label']} ({q_data['count']})")
print(f" Strategy: {q_data['strategy']}")
for s in q_data["stakeholders"]:
print(f" - {s['name']}" + (f" ({s['role']})" if s["role"] else ""))
print(f"\nCOMMUNICATION PLAN:")
print("-" * 60)
for cp in report["communication_plan"]:
print(f" {cp['name']:<25s} {cp['frequency']:<12s} Last: {cp['last_communication']}")
print()
def main():
parser = argparse.ArgumentParser(description="Map stakeholders by influence and interest")
subparsers = parser.add_subparsers(dest="command")
add_p = subparsers.add_parser("add", help="Add stakeholder")
add_p.add_argument("--name", required=True)
add_p.add_argument("--influence", required=True, choices=["low", "medium", "high"])
add_p.add_argument("--interest", required=True, choices=["low", "medium", "high"])
add_p.add_argument("--frequency", default="monthly", choices=["weekly", "bi-weekly", "monthly", "quarterly", "annually"])
add_p.add_argument("--role", default="")
add_p.add_argument("--notes", default="")
add_p.add_argument("--json", action="store_true")
add_p.add_argument("--store", default=DEFAULT_STORE)
list_p = subparsers.add_parser("list", help="List stakeholders")
list_p.add_argument("--json", action="store_true")
list_p.add_argument("--store", default=DEFAULT_STORE)
matrix_p = subparsers.add_parser("matrix", help="Show stakeholder matrix")
matrix_p.add_argument("--json", action="store_true")
matrix_p.add_argument("--store", default=DEFAULT_STORE)
report_p = subparsers.add_parser("report", help="Full stakeholder report")
report_p.add_argument("--json", action="store_true")
report_p.add_argument("--store", default=DEFAULT_STORE)
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
data = load_data(args.store)
if args.command == "add":
s = add_stakeholder(data, args.name, args.influence, args.interest, args.frequency, args.role, args.notes)
save_data(data, args.store)
if args.json:
print(json.dumps(s, indent=2))
else:
print(f"Stakeholder #{s['id']} added: {s['name']} -> {s['quadrant_label']}")
elif args.command == "list":
if args.json:
print(json.dumps(data["stakeholders"], indent=2))
else:
for s in data["stakeholders"]:
print(f" #{s['id']} {s['name']:<25s} Inf:{s['influence']:<6s} Int:{s['interest']:<6s} -> {s['quadrant_label']}")
elif args.command == "matrix":
matrix = generate_matrix(data)
if args.json:
print(json.dumps(matrix, indent=2))
else:
print_matrix_human(matrix)
elif args.command == "report":
report = generate_report(data)
if args.json:
print(json.dumps(report, indent=2))
else:
print_report_human(report)
if __name__ == "__main__":
main()
Related skills
FAQ
What is the core narrative?
One paragraph every other communication derives from, stating why the company exists, what it builds and why, its approach, and its honest current and target state.
What is the contradiction-detection protocol?
Before a major communication, you check what was told to investors and to employees on the topic and whether they are consistent, since people talk to each other.