Initial import from garrytan/gstack@026751e (main snapshot via local relay)
Some checks failed
Workflow Lint / actionlint (push) Has been cancelled
Build CI Image / build (push) Has been cancelled
Skill Docs Freshness / check-freshness (push) Has been cancelled
Periodic Evals / build-image (push) Has been cancelled
Periodic Evals / evals (map[file:test/codex-e2e.test.ts name:e2e-codex]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/gemini-e2e.test.ts name:e2e-gemini]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-design.test.ts name:e2e-design]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-plan.test.ts name:e2e-plan]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-bugs.test.ts name:e2e-qa-bugs]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-workflow.test.ts name:e2e-qa-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-review.test.ts name:e2e-review]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-workflow.test.ts name:e2e-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-routing-e2e.test.ts name:e2e-routing]) (push) Has been cancelled
Some checks failed
Workflow Lint / actionlint (push) Has been cancelled
Build CI Image / build (push) Has been cancelled
Skill Docs Freshness / check-freshness (push) Has been cancelled
Periodic Evals / build-image (push) Has been cancelled
Periodic Evals / evals (map[file:test/codex-e2e.test.ts name:e2e-codex]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/gemini-e2e.test.ts name:e2e-gemini]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-design.test.ts name:e2e-design]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-plan.test.ts name:e2e-plan]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-bugs.test.ts name:e2e-qa-bugs]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-qa-workflow.test.ts name:e2e-qa-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-review.test.ts name:e2e-review]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-e2e-workflow.test.ts name:e2e-workflow]) (push) Has been cancelled
Periodic Evals / evals (map[file:test/skill-routing-e2e.test.ts name:e2e-routing]) (push) Has been cancelled
Source: https://github.com/garrytan/gstack/commit/026751e
This commit is contained in:
186
scripts/archetypes.ts
Normal file
186
scripts/archetypes.ts
Normal file
@@ -0,0 +1,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);
|
||||
}
|
||||
Reference in New Issue
Block a user