/**
 * personas/tier-rubric.mjs — C48 D-05 tier-assignment rubric (prompt template).
 *
 * The task description is explicit: this is NOT a runtime LLM call. It is the
 * structured prompt template the apt:personas SKILL.md body injects into the
 * host LLM's classification step. The LLM applies the rubric; this module
 * owns the rubric text, the inputs it expects, and the output shape so the
 * SKILL.md body + the CLI command envelope stay in sync with a single source
 * of truth.
 *
 * Why not LLM-call-from-module-level: the framework must remain host-LLM-
 * agnostic (it runs under Claude Code, Codex, Gemini, OpenCode, …). The
 * LLM call is always orchestrated by the SKILL.md body (the host's built-in
 * LLM loop) — this module supplies the deterministic inputs.
 *
 * D-05 decision: "LLM heuristics with an explicit rubric (frequency of code
 * signal + strategic intent from PROJECT.md Target Audience + core-value
 * alignment)". The rubric below enumerates those three factors with
 * concrete weightings and the decision table the LLM follows.
 */
/**
 * @typedef {Object} RubricInputs
 * @property {string} productName             From product-context.json.
 * @property {string} productDescription      From product-context.json.
 * @property {string} targetAudience          From product-context.json / PROJECT.md Personas stub.
 * @property {string} coreValue               From PROJECT.md "Core Value" section.
 * @property {string[]} uniqueRoleStrings     From runRoleScan (role-scan.mjs).
 * @property {Array<{ file: string, line: number, matched: string, kind: string, confidence: string }>} roleFindings
 * @property {Array<{ id: string, name: string, description?: string }>} features   From product-context.json.
 */
/**
 * @typedef {Object} PersonaTierDraft
 * @property {string} name                    Persona handle (e.g. "Maya").
 * @property {string} archetype               Short label (e.g. "The Vibe Coder").
 * @property {'primary' | 'secondary' | 'tertiary'} tier
 * @property {string} rationale               1-2 sentences explaining WHY this tier.
 * @property {string[]} evidenceFiles         Subset of roleFindings.file showing codebase signal.
 */
/**
 * The rubric is injected into the SKILL.md's LLM prompt. It enumerates three
 * factors, how to weigh them, and the tier-decision table. Copy-paste friendly
 * — every line is plain markdown that an LLM can parse as bulleted guidance.
 *
 * @returns {string}
 */
export function buildTierRubricPrompt() {
    return [
        '## Tier Assignment Rubric (C48 D-05)',
        '',
        'Assign each persona one of three tiers. The three factors, in order:',
        '',
        '1. **Code signal frequency** — how many role-scan findings reference this',
        "   persona's role? Validator enums (v.literal / z.enum) count 2×; identifier",
        '   references (isAdmin, userRole) count 1×; route prefixes count 0.5×.',
        '',
        '2. **Strategic intent** — does PROJECT.md\'s "Target Audience" or "Core Value"',
        '   name this persona as the primary user? Explicit mention = primary signal.',
        '   Implicit support role (staff, admin, support agent) = secondary/tertiary',
        '   unless the product *is* internal tooling for that role.',
        '',
        '3. **Core-value alignment** — how central is this persona to the Core Value',
        '   sentence? A docs-site persona in a "we help developers ship" product is',
        '   tertiary even if they appear in code, because they are NOT the value lever.',
        '',
        '### Tier decision table',
        '',
        '- **primary** — strategic intent names them AND code signal is non-zero.',
        '  At least one but not more than two personas per project should be primary',
        '  unless the product is explicitly multi-sided (marketplace, 2-sided SaaS).',
        '- **secondary** — strategic intent or Core Value references them, code',
        '  signal is medium, and they change product decisions. Typical examples:',
        '  admin/staff personas in a product aimed at end-users; power users vs.',
        '  casual users.',
        '- **tertiary** — high code signal but low strategic intent (internal tooling',
        '  users, API-only consumers, occasional stakeholders). Still matters for',
        '  design decisions but does NOT drive roadmap priority.',
        '',
        '### Rationalizations to reject',
        '',
        '- "This persona has the most code so it\'s primary." Volume ≠ importance.',
        '  Internal admin tooling frequently dominates line-count in a product whose',
        '  real primary user is external.',
        '- "Let the user tier them manually." Defeats infer-first. Emit a draft with',
        '  your best tier + rationale and let the user override per-persona in the',
        '  confirmation step.',
        '- "All three tiers must be filled." No. A single-role product legitimately',
        '  has 1-2 personas and zero tertiary. Do not invent a tertiary to look',
        '  balanced — hitting the 3-5 count (D-08) is the bound, not a quota.',
        '',
    ].join('\n');
}
/**
 * Build the full prompt block the SKILL.md body hands to the host LLM. The
 * LLM consumes this + the role-scan findings + PROJECT.md and emits a JSON
 * array of `PersonaTierDraft` entries.
 *
 * Separated from buildTierRubricPrompt so the rubric itself is inspectable
 * without the surrounding input payload noise.
 *
 * @param {RubricInputs} inputs
 * @returns {string}
 */
