The real-group profiles stay as a test, so a change that moves a pick fails. Co-authored-by: Cursor <cursoragent@cursor.com>
138 lines
3.1 KiB
TypeScript
138 lines
3.1 KiB
TypeScript
/** Deck engine types. Shape matches the 2 Nov 2026 deck package. */
|
|
|
|
export const DECK_IDS = [
|
|
"decisionApproaches",
|
|
"membership",
|
|
"conflictManagement",
|
|
"communication",
|
|
] as const;
|
|
export type DeckId = (typeof DECK_IDS)[number];
|
|
|
|
export const SIZE_BANDS = [
|
|
"oneMember",
|
|
"twoToFive",
|
|
"sixToTwelve",
|
|
"thirteenToOneHundred",
|
|
"oneHundredToOneHundredK",
|
|
] as const;
|
|
export type SizeBand = (typeof SIZE_BANDS)[number];
|
|
|
|
export const KINDS = [
|
|
"workersCoop",
|
|
"mutualAid",
|
|
"nonprofit",
|
|
"forProfit",
|
|
"openSource",
|
|
"dao",
|
|
] as const;
|
|
export type Kind = (typeof KINDS)[number];
|
|
|
|
export const SCALES = ["local", "regional", "national", "global"] as const;
|
|
export type Scale = (typeof SCALES)[number];
|
|
|
|
export const STAGES = [
|
|
"earlyStage",
|
|
"growthStage",
|
|
"established",
|
|
"enterprise",
|
|
] as const;
|
|
export type Stage = (typeof STAGES)[number];
|
|
|
|
export const TRAIT_KEYS = [
|
|
"network",
|
|
"nonvoters",
|
|
"employer",
|
|
"sponsor",
|
|
"records",
|
|
"risky",
|
|
] as const;
|
|
export type TraitKey = (typeof TRAIT_KEYS)[number];
|
|
|
|
export const DISPLAY_TIERS = ["frame", "safety", "fitted", "floor", "none"] as const;
|
|
export type DisplayTier = (typeof DISPLAY_TIERS)[number];
|
|
export type RankTier = Exclude<DisplayTier, "none"> | "answers";
|
|
|
|
export interface CardFacets {
|
|
size: Record<SizeBand, boolean>;
|
|
orgType: Record<Kind, boolean>;
|
|
scale: Record<Scale, boolean>;
|
|
maturity: Record<Stage, boolean>;
|
|
}
|
|
export type FacetFile = Record<string, CardFacets>;
|
|
|
|
export interface CardTraits {
|
|
beside?: TraitKey[];
|
|
besideJob?: string;
|
|
fits?: TraitKey[];
|
|
opens?: TraitKey[];
|
|
}
|
|
|
|
export interface CardEngineEntry {
|
|
job: string;
|
|
priority: number;
|
|
everyRuleHas?: boolean;
|
|
traits?: CardTraits;
|
|
}
|
|
|
|
export interface JobDefinition {
|
|
id: string;
|
|
label: string;
|
|
displayTier: DisplayTier;
|
|
rank?: number;
|
|
why: string;
|
|
}
|
|
|
|
export interface DeckEngine {
|
|
jobs: JobDefinition[];
|
|
addedOrder: string[];
|
|
cards: Record<string, CardEngineEntry>;
|
|
}
|
|
|
|
export interface JobsFile {
|
|
about: string;
|
|
version: string;
|
|
displayLimit: number;
|
|
maxAddedInSeven: number;
|
|
tierOrder: RankTier[];
|
|
traitKeys: TraitKey[];
|
|
sizeAnswers: Record<string, SizeBand>;
|
|
decks: Record<DeckId, DeckEngine>;
|
|
}
|
|
|
|
export interface GroupAnswers {
|
|
size?: string | null;
|
|
mainKind?: Kind | null;
|
|
otherKinds?: Kind[];
|
|
scale?: Scale[];
|
|
stage?: Stage | null;
|
|
traits?: Partial<Record<TraitKey, boolean>>;
|
|
}
|
|
|
|
export type MatchedFacet = "size" | "kind" | "scale" | "stage";
|
|
|
|
export type RecommendationReason =
|
|
| { kind: "fit"; matched: MatchedFacet[]; answer?: TraitKey }
|
|
| { kind: "opened"; matched: MatchedFacet[]; answer: TraitKey }
|
|
| { kind: "added"; answer: TraitKey };
|
|
|
|
export interface RecommendedCard {
|
|
id: string;
|
|
job: string;
|
|
showsWith: string;
|
|
tier: RankTier;
|
|
tag: "recommended" | "everyRuleHas";
|
|
reason: RecommendationReason;
|
|
alternative?: string;
|
|
score?: number;
|
|
decidedBy?: "only" | "score" | "otherKinds" | "rowOrder";
|
|
}
|
|
|
|
export interface DeckRecommendation {
|
|
deck: DeckId;
|
|
fallback: boolean;
|
|
recommended: RecommendedCard[];
|
|
shown: RecommendedCard[];
|
|
waiting: RecommendedCard[];
|
|
seeAll: string[];
|
|
}
|