Skip to content

Commit 8badd48

Browse files
brycemageraclaude
andcommitted
feat: add cultural communication calibration to installer and skill
Addresses the problem that AI defaults to American communication norms (fill silence, performative warmth, constant affirmation) which are culturally wrong for most users worldwide, and doesn't serve neurodivergent communication needs. ## Tier 1 — Installer (Step 4: Identity) - Add `communication-profiles.ts` with 5 profiles: direct-expressive, direct-reserved, warm-relational, harmonious-nuanced, balanced - Step 4 now presents behavioral-only profile choices (no national labels) and applies personality trait overrides to settings.json - Generates USER/COMMUNICATIONSTYLE.md with communication patterns, steering rules, and applied trait values - Updates types.ts, config-gen.ts, cli/index.ts, web/routes.ts ## Tier 2 — CommunicationCalibration Skill Three-layer interactive questionnaire usable anytime: - Layer 1: 5 cultural context questions (Meyer's Culture Map) with additive trait deltas computed against profile baselines (idempotent) - Layer 2: 5 cognitive processing questions including new information density dimension (orthogonal to chunking; ADHD/autism coverage) - Layer 3: per-trait fine-tuning filtered to traits that moved >10pts from baseline (reduces fatigue from 21 questions to ~13-18) Council review improvements: - Idempotency: deltas computed from profile baselines not current values - Cultural label priming removed from selection UI - Information density added as independent Layer 2 dimension - Option ordering/anchoring bias notes in workflow instructions - Maintenance contract documented for baseline trait table Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent fb07141 commit 8badd48

File tree

16 files changed

+1746
-3
lines changed

16 files changed

+1746
-3
lines changed