export function buildTierPrompt(inputs) {
    const sampleFindings = inputs.roleFindings.slice(0, 30);
    const findingsBlock = sampleFindings.length === 0
        ? '(no role signals found — scan returned empty)'
        : sampleFindings
            .map((f) => `- ${f.file}:${f.line} [${f.kind}/${f.confidence}] matched: \`${f.matched}\``)
            .join('\n');
    const featuresBlock = inputs.features.length === 0
        ? '(no features declared)'
        : inputs.features
            .map((f) => `- ${f.name}${f.description ? `: ${f.description}` : ''}`)
            .join('\n');
    const rolesBlock = inputs.uniqueRoleStrings.length === 0
        ? '(no role strings detected)'
        : inputs.uniqueRoleStrings.map((r) => `- ${r}`).join('\n');
    return [
        '# Persona tier classification',
        '',
        'You are drafting 3-5 personas for this product. For each persona you draft,',
        'apply the rubric below to pick a tier. Return a JSON array of objects with',
        'keys: `name`, `archetype`, `tier`, `rationale`, `evidenceFiles`.',
        '',
        buildTierRubricPrompt(),
        '',
        '## Inputs',
        '',
        `**Product:** ${inputs.productName}`,
        `**Description:** ${inputs.productDescription}`,
        `**Target audience:** ${inputs.targetAudience || '(not specified)'}`,
        `**Core value:** ${inputs.coreValue || '(not specified)'}`,
        '',
        '### Declared features (PROJECT.md Scope.In)',
        featuresBlock,
        '',
        '### Detected role strings (from role-scan)',
        rolesBlock,
        '',
        '### Sample role-signal findings (up to 30 newest)',
        findingsBlock,
        '',
        '## Output format',
        '',
        '```json',
        '[',
        '  {',
        '    "name": "Maya",',
        '    "archetype": "The Vibe Coder",',
        '    "tier": "primary",',
        '    "rationale": "Named in PROJECT.md target-audience; high validator-enum signal for the `user` role; directly aligned with the product\'s Core Value.",',
        '    "evidenceFiles": ["packages/ui/src/sections/personas/data.ts", "apps/web/src/app/api/users/route.ts"]',
        '  }',
        ']',
        '```',
        '',
    ].join('\n');
}
/**
 * The list of inputs the SKILL.md body MUST populate before calling
 * buildTierPrompt. Useful for schema docs / CLI envelope manifests.
 */
export const REQUIRED_RUBRIC_INPUTS = Object.freeze([
    'productName',
    'productDescription',
    'targetAudience',
    'coreValue',
    'uniqueRoleStrings',
    'roleFindings',
    'features',
]);
//# sourceMappingURL=tier-rubric.mjs.map