export class FailureLoopDriver {
    ledger;
    deps;
    constructor(ledger, deps) {
        this.ledger = ledger;
        this.deps = deps;
    }
    now() { return this.deps.now ? this.deps.now() : Date.now(); }
    /**
     * IMPLEMENT step: for each newly-`discovered` insight, open a tracked Action +
     * a draft Initiative (needs-user) and move it to `acted-on` with a verification
     * window. Never mints a proposal (see class docs). Idempotent: an insight
     * already past `discovered` is skipped.
     */
    async actOnNewInsights() {
        const actedOn = [];
        for (const insight of this.ledger.listInsights({ status: 'discovered' })) {
            const slug = insight.identityKey.replace(/[^a-z0-9]+/gi, '-').toLowerCase().slice(0, 50);
            const initiativeId = `failure-insight-${slug}`;
            // 1) tracked Evolution Action (a to-do that can't be forgotten)
            this.deps.addAction({
                title: `Process improvement: ${insight.summary}`,
                description: `${insight.recommendation}\n\nEvidence: ${insight.supportingFailureIds.length} failures (${insight.distinctSessions} sessions / ${insight.distinctCauseCommits} cause-commits). Insight ${insight.id}.`,
                priority: 'medium',
                source: 'failure-learning-loop',
                tags: ['failure-learning', insight.targetCategory ?? 'unknown'],
            });
            // 2) draft Initiative in needs-user (a human approves making it real)
            await this.deps.createInitiative({
                id: initiativeId,
                title: `Process improvement: ${insight.targetCategory} failures`,
                description: `${insight.recommendation}\n\nDiscovered by the Failure-Learning Loop (insight ${insight.id}). Approve to turn into a tracked spec → build.`,
                phases: [{ name: 'Approve + spec the process change', status: 'pending' }],
                needsUser: true,
                needsUserReason: `Failure-Learning Loop recommends a process change (${insight.targetCategory}); human approval required before any change ships.`,
            });
            // 3) move to acted-on with a verification window
            const windowDays = this.deps.verifyWindowDays ?? 42;
            const start = new Date(this.now()).toISOString();
            const end = new Date(this.now() + windowDays * 86400_000).toISOString();
            const res = this.ledger.updateInsight(insight.id, { status: 'acted-on', actedOnVia: initiativeId, verifyWindowStart: start, verifyWindowEnd: end }, insight.version);
            if (res.ok)
                actedOn.push(res.record);
        }
        return { actedOn };
    }
    /**
     * VERIFY step: for each `acted-on` insight whose window has elapsed, decide
     * the (correlational) outcome. Drop below baseline → verified-effective;
     * no drop → verified-ineffective + reopen (capped → inconclusive); too little
     * post-window activity → insufficient-exposure (extend once → inconclusive).
     */
    runVerification() {
        const evaluated = [];
        const maxReopens = this.deps.maxReopens ?? 2;
        const minExposure = this.deps.minPostExposure ?? 1;
        for (const insight of this.ledger.listInsights({ status: 'acted-on' })) {
            if (!insight.verifyWindowEnd || this.now() < Date.parse(insight.verifyWindowEnd))
                continue;
            const category = insight.targetCategory;
            if (!category || !insight.verifyWindowStart)
                continue;
            // Count only NEW failures of this category after the fix shipped — exclude
            // the pre-fix baseline failures that motivated the insight (they sit at
            // ≈window-start, so including them would mask any genuine drop).
            const baselineIds = new Set(insight.supportingFailureIds);
            const postWindow = this.ledger
                .list({ category, sinceMs: Date.parse(insight.verifyWindowStart), limit: 1000 })
                .filter((r) => (r.attribution === 'automatic' || r.attribution === 'one-tap') && !baselineIds.has(r.id));
            const postCount = postWindow.length;
            const baseline = insight.baselineRate ?? insight.supportingFailureIds.length;
            let next;
            if (postCount < minExposure) {
                // Not enough post-change activity to conclude — extend once, then give up.
                if (insight.reopenCount >= 1) {
                    next = { status: 'inconclusive', verifiedOutcome: 'insufficient-exposure' };
                }
                else {
                    const end = new Date(this.now() + (this.deps.verifyWindowDays ?? 42) * 86400_000).toISOString();
                    next = { reopenCount: insight.reopenCount + 1, verifyWindowEnd: end };
                }
            }
            else if (postCount < baseline) {
                next = { status: 'verified-effective', verifiedOutcome: 'effective' };
            }
            else if (insight.reopenCount >= maxReopens) {
                next = { status: 'inconclusive', verifiedOutcome: 'ineffective' };
            }
            else {
                // Didn't drop — reopen for another window (capped).
                const end = new Date(this.now() + (this.deps.verifyWindowDays ?? 42) * 86400_000).toISOString();
                next = { status: 'acted-on', verifiedOutcome: 'ineffective', reopenCount: insight.reopenCount + 1, verifyWindowEnd: end };
            }
            const res = this.ledger.updateInsight(insight.id, next, insight.version);
            if (res.ok)
                evaluated.push(res.record);
        }
        return { evaluated };
    }
}
//# sourceMappingURL=FailureLoopDriver.js.map