/**
 * 逐资产运行时消费证据 — 由真实生产入口函数执行产生 trace。
 *
 * 调用真实生产模块（template_init_sync、template_manifest_io、scaffolder、
 * intent_expander、claude_md、index_manager），收集 consumption_trace_store 中的 trace，
 * 验证每个注册资产都有对应 consume_category 要求的真实 consumer trace。
 *
 * mainline 资产: 按 mainline_consumer 字段映射到真实 trace consumer，
 * 而非统一使用 category 级别检查。
 *
 * 禁止：从合同声明字段直接复制 selected/pass。
 * 禁止：以"文件存在"或"export 存在"作为消费证据。
 * 禁止：自行遍历文件后伪写 consumer="verified_consumer" 的模拟证据。
 * 禁止：用 contract_gate 为未运行资产补"消费通过"逻辑。
 * 禁止：有任意 trace 即 pass 的宽松分支。
 */
import path from "node:path";
import fs from "node:fs";
import os from "node:os";
import { getConsumptionTraces, clearConsumptionTraces, getTraceCounts, } from "./consumption_trace_store.js";
import { buildTemplateAssetContracts, getContractDecision, clearContractCache, } from "./template_asset_contract_registry.js";
/**
 * mainline_consumer 字段 → 合法的真实 trace consumer（单精确匹配）。
 *
 * 每个 mainline 资产只能由对应入口函数产生的 trace 验证，
 * trace 必须携带 declared_consumer 与合同 mainline_consumer 精确对应。
 * 禁止跨入口替代：不得用 init/knowledge_index 代替专项消费者。
 */
const MAINLINE_CONSUMER_TRACE_MAP = {
    "soloforge.copyGlobalPatterns": "init",
    "intent_expander.loadBuiltinByPipeline": "expander",
    "intent_expander.queryKnowledgeForRoute": "expander",
    "claude_md.generateClaudeMd": "adapter_prompt",
    "scaffolder.generateScaffold": "scaffold",
    "knowledge_asset_consumer.consumeForRouter": "knowledge_index",
    "knowledge_config_loader.loadKnowledgeConfig": "knowledge_index",
    "knowledge_manager.addKnowledge": "knowledge_index",
    "template_mechanism_auditor": "knowledge_index",
    "code_reviewer.classifyFindingsByTrust": "knowledge_index",
};
/** consume_category → 必须由这些 consumer 之一产生真实 trace（非 mainline 类别） */
const REQUIRED_CONSUMERS = {
    copy_only: ["init", "sync_templates", "pipeline_sync"],
    copy_only_pipeline: [], // 管线模板由 manifest.yaml 跟踪，不需要 trace 匹配
    scaffold_only: ["scaffold"],
    internal_runtime: [], // 由合同门禁拒绝验证
    audit_only: [], // 由合同门禁拒绝验证
};
/**
 * 运行真实 init scenario: 调用共享模板同步模块。
 */
async function runRealInitScenario(rootDir, tmpDir) {
    const { copyKnowledgeToProject, copyPatternsToGlobal, copyPipelineTemplatesToProject } = await import("./template_init_sync.js");
    await copyKnowledgeToProject(rootDir, tmpDir);
    const globalDir = path.join(tmpDir, ".soloforge", "patterns");
    await copyPatternsToGlobal(rootDir, globalDir);
    // 管线模板同步（阶段5）— 未指定 systemType 时复制全部
    await copyPipelineTemplatesToProject(rootDir, tmpDir);
}
/**
 * 运行真实 sync-templates scenario: 调用 buildTemplateManifestWithStats。
 */
async function runRealSyncScenario(rootDir) {
    const { buildTemplateManifestWithStats } = await import("./template_manifest_io.js");
    const templatesDir = path.join(rootDir, "templates");
    if (!fs.existsSync(templatesDir))
        return;
    buildTemplateManifestWithStats(templatesDir, "0.0.0");
}
/**
 * 运行真实 scaffold scenario: 调用 generateScaffold。
 */
async function runRealScaffoldScenario(rootDir) {
    const { generateScaffold } = await import("./scaffolder.js");
    try {
        await generateScaffold({
            intent: "新建订单模块",
            classification: { task_type: "new_feature", risk: "low", complexity: "medium", ambiguity: "none", affected_repos: [], mode: "autonomous", strategy: "full_pipeline" },
            config: { tech_stack: { backend: { framework: "spring" }, frontend: { framework: "react" } } },
            projectPath: rootDir,
        });
    }
    catch {
        // scaffold 可能因缺少模板目录而失败，不影响 trace 收集
    }
}
/**
 * 运行真实 adapter prompt scenario: 调用 generateClaudeMd。
 * generateClaudeMd 内部调用 listConsumableAssetsByConsumer 并记录 adapter_prompt trace。
 */