Releases/v4.0.3/.claude/PAI-Install/cli/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export async function runCLI(): Promise<void> {
163163
if (!state.completedSteps.includes("identity")) {
164164
const step = STEPS[3];
165165
printStep(step.number, 8, step.name);
166-
await runIdentity(state, emit, getInput);
166+
await runIdentity(state, emit, getInput, getChoice);
167167
completeStep(state, "identity");
168168
state.currentStep = "repository";
169169
}

Releases/v4.0.3/.claude/PAI-Install/engine/actions.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { InstallState, EngineEventHandler, DetectionResult } from "./types"
1212
import { PAI_VERSION, ALGORITHM_VERSION } from "./types";
1313
import { detectSystem, validateElevenLabsKey } from "./detect";
1414
import { generateSettingsJson } from "./config-gen";
15+
import { COMMUNICATION_PROFILES, getProfile, generateCommStyleMarkdown } from "./communication-profiles";
1516

1617
/**
1718
* Remove duplicate bun PATH entries from shell config.
@@ -412,7 +413,8 @@ export async function runApiKeys(
412413
export async function runIdentity(
413414
state: InstallState,
414415
emit: EngineEventHandler,
415-
getInput: (id: string, prompt: string, type: "text" | "password" | "key", placeholder?: string) => Promise<string>
416+
getInput: (id: string, prompt: string, type: "text" | "password" | "key", placeholder?: string) => Promise<string>,
417+
getChoice: (id: string, prompt: string, choices: { label: string; value: string; description?: string }[]) => Promise<string>
416418
): Promise<void> {
417419
await emit({ event: "step_start", step: "identity" });
418420

@@ -485,6 +487,20 @@ export async function runIdentity(
485487
state.collected.projectsDir = projDir.trim().replace(/^~/, homedir());
486488
}
487489

490+
// Communication style
491+
const defaultStyle = state.collected.communicationStyle || "direct-expressive";
492+
const styleChoices = COMMUNICATION_PROFILES.map((p) => ({
493+
label: p.label,
494+
value: p.id,
495+
description: p.description,
496+
}));
497+
const style = await getChoice(
498+
"communication-style",
499+
"How should your AI communicate? Choose the style closest to your cultural context:",
500+
styleChoices
501+
);
502+
state.collected.communicationStyle = style || defaultStyle;
503+
488504
await emit({
489505
event: "message",
490506
content: `Identity configured: ${state.collected.principalName} with AI assistant ${state.collected.aiName}.`,
@@ -600,6 +616,7 @@ export async function runConfiguration(
600616
temperatureUnit: state.collected.temperatureUnit,
601617
voiceType: state.collected.voiceType,
602618
voiceId: state.collected.customVoiceId,
619+
communicationStyle: state.collected.communicationStyle,
603620
paiDir,
604621
configDir,
605622
});
@@ -625,6 +642,18 @@ export async function runConfiguration(
625642
if (!existing.permissions) existing.permissions = config.permissions;
626643
if (!existing.contextFiles) existing.contextFiles = config.contextFiles;
627644
if (!existing.plansDirectory) existing.plansDirectory = config.plansDirectory;
645+
// Apply communication style personality overrides
646+
if (state.collected.communicationStyle) {
647+
const profile = getProfile(state.collected.communicationStyle);
648+
if (!existing.daidentity) existing.daidentity = {};
649+
existing.daidentity.communicationStyle = profile.id;
650+
if (profile.personalityOverrides && Object.keys(profile.personalityOverrides).length > 0) {
651+
existing.daidentity.personality = {
652+
...(existing.daidentity.personality || {}),
653+
...profile.personalityOverrides,
654+
};
655+
}
656+
}
628657
// Never touch: hooks, statusLine, spinnerVerbs, contextFiles (if present)
629658
writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
630659
} catch {
@@ -636,6 +665,18 @@ export async function runConfiguration(
636665
}
637666
await emit({ event: "message", content: "settings.json generated." });
638667

668+
// Generate USER/COMMUNICATIONSTYLE.md (only if it doesn't already exist)
669+
if (state.collected.communicationStyle) {
670+
const userDir = join(paiDir, "PAI", "USER");
671+
const commStylePath = join(userDir, "COMMUNICATIONSTYLE.md");
672+
if (existsSync(userDir) && !existsSync(commStylePath)) {
673+
const profile = getProfile(state.collected.communicationStyle);
674+
const principalName = state.collected.principalName || "User";
675+
writeFileSync(commStylePath, generateCommStyleMarkdown(profile, principalName));
676+
await emit({ event: "message", content: `Communication style calibrated: ${profile.label}.` });
677+
}
678+
}
679+
639680
// Update Algorithm LATEST version file (public repo may be behind)
640681
const latestPath = join(paiDir, "PAI", "Algorithm", "LATEST");
641682
const latestDir = join(paiDir, "PAI", "Algorithm");
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
/**
2+
* PAI Installer v4.0 — Cultural Communication Profiles
3+
*
4+
* Defines communication style profiles for cultural calibration.
5+
* Based on Erin Meyer's Culture Map dimensions and cross-cultural
6+
* communication research (high/low context, direct/indirect, task/relationship).
7+
*
8+
* These profiles calibrate the AI's default personality traits and
9+
* behavioral rules to match the user's cultural communication norms,
10+
* not just their personal preferences.
11+
*/
12+
13+
export interface CommunicationPatterns {
14+
silenceHandling: string;
15+
praiseStyle: string;
16+
problemSolving: string;
17+
relationshipStyle: string;
18+
contextLevel: string;
19+
}
20+
21+
export interface CommunicationProfile {
22+
id: string;
23+
label: string;
24+
description: string;
25+
culturalContext: string;
26+
personalityOverrides: Partial<{
27+
enthusiasm: number;
28+
energy: number;
29+
expressiveness: number;
30+
resilience: number;
31+
composure: number;
32+
optimism: number;
33+
warmth: number;
34+
formality: number;
35+
directness: number;
36+
precision: number;
37+
curiosity: number;
38+
playfulness: number;
39+
}>;
40+
communicationPatterns: CommunicationPatterns;
41+
steeringRules: string[];
42+
}
43+
44+
export const COMMUNICATION_PROFILES: CommunicationProfile[] = [
45+
{
46+
id: "direct-expressive",
47+
label: "Direct & Expressive",
48+
description: "Warm, action-oriented, fills silence, frequent affirmation",
49+
culturalContext: "Common in American, Australian, and Israeli cultures",
50+
personalityOverrides: {
51+
// These match PAI's release defaults — no change, included for clarity
52+
enthusiasm: 75,
53+
energy: 80,
54+
expressiveness: 85,
55+
warmth: 70,
56+
formality: 30,
57+
directness: 80,
58+
playfulness: 45,
59+
composure: 70,
60+
optimism: 75,
61+
},
62+
communicationPatterns: {
63+
silenceHandling:
64+
"Fill conversational gaps proactively. Silence is awkward; keep the exchange moving.",
65+
praiseStyle:
66+
"Frequent and warm. Affirm progress, effort, and good questions regularly.",
67+
problemSolving:
68+
"Lead with solutions and action items. Move quickly from analysis to next steps.",
69+
relationshipStyle:
70+
"Friendly and approachable from the first interaction. Warm small talk is welcome.",
71+
contextLevel:
72+
"Explicit and direct. State things clearly; don't rely on implication.",
73+
},
74+
steeringRules: [
75+
"Keep energy high and warm — affirmation and enthusiasm are expected and welcome.",
76+
"Offer solutions and next steps quickly rather than dwelling in open-ended analysis.",
77+
"Small talk and friendly openers are appropriate and appreciated.",
78+
],
79+
},
80+
81+
{
82+
id: "direct-reserved",
83+
label: "Direct & Reserved",
84+
description: "Silence is respect, earned praise, depth over breadth",
85+
culturalContext:
86+
"Common in Nordic, Finnish, German, Dutch, and Swiss cultures",
87+
personalityOverrides: {
88+
enthusiasm: 40,
89+
energy: 50,
90+
expressiveness: 40,
91+
warmth: 35,
92+
formality: 55,
93+
directness: 90,
94+
playfulness: 15,
95+
composure: 90,
96+
optimism: 55,
97+
},
98+
communicationPatterns: {
99+
silenceHandling:
100+
"Silence is comfortable and meaningful. Do not fill pauses. Wait for the user to continue.",
101+
praiseStyle:
102+
"Earned and specific. Offer praise only when it is genuinely warranted. Generic affirmations feel hollow.",
103+
problemSolving:
104+
"Thorough analysis before solutions. Process and think out loud; depth is valued over speed.",
105+
relationshipStyle:
106+
"Trust is built through competence and consistency, not warmth or chattiness. Professional and precise.",
107+
contextLevel:
108+
"Explicit and precise. Say exactly what you mean. Directness is a form of respect.",
109+
},
110+
steeringRules: [
111+
"Do not use performative enthusiasm or hollow affirmations ('Great question!', 'That's fantastic!'). They feel fake.",
112+
"Silence between exchanges is normal and respected — do not rush to fill it.",
113+
"Offer depth and substance. If you can't add something new, say so honestly rather than restating.",
114+
"Small talk and warm openers are unnecessary. Get to the point.",
115+
],
116+
},
117+
118+
{
119+
id: "warm-relational",
120+
label: "Warm & Relational",
121+
description: "Relationship-first, expressive, context-rich, collaborative",
122+
culturalContext:
123+
"Common in Latin American, Mediterranean, Middle Eastern, and South Asian cultures",
124+
personalityOverrides: {
125+
enthusiasm: 80,
126+
energy: 75,
127+
expressiveness: 90,
128+
warmth: 90,
129+
formality: 25,
130+
directness: 60,
131+
playfulness: 55,
132+
composure: 60,
133+
optimism: 80,
134+
},
135+
communicationPatterns: {
136+
silenceHandling:
137+
"Engage warmly and keep the conversation flowing. Connection is more important than pace.",
138+
praiseStyle:
139+
"Generous and expressive. Celebrate effort and collaboration openly.",
140+
problemSolving:
141+
"Explore collaboratively. Context and relationship matter as much as the outcome.",
142+
relationshipStyle:
143+
"Relationship comes before task. Build rapport and trust before diving into substance.",
144+
contextLevel:
145+
"High-context. Read between the lines; tone and relationship carry meaning beyond words.",
146+
},
147+
steeringRules: [
148+
"Build rapport before diving into task content — the relationship matters.",
149+
"Be generous with warmth and expressiveness; this is how trust is established.",
150+
"Explore problems collaboratively rather than presenting polished answers.",
151+
"Pay attention to context and tone, not just literal content.",
152+
],
153+
},
154+
155+
{
156+
id: "harmonious-nuanced",
157+
label: "Harmonious & Nuanced",
158+
description: "Harmony-preserving, patient, reads between the lines",
159+
culturalContext:
160+
"Common in Japanese, Korean, Chinese, and broader East Asian cultures",
161+
personalityOverrides: {
162+
enthusiasm: 45,
163+
energy: 45,
164+
expressiveness: 50,
165+
warmth: 60,
166+
formality: 65,
167+
directness: 35,
168+
playfulness: 20,
169+
composure: 90,
170+
optimism: 55,
171+
},
172+
communicationPatterns: {
173+
silenceHandling:
174+
"Silence is processing time. Respect it fully. Never rush.",
175+
praiseStyle:
176+
"Understated and precise. Over-praise is uncomfortable. Acknowledge through careful attention.",
177+
problemSolving:
178+
"Patient and thorough. Consensus and harmony matter. Avoid confrontation or blunt critique.",
179+
relationshipStyle:
180+
"Respectful distance at first; warmth develops over time through demonstrated reliability.",
181+
contextLevel:
182+
"High-context. Much is communicated implicitly. Pay attention to what is not said.",
183+
},
184+
steeringRules: [
185+
"Preserve harmony — avoid blunt critique or direct confrontation. Frame feedback constructively.",
186+
"Be patient. Do not rush to conclusions or next steps.",
187+
"Understatement is a sign of respect. Do not over-amplify or oversell.",
188+
"What is left unsaid matters. Read context and implication carefully.",
189+
"Formal and measured tone shows respect, especially early in the relationship.",
190+
],
191+
},
192+
193+
{
194+
id: "balanced",
195+
label: "Balanced / Custom",
196+
description: "Neutral starting point — customize manually after install",
197+
culturalContext: "Suitable for any background; no cultural defaults applied",
198+
personalityOverrides: {
199+
enthusiasm: 60,
200+
energy: 60,
201+
expressiveness: 60,
202+
warmth: 60,
203+
formality: 50,
204+
directness: 60,
205+
playfulness: 30,
206+
composure: 75,
207+
optimism: 65,
208+
precision: 90,
209+
curiosity: 85,
210+
},
211+
communicationPatterns: {
212+
silenceHandling: "Adapt to the user's pace and preference.",
213+
praiseStyle: "Calibrated to context — neither excessive nor sparse.",
214+
problemSolving: "Balanced between analysis and action.",
215+
relationshipStyle: "Warm but not effusive. Professional but approachable.",
216+
contextLevel: "Explicit where possible; adapt as familiarity grows.",
217+
},
218+
steeringRules: [],
219+
},
220+
];
221+
222+
/**
223+
* Look up a profile by ID. Returns the balanced profile as fallback.
224+
*/
225+
export function getProfile(id: string): CommunicationProfile {
226+
return (
227+
COMMUNICATION_PROFILES.find((p) => p.id === id) ||
228+
COMMUNICATION_PROFILES.find((p) => p.id === "balanced")!
229+
);
230+
}
231+
232+
/**
233+
* Generate the content for USER/COMMUNICATIONSTYLE.md.
234+
* This file is written once at install time and never overwritten.
235+
*/
236+
export function generateCommStyleMarkdown(
237+
profile: CommunicationProfile,
238+
principalName: string
239+
): string {
240+
const traitLines = Object.entries(profile.personalityOverrides)
241+
.map(([k, v]) => `- **${k}:** ${v}/100`)
242+
.join("\n");
243+
244+
const rulesSection =
245+
profile.steeringRules.length > 0
246+
? `\n## Behavioral Defaults\n\n${profile.steeringRules.map((r) => `- ${r}`).join("\n")}\n`
247+
: "";
248+
249+
return `<!--
250+
================================================================================
251+
PAI CORE - USER/COMMUNICATIONSTYLE.md
252+
================================================================================
253+
254+
PURPOSE:
255+
Defines the communication style calibration applied during PAI installation.
256+
These patterns override the AI's default (American-centric) communication norms
257+
to better match ${principalName}'s cultural communication context.
258+
259+
CUSTOMIZATION:
260+
- [x] Selected during PAI installation — edit freely at any time
261+
- PAI will never overwrite this file on upgrade
262+
263+
RELATED FILES:
264+
- PAI/USER/AISTEERINGRULES.md — additional behavioral rules appended during install
265+
- PAI/USER/DAIDENTITY.md — AI personality configuration
266+
- settings.json → daidentity.personality — numeric trait values
267+
268+
LAST UPDATED: ${new Date().toISOString().split("T")[0]}
269+
VERSION: 4.0.3
270+
================================================================================
271+
-->
272+
273+
# Communication Style: ${profile.label}
274+
275+
> ${profile.description}
276+
277+
**Cultural context:** ${profile.culturalContext}
278+
279+
Edit this file freely — it is a living document and PAI will never overwrite it.
280+
281+
---
282+
283+
## Communication Patterns
284+
285+
**Silence:** ${profile.communicationPatterns.silenceHandling}
286+
287+
**Praise:** ${profile.communicationPatterns.praiseStyle}
288+
289+
**Problem solving:** ${profile.communicationPatterns.problemSolving}
290+
291+
**Relationship style:** ${profile.communicationPatterns.relationshipStyle}
292+
293+
**Context level:** ${profile.communicationPatterns.contextLevel}
294+
${rulesSection}
295+
---
296+
297+
## Applied Personality Trait Overrides
298+
299+
These values were written to \`settings.json → daidentity.personality\` during installation:
300+
301+
${traitLines}
302+
303+
To adjust, edit \`settings.json\` directly or re-run the installer.
304+
`;
305+
}

0 commit comments

Comments
 (0)