This repository was archived by the owner on Jun 15, 2026. It is now read-only.
forked from garrytan/gstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patharchetypes.ts
More file actions
186 lines (178 loc) · 5.4 KB
/
Copy patharchetypes.ts
File metadata and controls
186 lines (178 loc) · 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/**
* Archetypes — one-word builder identities computed from dimension clusters.
*
* Used by future /plan-tune vibe and /plan-tune narrative commands (v2).
* v1 ships the definitions but doesn't wire them into user-facing output
* yet. This file exists so the archetype model is stable by the time v2
* narrative generation ships.
*
* Design
* ------
* Each archetype is a point or region in the 5-dimensional psychographic
* space. `distance()` computes L2 distance from a profile to the archetype
* center, scaled by the archetype's "tightness" (how close you have to be
* to match). The archetype with smallest distance is the user's match.
*
* When no archetype is within threshold, return 'Polymath' — a calibrated
* "doesn't fit the common patterns" label that's respectful rather than
* generic.
*/
import type { Dimension } from './psychographic-signals';
export interface Archetype {
/** Short vibe label — one or two words. */
name: string;
/** One-line description anchored in observable behavior. */
description: string;
/** Center point in the 5-dimensional space. */
center: Record<Dimension, number>;
/** Inverse-weighted radius. Smaller = tighter match needed. */
tightness: number;
}
export const ARCHETYPES: readonly Archetype[] = [
{
name: 'Cathedral Builder',
description: 'Boil the ocean. Architecture first. Ship the complete thing.',
center: {
scope_appetite: 0.85,
risk_tolerance: 0.55,
detail_preference: 0.5,
autonomy: 0.5,
architecture_care: 0.85,
},
tightness: 1.0,
},
{
name: 'Ship-It Pragmatist',
description: 'Small scope, fast iteration. Good enough is done.',
center: {
scope_appetite: 0.25,
risk_tolerance: 0.75,
detail_preference: 0.3,
autonomy: 0.65,
architecture_care: 0.4,
},
tightness: 1.0,
},
{
name: 'Deep Craft',
description: 'Every detail matters. Verbose explanations. Slow and considered.',
center: {
scope_appetite: 0.6,
risk_tolerance: 0.35,
detail_preference: 0.85,
autonomy: 0.35,
architecture_care: 0.85,
},
tightness: 1.0,
},
{
name: 'Taste Maker',
description: 'Decisions feel intuitive. Overrides recommendations when taste dictates.',
center: {
scope_appetite: 0.6,
risk_tolerance: 0.6,
detail_preference: 0.5,
autonomy: 0.4,
architecture_care: 0.7,
},
tightness: 0.9,
},
{
name: 'Solo Operator',
description: 'High autonomy. Delegate to the agent. Trust but verify.',
center: {
scope_appetite: 0.5,
risk_tolerance: 0.7,
detail_preference: 0.3,
autonomy: 0.85,
architecture_care: 0.55,
},
tightness: 0.9,
},
{
name: 'Consultant',
description: 'Hands-on. Wants to be consulted on everything. Verifies each step.',
center: {
scope_appetite: 0.5,
risk_tolerance: 0.3,
detail_preference: 0.7,
autonomy: 0.2,
architecture_care: 0.65,
},
tightness: 0.9,
},
{
name: 'Wedge Hunter',
description: 'Narrow scope aggressively. Find the smallest thing worth building.',
center: {
scope_appetite: 0.15,
risk_tolerance: 0.5,
detail_preference: 0.4,
autonomy: 0.55,
architecture_care: 0.6,
},
tightness: 0.85,
},
{
name: 'Builder-Coach',
description: 'Balanced steering. Makes room for the agent to propose and challenge.',
center: {
scope_appetite: 0.55,
risk_tolerance: 0.5,
detail_preference: 0.55,
autonomy: 0.55,
architecture_care: 0.6,
},
tightness: 0.75,
},
];
/**
* Fallback used when no archetype is close enough — meaning the user's
* dimension cluster genuinely doesn't match any named pattern.
*/
export const FALLBACK_ARCHETYPE: Archetype = {
name: 'Polymath',
description: "Your steering style doesn't fit a common archetype. That's a compliment.",
center: { scope_appetite: 0.5, risk_tolerance: 0.5, detail_preference: 0.5, autonomy: 0.5, architecture_care: 0.5 },
tightness: 0,
};
const DIMENSIONS: readonly Dimension[] = [
'scope_appetite',
'risk_tolerance',
'detail_preference',
'autonomy',
'architecture_care',
] as const;
function euclidean(a: Record<Dimension, number>, b: Record<Dimension, number>): number {
let sumSq = 0;
for (const d of DIMENSIONS) {
const diff = (a[d] ?? 0.5) - (b[d] ?? 0.5);
sumSq += diff * diff;
}
return Math.sqrt(sumSq);
}
/**
* Match a profile to its best archetype.
* Returns FALLBACK_ARCHETYPE if no defined archetype is within threshold.
*/
export function matchArchetype(dims: Record<Dimension, number>): Archetype {
let best: Archetype = FALLBACK_ARCHETYPE;
let bestScore = Infinity; // lower is better
// Threshold: if no archetype scores below this, return Polymath.
// Max possible distance in [0,1]^5 is sqrt(5) ≈ 2.236. 0.55 = ~half the space.
const THRESHOLD = 0.55;
for (const arch of ARCHETYPES) {
const dist = euclidean(dims, arch.center);
// Scale by tightness — tighter archetypes require smaller actual distance.
const scaled = dist / (arch.tightness || 1);
if (scaled < bestScore && scaled <= THRESHOLD) {
bestScore = scaled;
best = arch;
}
}
return best;
}
/** All archetype names, useful for tests and /plan-tune stats. */
export function getAllArchetypeNames(): string[] {
return ARCHETYPES.map((a) => a.name).concat(FALLBACK_ARCHETYPE.name);
}