/**
 * @fileoverview Classify · pure dry-run disposition helper.
 *
 * Honest, in-memory PREVIEW of how a source's EXISTING extracted ContentItemTags
 * would be dispositioned under its current taxonomy mode + thresholds. This is
 * NOT a fresh LLM run — it replays the deterministic, post-resolution routing
 * portion of the server's autotagger so operators can see the effect of mode +
 * thresholds before committing a real run.
 *
 * This module is intentionally framework-free (no Angular, no DI) so it can be
 * unit-tested in isolation. The Angular dialog builds the `resolve` closure from
 * cached taxonomy metadata (Tags / TagSynonyms) and feeds rows in here.
 *
 * NOTE: the server also has a semantic/embedding match tier that is NOT
 * reproducible client-side. Borderline rows ('none' here) may resolve
 * differently in a real run; the UI labels this clearly.
 */
/**
 * Resolve a single input row to its disposition under the given config.
 *
 * Routing mirrors the server's tiered model (deterministic portion only):
 * 1. synonym/exact tier (or score ≥ matchThreshold) → auto-apply.
 * 2. fuzzy/near with suggestThreshold ≤ score < matchThreshold → route-to-inbox.
 * 3. no match (tier 'none') → governed by taxonomy mode:
 *    - constrained → route-to-inbox (novel tag goes to human review; we route to
 *      inbox rather than 'reject' so the operator can promote/discard it — this
 *      matches the server enqueuing a Tag Suggestion instead of silently dropping).
 *    - auto-grow / free-flow → create-new (would create the tag).
 */
function disposeRow(input, cfg, r) {
    // Tier 1 — exact/synonym match (or a numeric score that clears the match bar).
    if (r.tier === 'synonym' || r.tier === 'exact' || (r.score != null && r.score >= cfg.matchThreshold)) {
        return {
            tag: input.tag,
            matchedTag: r.tagName,
            score: r.score,
            disposition: 'auto-apply',
            reason: r.tier === 'synonym' ? 'synonym match' : 'exact/synonym match',
        };
    }
    // Tier 2 — fuzzy/near match inside the suggest band → human-in-the-loop.
    if (r.score != null && r.score >= cfg.suggestThreshold && r.score < cfg.matchThreshold) {
        return {
            tag: input.tag,
            matchedTag: r.tagName,
            score: r.score,
            disposition: 'route-to-inbox',
            reason: 'below match threshold',
        };
    }
    // Tier 3 — no usable match → governed by taxonomy mode.
    if (cfg.mode === 'constrained') {
        return {
            tag: input.tag,
            matchedTag: null,
            score: r.score,
            disposition: 'route-to-inbox',
            reason: 'constrained: novel tag → review',
        };
    }
    // auto-grow | free-flow
    return {
        tag: input.tag,
        matchedTag: null,
        score: r.score,
        disposition: 'create-new',
        reason: cfg.mode === 'auto-grow' ? 'auto-grow: would create tag' : 'free-flow: would create tag',
    };
}
/**
 * Preview the disposition of a batch of existing extracted tags under a config.
 * Pure and deterministic — given the same inputs + resolve function it always
 * returns the same rows in the same order.
 */
export function previewDispositions(input, cfg, resolve) {
    return input.map(row => disposeRow(row, cfg, resolve(row.tag)));
}
//# sourceMappingURL=classify.dryrun.js.map