async function runRealAdapterPromptScenario() {
    const { generateClaudeMd } = await import("../../adapters/claude_code/claude_md.js");
    const config = {
        name: "observe-scenario",
        tech_stack: {
            backend: { lang: "java", framework: "spring-boot", version: "3.x" },
            frontend: { lang: "ts", framework: "react", version: "18" },
        },
        product_profile: "saas",
        repos: [],
        build_commands: {
            backend: { build: "mvn compile", test: "mvn test", full: "mvn verify" },
            frontend: { build: "npm run build", test: "npm test", full: "npm run build && npm test" },
        },
        scope: { backend: ["src/"], frontend: ["src/"] },
    };
    generateClaudeMd(config);
}
/**
 * 运行真实 expander scenario: 调用 expand()。
 * expand 内部调用 listConsumableAssetsByConsumer("intent_expander") 并记录 expander trace。
 */
async function runRealExpanderScenario(_rootDir, tmpDir) {
    const { KnowledgeIndexManager } = await import("../../knowledge/index_manager.js");
    const { expand } = await import("../pipeline/intent_expander.js");
    const config = {
        name: "observe-scenario",
        tech_stack: {
            backend: { lang: "java", framework: "spring-boot", version: "3.x" },
            frontend: { lang: "ts", framework: "react", version: "18" },
        },
        product_profile: "saas",
        repos: [],
        build_commands: {
            backend: { build: "mvn compile", test: "mvn test", full: "mvn verify" },
            frontend: { build: "npm run build", test: "npm test", full: "npm run build && npm test" },
        },
        scope: { backend: ["src/"], frontend: ["src/"] },
        _projectPath: tmpDir,
    };
    const kim = new KnowledgeIndexManager(config, { watch: false });
    try {
        await kim.build();
        await expand({
            intent: "修复按钮文字拼写错误",
            classification: {
                task_type: "code_change",
                risk: "low",
                complexity: "low",
                ambiguity: "none",
                affected_repos: [],
                mode: "autonomous",
                strategy: "quick_fix",
            },
            projectPath: tmpDir,
            config,
            knowledgeIndex: kim,
        });
    }
    catch {
        // expand 可能因缺少完整上下文而失败，trace 在失败前已记录
    }
    finally {
        await kim.close();
    }
}
/**
 * 生成全量逐资产运行时消费观察报告。
 *
 * 运行真实生产入口函数，收集 trace，与权威合同注册表交叉验证。
 * mainline 资产按 mainline_consumer 映射到真实 trace consumer；
 * 其他类别按 consume_category 要求特定 consumer 的真实 trace。
 */
