/**
 * Nexus Learning Network
 *
 * Turns run evidence into measured behavior change. Memory is the evidence
 * layer; learning is the promoted policy that changes the next run.
 */
export const LEARNING_PACKET_FILE = 'learning-packet.json';
export const LEARNING_SIGNALS_FILE = 'learning-signals.jsonl';
export const LEARNING_POLICY_DELTAS_FILE = 'learning-policy-deltas.json';
export const LEARNING_FEEDBACK_FILE = 'learning-feedback.jsonl';
export const LEARNING_NETWORK_BLUEPRINT = {
    axiom: 'Memory is stored evidence. Learning is a verified change in future behavior.',
    podModule: {
        name: 'Learning POD',
        expansion: 'Proof, Outcome, Decision',
        purpose: 'Compile run evidence into project, persona, stack, style, routing, and verification policies that workers can actually use.',
        members: [
            'learning-compiler',
            'context-router',
            'decision-spine-miner',
            'user-feedback-miner',
            'verification-scorer',
            'worker-policy-writer',
        ],
        outputContract: [
            'learning_packet',
            'project_profile_delta',
            'persona_profile_delta',
            'stack_style_delta',
            'worker_instruction_delta',
            'context_router_rule',
            'verification_gate_delta',
        ],
    },
    signalSources: [
        'memory',
        'decision_spine',
        'llm_output',
        'user_feedback',
        'worker_output',
        'file_change',
        'verification',
        'pod_signal',
        'context_router',
    ],
    domains: [
        'persona',
        'project',
        'stack',
        'coding_style',
        'quality_bar',
        'tooling',
        'routing',
        'verification',
    ],
    loop: [
        'observe run evidence',
        'extract candidate lessons',
        'score by evidence, recency, repetition, acceptance, and contradiction risk',
        'compile a learning packet',
        'route the packet into workers, context, model selection, and verification',
        'measure next-run outcome',
        'promote, decay, or quarantine the lesson',
    ],
    qualityMetrics: [
        'user acceptance rate',
        'repeat-rejection rate',
        'verification pass rate',
        'time-to-correct-diff',
        'context recall hit rate',
        'worker reuse rate',
        'model-route fit rate',
    ],
    routingOutputs: [
        'which memories to retrieve',
        'which worker or crew gets the task',
        'which model tier should handle each segment',
        'which files deserve context first',
        'which style rules must be active',
        'which tests prove the change',
    ],
    guardrails: [
        'Never claim quality improvement without a metric or user-confirmed acceptance signal.',
        'Never promote raw memory directly into policy; compile and score it first.',
        'Keep rejected user feedback as first-class evidence, not noise.',
        'Keep secrets and raw prompts out of durable learning artifacts.',
        'Decay stale lessons when project structure, stack, or user preference changes.',
    ],
};
export const LEARNING_NETWORK_PROMPT = `You are Nexus Prime Learning Network, the system that turns evidence into better future work.

Core axiom: memory is stored evidence; learning is a verified behavior change that improves the next run.

Build a Learning POD: Proof, Outcome, Decision.
The POD reads memories, decision-spine events, LLM outputs, worker outputs, file changes, verification results, context-router choices, and user accept/reject feedback. It does not hoard facts. It compiles lessons.

For every run:
1. Observe the evidence trail: task goal, selected context, model route, hired workers, decisions made, files changed, commands run, tests passed or failed, user feedback, and memories created.
2. Extract candidate lessons across persona, project, stack, coding style, quality bar, tooling, routing, and verification.
3. Score each lesson by evidence strength, recency, repetition, acceptance, contradiction risk, and blast radius.
4. Promote only the lessons that can change future behavior: retrieval rules, worker instructions, model-routing policy, file-prior hints, verification gates, coding-style constraints, and user-preference deltas.
5. Quarantine weak, conflicting, stale, secret-bearing, or one-off observations.
6. On the next task, route the active learning packet into context selection, worker hiring, model choice, prompts, tests, and dashboard explanation.
7. Measure whether output quality actually improved: fewer repeated fixes, fewer user rejections, faster verified diffs, better context recall, higher worker reuse, and clearer model-route decisions.

Output a compact learning packet with:
- lesson: the behavior change
- evidence: memory IDs, run IDs, file paths, decision IDs, or feedback events
- scope: persona, project, stack, coding style, routing, or verification
- confidence: 0 to 1
- action: promote, test, decay, or quarantine
- next_run_effect: exactly what workers, context router, model router, or verifier should do differently

Do not summarize memory as learning. Do not claim improvement without proof. Make the next run visibly smarter.`;
export function buildLearningNetworkPrompt(input = {}) {
    const context = [
        input.projectName ? `Project: ${input.projectName}` : null,
        input.persona ? `Persona: ${input.persona}` : null,
        input.currentGoal ? `Current goal: ${input.currentGoal}` : null,
        input.stack?.length ? `Stack: ${input.stack.join(', ')}` : null,
        input.codingStyle?.length ? `Coding style: ${input.codingStyle.join(', ')}` : null,
        input.evidenceRefs?.length ? `Evidence refs: ${input.evidenceRefs.join(', ')}` : null,
    ].filter(Boolean);
    return context.length
        ? `${LEARNING_NETWORK_PROMPT}\n\nCurrent learning context:\n${context.join('\n')}`
        : LEARNING_NETWORK_PROMPT;
}
export function scoreLearningSignal(signal) {
    const evidenceWeight = Math.min(0.18, Math.max(0, signal.evidenceRefs.length - 1) * 0.03);
    const acceptanceWeight = signal.accepted ? 0.14 : 0;
    const rejectionPenalty = signal.rejected ? 0.22 : 0;
    return Math.max(0, Math.min(1, signal.confidence + evidenceWeight + acceptanceWeight - rejectionPenalty));
}
export function buildLearningPodTags(signal) {
    return Array.from(new Set([
        '#learning-network',
        '#pod:learning',
        `#source:${signal.source}`,
        `#domain:${signal.domain}`,
        signal.runId ? `#run:${signal.runId}` : null,
        ...(signal.filePaths ?? []).slice(0, 6).map(path => `#file:${path}`),
        ...(signal.tags ?? []),
    ].filter((tag) => Boolean(tag))));
}
function shortHash(input) {
    let hash = 2166136261;
    for (let index = 0; index < input.length; index += 1) {
        hash ^= input.charCodeAt(index);
        hash = Math.imul(hash, 16777619);
    }
    return (hash >>> 0).toString(36);
}
function uniq(values) {
    return Array.from(new Set(values.filter((value) => Boolean(value && value.trim()))));
}
function asArray(value) {
    return Array.isArray(value) ? value : [];
}
function changedFilesOf(selectionPlan, contextLog = [], workerResults = []) {
    return uniq([
        ...asArray(selectionPlan?.selected?.changedFiles),
        ...asArray(selectionPlan?.contextRouter?.changedFiles),
        ...contextLog.flatMap((entry) => asArray(entry?.fileRefs)),
        ...workerResults.flatMap((result) => asArray(result?.changedFiles)),
    ]);
}
function evidenceRefsFor(runId, suffixes) {
    return suffixes.map((suffix) => `${runId}:${suffix}`);
}
function makeSignal(input) {
    const id = `ls_${shortHash([
        input.runId,
        input.source,
        input.domain,
        input.summary,
        input.evidenceRefs.join('|'),
    ].join(':'))}`;
    return {
        ...input,
        id,
        confidence: Math.max(0, Math.min(1, input.confidence)),
        tags: buildLearningPodTags(input),
    };
}
function packetFromSignal(signal, action, nextRunEffect, createdAt) {
    const confidence = scoreLearningSignal(signal);
    const id = `lp_${shortHash([signal.id, signal.summary, action].join(':'))}`;
    return {
        id,
        runId: signal.runId ?? 'unknown-run',
        lesson: signal.summary,
        evidence: signal.evidenceRefs,
        evidenceRefs: signal.evidenceRefs,
        scope: signal.domain,
        confidence,
        action,
        nextRunEffect,
        sourceSignals: [signal.id],
        createdAt,
    };
}
export function deriveLearningSignals(evidence) {
    const runId = evidence.runId;
    const selectionPlan = evidence.selectionPlan ?? {};
    const contextLog = asArray(evidence.contextLog);
    const decisionLog = asArray(evidence.decisionLog);
    const workerResults = asArray(evidence.workerResults);
    const verificationResults = asArray(evidence.verificationResults);
    const feedback = asArray(evidence.userFeedback);
    const selected = selectionPlan.selected ?? {};
    const router = selectionPlan.contextRouter ?? {};
    const modelRoute = selectionPlan.modelRoute ?? evidence.requestBrief?.modelPolicy ?? {};
    const changedFiles = changedFilesOf(selectionPlan, contextLog, workerResults);
    const signals = [];
    if (router.status || asArray(selected.files).length || changedFiles.length) {
        signals.push(makeSignal({
            source: 'context_router',
            domain: 'routing',
            summary: 'Use the decision-spine context router as the next-run retrieval policy for similar work.',
            evidenceRefs: evidenceRefsFor(runId, [
                'selection-plan.contextRouter',
                'context-log',
                changedFiles.length ? 'changed-files' : 'selected-files',
            ]),
            confidence: changedFiles.length ? 0.84 : 0.72,
            runId,
            filePaths: changedFiles.length ? changedFiles : asArray(selected.files),
            accepted: true,
        }));
    }
    if (modelRoute.workerTier || modelRoute.selectedModel || asArray(modelRoute.providerChoices).length) {
        signals.push(makeSignal({
            source: 'decision_spine',
            domain: 'routing',
            summary: 'Preserve model-router decisions with tier, provider, and context-fit rationale for future routing.',
            evidenceRefs: evidenceRefsFor(runId, ['selection-plan.modelRoute', 'request-brief.modelPolicy']),
            confidence: 0.82,
            runId,
            tags: ['#model-router'],
            accepted: true,
        }));
    }
    const selectedWorkers = [
        ...asArray(selected.crews),
        ...asArray(selected.specialists),
        ...asArray(selected.workers),
    ];
    if (selectedWorkers.length || workerResults.length) {
        signals.push(makeSignal({
            source: 'worker_output',
            domain: 'project',
            summary: 'Reuse worker and crew selection evidence instead of rehiring unrelated specialists on similar tasks.',
            evidenceRefs: evidenceRefsFor(runId, ['selection-plan.selected', 'worker-results']),
            confidence: workerResults.some((result) => result?.verified) ? 0.86 : 0.74,
            runId,
            tags: selectedWorkers.slice(0, 8),
            accepted: true,
        }));
    }
    const verifyCommands = asArray(selected.verifyCommands ?? router.verifyCommands);
    if (verifyCommands.length || verificationResults.length) {
        const passed = verificationResults.filter((result) => result?.passed || result?.status === 'passed' || result?.ok).length;
        const total = verificationResults.length;
        signals.push(makeSignal({
            source: 'verification',
            domain: 'verification',
            summary: 'Carry forward verification gates that proved the change, including focused tests and browser/API smoke when relevant.',
            evidenceRefs: evidenceRefsFor(runId, ['verification-results', 'selection-plan.verifyCommands']),
            confidence: total ? 0.72 + (passed / Math.max(total, 1)) * 0.18 : 0.76,
            runId,
            tags: verifyCommands.slice(0, 6),
            accepted: total ? passed === total : true,
        }));
    }
    if (changedFiles.length) {
        signals.push(makeSignal({
            source: 'file_change',
            domain: 'project',
            summary: 'Treat changed files as file-prior hints for follow-up work in the same product area.',
            evidenceRefs: evidenceRefsFor(runId, ['changed-files', 'decision-log.change']),
            confidence: 0.78,
            runId,
            filePaths: changedFiles,
            accepted: true,
        }));
    }
    const failed = evidence.state === 'failed' || decisionLog.some((entry) => entry?.verb === 'blocked');
    if (failed) {
        signals.push(makeSignal({
            source: 'decision_spine',
            domain: 'quality_bar',
            summary: 'Keep blocked or failed runs as cautionary learning until a future run verifies the recovery path.',
            evidenceRefs: evidenceRefsFor(runId, ['decision-log.blocked', 'run.state']),
            confidence: 0.7,
            runId,
            rejected: true,
            tags: ['#failure-learning'],
        }));
    }
    if (!signals.length && (evidence.goal || decisionLog.length || evidence.requestBrief)) {
        signals.push(makeSignal({
            source: 'decision_spine',
            domain: 'project',
            summary: 'Preserve the run goal and outcome as provisional learning until stronger evidence appears.',
            evidenceRefs: evidenceRefsFor(runId, ['run.goal', 'decision-log']),
            confidence: 0.62,
            runId,
            tags: ['#provisional-learning'],
        }));
    }
    for (const item of feedback) {
        const rating = item.rating;
        const rejected = rating === 'rejected' || rating === 'ignore';
        signals.push(makeSignal({
            source: 'user_feedback',
            domain: rating === 'rejected' ? 'quality_bar' : 'persona',
            summary: item.note?.trim()
                ? `User feedback should change future behavior: ${item.note.trim()}`
                : rating === 'rejected'
                    ? 'User rejection must become a first-class learning signal, not a hidden memory note.'
                    : 'User acceptance should promote the underlying run policy for similar future work.',
            evidenceRefs: uniq([`${runId}:learning-feedback`, item.packetId ? `${runId}:packet:${item.packetId}` : null]),
            confidence: rejected ? 0.82 : 0.88,
            runId,
            accepted: !rejected,
            rejected,
            tags: [`#feedback:${rating}`],
        }));
    }
    return signals.sort((left, right) => scoreLearningSignal(right) - scoreLearningSignal(left));
}
function nextRunEffect(signal) {
    if (signal.source === 'context_router')
        return 'Prime context retrieval with the selected files, changed files, and decision-spine route before hiring workers.';
    if (signal.source === 'decision_spine' && signal.tags?.includes('#model-router'))
        return 'Send model-router hints to the orchestrator so Codex, Claude, Cursor, and local agents receive the right tier and context window.';
    if (signal.source === 'worker_output')
        return 'Bias worker hiring toward the crew/specialists that produced verified output for this task shape.';
    if (signal.source === 'verification')
        return 'Attach the proven verification gate to similar future work and require dashboard evidence when UI surfaces change.';
    if (signal.source === 'file_change')
        return 'Raise these files in file-prior ranking for follow-up tasks in the same product surface.';
    if (signal.source === 'user_feedback')
        return 'Inject this user feedback into persona, prompt, worker, and verifier policies before the next run.';
    return 'Route the lesson into context selection, worker instructions, and verification for the next similar run.';
}
function actionFor(signal) {
    const score = scoreLearningSignal(signal);
    if (signal.rejected)
        return score >= 0.78 ? 'test' : 'quarantine';
    if (score >= 0.82 && signal.accepted)
        return 'promote';
    if (score >= 0.64)
        return 'test';
    return 'quarantine';
}
export function buildLearningPackets(signals, createdAt = Date.now()) {
    return signals
        .map((signal) => packetFromSignal(signal, actionFor(signal), nextRunEffect(signal), createdAt))
        .sort((left, right) => right.confidence - left.confidence)
        .slice(0, 12);
}
export function buildLearningPolicyDeltas(packets) {
    return packets
        .filter((packet) => packet.action === 'promote' || packet.action === 'test')
        .map((packet) => {
        const target = packet.scope === 'routing' ? 'context_router'
            : packet.scope === 'verification' ? 'verifier'
                : packet.scope === 'persona' ? 'prompt'
                    : packet.scope === 'project' ? 'worker_router'
                        : 'dashboard';
        return {
            id: `ld_${shortHash([packet.id, target].join(':'))}`,
            packetId: packet.id,
            scope: packet.scope,
            target,
            rule: packet.nextRunEffect,
            confidence: packet.confidence,
            evidenceRefs: packet.evidenceRefs,
        };
    });
}
export function buildLearningQuality(evidence, packets) {
    const verification = asArray(evidence.verificationResults);
    const passed = verification.filter((result) => result?.passed || result?.status === 'passed' || result?.ok).length;
    const feedback = asArray(evidence.userFeedback);
    const changedFiles = changedFilesOf(evidence.selectionPlan, asArray(evidence.contextLog), asArray(evidence.workerResults));
    return {
        packetCount: packets.length,
        promotedCount: packets.filter((packet) => packet.action === 'promote').length,
        testCount: packets.filter((packet) => packet.action === 'test').length,
        quarantinedCount: packets.filter((packet) => packet.action === 'quarantine').length,
        verificationPassRate: verification.length ? passed / verification.length : null,
        acceptedFeedback: feedback.filter((item) => item.rating === 'accepted' || item.rating === 'useful').length,
        rejectedFeedback: feedback.filter((item) => item.rating === 'rejected' || item.rating === 'ignore').length,
        changedFileCount: changedFiles.length,
        workerReuseSignals: packets.filter((packet) => packet.scope === 'project').length,
        modelRouteSignals: packets.filter((packet) => packet.nextRunEffect.toLowerCase().includes('model-router')).length,
    };
}
export function buildLearningGraph(envelopes) {
    const nodes = new Map();
    const edges = [];
    for (const envelope of envelopes) {
        const runNode = `run:${envelope.runId}`;
        nodes.set(runNode, { id: runNode, label: envelope.runId, type: 'run' });
        for (const packet of envelope.packets) {
            const packetNode = `packet:${packet.id}`;
            nodes.set(packetNode, {
                id: packetNode,
                label: packet.lesson,
                type: 'packet',
                scope: packet.scope,
                confidence: packet.confidence,
            });
            edges.push({ source: runNode, target: packetNode, type: 'generated' });
            for (const ref of packet.evidenceRefs.slice(0, 8)) {
                const refNode = `evidence:${ref}`;
                nodes.set(refNode, { id: refNode, label: ref, type: 'evidence' });
                edges.push({ source: refNode, target: packetNode, type: 'supported-by' });
            }
            for (const delta of envelope.policyDeltas.filter((entry) => entry.packetId === packet.id)) {
                const policyNode = `policy:${delta.id}`;
                nodes.set(policyNode, { id: policyNode, label: delta.target, type: 'policy', scope: delta.scope, confidence: delta.confidence });
                edges.push({ source: packetNode, target: policyNode, type: 'changes' });
            }
        }
    }
    return { nodes: [...nodes.values()], edges };
}
export function buildLearningNetworkEnvelope(evidence) {
    const generatedAt = Date.now();
    const signals = deriveLearningSignals(evidence);
    const packets = buildLearningPackets(signals, generatedAt);
    const policyDeltas = buildLearningPolicyDeltas(packets);
    const envelope = {
        runId: evidence.runId,
        generatedAt,
        promptVersion: 'learning-network-pod-v1',
        axiom: LEARNING_NETWORK_BLUEPRINT.axiom,
        packets,
        signals,
        policyDeltas,
        quality: buildLearningQuality(evidence, packets),
        graph: { nodes: [], edges: [] },
    };
    envelope.graph = buildLearningGraph([envelope]);
    return envelope;
}
export function learningSignalsToJsonl(signals) {
    return signals.map((signal) => JSON.stringify(signal)).join('\n') + (signals.length ? '\n' : '');
}