export async function observeAllConsumption(rootDir, _techStack) {
    clearContractCache();
    clearConsumptionTraces();
    const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "soloforge-obs-"));
    const contracts = buildTemplateAssetContracts(rootDir);
    try {
        await runRealInitScenario(rootDir, tmpDir);
        await runRealExpanderScenario(rootDir, tmpDir);
        // 这些入口放在最后，避免知识索引重建时覆盖同资产 trace。
        await runRealSyncScenario(rootDir);
        await runRealScaffoldScenario(rootDir);
        await runRealAdapterPromptScenario();
    }
    finally {
        fs.rmSync(tmpDir, { recursive: true, force: true });
    }
    // 收集所有 trace，按 asset_path 聚合
    const traces = getConsumptionTraces();
    const traceByPath = new Map();
    for (const t of traces) {
        if (t.asset_path) {
            const arr = traceByPath.get(t.asset_path) ?? [];
            arr.push(t);
            traceByPath.set(t.asset_path, arr);
        }
    }
    // 与权威合同交叉验证
    const observations = [];
    const failures = [];
    let passCount = 0;
    let failCount = 0;
    let syncedCount = 0;
    let indexedCount = 0;
    let injectedCount = 0;
    let blockedCount = 0;
    let skippedInternal = 0;
    let skippedUnregistered = 0;
    for (const contract of contracts) {
        const assetTraces = traceByPath.get(contract.path) ?? [];
        const rv = contract.runtime_visibility;
        // internal_runtime/audit_only: 必须验证合同门禁正确拒绝
        if (contract.consume_category === "internal_runtime" || contract.consume_category === "audit_only") {
            const decision = getContractDecision(contract.path);
            const correctlyDenied = decision.decision === "denied";
            if (!correctlyDenied) {
                failCount++;
                blockedCount++;
                const reason = `${contract.path}: ${contract.consume_category} 资产未被正确拒绝（decision=${decision.decision}）`;
                failures.push(reason);
                observations.push({
                    asset_path: contract.path,
                    contract_id: contract.asset_id,
                    consume_category: contract.consume_category,
                    runtime_visibility: rv,
                    entry_point: "contract_gate",
                    contract_decision: decision.decision,
                    trace_count: assetTraces.length,
                    validation_result: "fail",
                    failure_reason: reason,
                });
            }
            else {
                skippedInternal++;
                blockedCount++;
                observations.push({
                    asset_path: contract.path,
                    contract_id: contract.asset_id,
                    consume_category: contract.consume_category,
                    runtime_visibility: rv,
                    entry_point: "contract_gate",
                    contract_decision: "denied",
                    trace_count: assetTraces.length,
                    validation_result: "pass",
                });
            }
            continue;
        }
        // mainline: trace 的 declared_consumer 必须与合同 mainline_consumer 精确匹配
        if (contract.consume_category === "mainline") {
            const expectedConsumer = MAINLINE_CONSUMER_TRACE_MAP[contract.mainline_consumer];
            if (!expectedConsumer) {
                failCount++;
                const reason = `${contract.path}: mainline_consumer="${contract.mainline_consumer}" 无真实 trace consumer 映射`;
                failures.push(reason);
                observations.push({
                    asset_path: contract.path,
                    contract_id: contract.asset_id,
                    consume_category: contract.consume_category,
                    runtime_visibility: rv,
                    entry_point: "none",
                    contract_decision: "unregistered",
                    trace_count: assetTraces.length,
                    validation_result: "fail",
                    failure_reason: reason,
                });
                continue;
            }
            // 精确匹配: declared_consumer === mainline_consumer 且 consumer 类型正确
            const validTrace = assetTraces.find(t => t.consumer === expectedConsumer && t.declared_consumer === contract.mainline_consumer);
            if (!validTrace) {
                failCount++;
                const actualConsumers = assetTraces.map(t => `${t.consumer}(declared=${t.declared_consumer ?? "none"})`);
                const reason = `${contract.path}: 无精确消费 trace（mainline_consumer=${contract.mainline_consumer}，需 consumer=${expectedConsumer} declared_consumer=${contract.mainline_consumer}，实际 [${actualConsumers.join(", ")}]）`;
                failures.push(reason);
                observations.push({
                    asset_path: contract.path,
                    contract_id: contract.asset_id,
                    consume_category: contract.consume_category,
                    runtime_visibility: rv,
                    entry_point: actualConsumers[0] ?? "none",
                    contract_decision: "unregistered",
                    trace_count: assetTraces.length,
                    validation_result: "fail",
                    failure_reason: reason,
                });
                continue;
            }
            passCount++;
            // 按 runtime_visibility 分类计数
            if (rv === "injectable")
                injectedCount++;
            else if (rv === "index_only")
                indexedCount++;
            observations.push({
                asset_path: contract.path,
                contract_id: contract.asset_id,
                consume_category: contract.consume_category,
                runtime_visibility: rv,
                entry_point: validTrace.consumer,
                contract_decision: validTrace.contract_decision ?? "allowed",
                trace_count: assetTraces.length,
                validation_result: "pass",
            });
            continue;
        }
        // 其他类别: 按 REQUIRED_CONSUMERS 查找
        const requiredConsumers = REQUIRED_CONSUMERS[contract.consume_category];
        if (!requiredConsumers || requiredConsumers.length === 0) {
            passCount++;
            // 按 runtime_visibility 分类计数
            if (rv === "injectable")
                injectedCount++;
            else if (rv === "index_only")
                indexedCount++;
            else if (rv === "internal_only" || rv === "scaffold_only")
                blockedCount++;
            observations.push({
                asset_path: contract.path,
                contract_id: contract.asset_id,
                consume_category: contract.consume_category,
                runtime_visibility: rv,
                entry_point: "none_required",
                contract_decision: "allowed",
                trace_count: assetTraces.length,
                validation_result: "pass",
            });
            continue;
        }
        const validTrace = assetTraces.find(t => requiredConsumers.includes(t.consumer));
        if (!validTrace) {
            failCount++;
            const reason = `${contract.path}: 无合法消费 trace（consume_category=${contract.consume_category}，需 ${requiredConsumers.join("|")}，实际 consumer=[${assetTraces.map(t => t.consumer).join(",")}]）`;
            failures.push(reason);
            observations.push({
                asset_path: contract.path,
                contract_id: contract.asset_id,
                consume_category: contract.consume_category,
                runtime_visibility: rv,
                entry_point: assetTraces[0]?.consumer ?? "none",
                contract_decision: "unregistered",
                trace_count: assetTraces.length,
                validation_result: "fail",
                failure_reason: reason,
            });
            continue;
        }
        passCount++;
        // copy_only 资产由 init/sync 入口消费
        if (contract.consume_category === "copy_only")
            syncedCount++;
        else if (rv === "injectable")
            injectedCount++;
        else if (rv === "index_only")
            indexedCount++;
        observations.push({
            asset_path: contract.path,
            contract_id: contract.asset_id,
            consume_category: contract.consume_category,
            runtime_visibility: rv,
            entry_point: validTrace.consumer,
            contract_decision: validTrace.contract_decision ?? "allowed",
            trace_count: assetTraces.length,
            validation_result: "pass",
        });
    }
    // 检查未注册文件
    const registeredPaths = new Set(contracts.map(c => c.path));
    for (const [assetPath, tArr] of traceByPath) {
        if (!registeredPaths.has(assetPath) && tArr.some(t => t.contract_decision === "unregistered")) {
            skippedUnregistered++;
        }
    }
    return {
        total: observations.length,
        pass: passCount,
        fail: failCount,
        synced: syncedCount,
        indexed: indexedCount,
        injected: injectedCount,
        blocked: blockedCount,
        skipped_internal: skippedInternal,
        skipped_unregistered: skippedUnregistered,
        observations,
        failures,
        trace_counts: getTraceCounts(),
    };
}
//# sourceMappingURL=observed_consumption.js.map