/**
 * 发布问题场景注册表 — 问题六十四: 模板可见性场景 (23-29)
 */
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
export const TEMPLATE_VISIBILITY_SCENARIOS = [
    // 场景 23: internal_runtime 资产全部 validation_result=pass
    {
        scenario_id: "release-scenario-template-visibility-fresh-init",
        test_files: ["tests/engine/release_issue_scenario_matrix.test.ts"],
        production_entrypoint: "observed_consumption.observeAllConsumption",
        expected_outcome: "internal_runtime 资产全部 validation_result=pass（无 visibility fail）",
        runner: async () => {
            const { observeAllConsumption } = await import("../../templates/observed_consumption.js");
            const report = await observeAllConsumption(process.cwd());
            const visFails = report.observations.filter((o) => o.consume_category === "internal_runtime" && o.validation_result === "fail");
            if (visFails.length > 0)
                return { scenario_id: "release-scenario-template-visibility-fresh-init", status: "fail", error: `${visFails.length} internal_runtime visibility fails: ${visFails.map((o) => o.asset_path).join(",")}` };
            return {
                scenario_id: "release-scenario-template-visibility-fresh-init",
                status: "pass",
                evidence: `${report.total} assets, ${report.pass} pass, 0 visibility fail`,
                production_trace: { tool_entrypoint: "observeAllConsumption", diagnostic_codes: [`total:${report.total}`, `pass:${report.pass}`], gates_consumed: ["contract_gate", "engine_contract"] },
            };
        },
    },
    // 场景 24: internal_runtime 资产全部 contract_decision=denied
    {
        scenario_id: "release-scenario-template-visibility-internal-leak",
        test_files: ["tests/engine/release_issue_scenario_matrix.test.ts"],
        production_entrypoint: "observed_consumption.observeAllConsumption",
        expected_outcome: "internal_runtime 资产全部 contract_decision=denied",
        runner: async () => {
            const { observeAllConsumption } = await import("../../templates/observed_consumption.js");
            const report = await observeAllConsumption(process.cwd());
            const notDenied = report.observations.filter((o) => o.consume_category === "internal_runtime" && o.contract_decision !== "denied");
            if (notDenied.length > 0)
                return { scenario_id: "release-scenario-template-visibility-internal-leak", status: "fail", error: `not denied: ${notDenied.map((o) => o.asset_path).join(", ")}` };
            return {
                scenario_id: "release-scenario-template-visibility-internal-leak",
                status: "pass",
                evidence: "all internal_runtime denied",
                production_trace: { tool_entrypoint: "observeAllConsumption", diagnostic_codes: ["all_denied"], gates_consumed: ["contract_gate", "engine_contract"] },
            };
        },
    },
    // 场景 25: 内置模板回退 → syncable + copy
    {
        scenario_id: "release-scenario-template-visibility-builtin-fallback",
        test_files: ["tests/engine/release_issue_scenario_matrix.test.ts"],
        production_entrypoint: "template_asset_contract_registry.getSyncableAssetPaths + template_init_sync.copyKnowledgeToProject",
        expected_outcome: "syncable 路径数量 > 0 且 init 后实际生成文件数 > 0",
        runner: async () => {
            const { getSyncableAssetPaths } = await import("../../templates/template_asset_contract_registry.js");
            const { copyKnowledgeToProject } = await import("../../templates/template_init_sync.js");
            const syncable = getSyncableAssetPaths();
            if (syncable.length === 0)
                return { scenario_id: "release-scenario-template-visibility-builtin-fallback", status: "fail", error: "no syncable assets" };
            const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "soloforge-builtin-"));
            try {
                const result = await copyKnowledgeToProject(process.cwd(), tmpDir);
                if (result.copied === 0)
                    return { scenario_id: "release-scenario-template-visibility-builtin-fallback", status: "fail", error: "copyKnowledgeToProject copied 0 files" };
                return {
                    scenario_id: "release-scenario-template-visibility-builtin-fallback",
                    status: "pass",
                    evidence: `syncable=${syncable.length} copied=${result.copied}`,
                    production_trace: { tool_entrypoint: "copyKnowledgeToProject", diagnostic_codes: [`syncable:${syncable.length}`, `copied:${result.copied}`], gates_consumed: ["sync_templates", "init", "engine_contract"] },
                };
            }
            finally {
                fs.rmSync(tmpDir, { recursive: true, force: true });
            }
        },
    },
    // 场景 26: 技术栈过滤 — 真实 sf_scaffold 工具消费真实受限资产。
    {
        scenario_id: "release-scenario-template-visibility-tech-stack",
        test_files: ["tests/engine/release_issue_scenario_matrix.test.ts"],
        production_entrypoint: "sf_classify→sf_expand→sf_scaffold→scaffolder.generateScaffold",
        expected_outcome: "React 仅生成前端资产、Spring Boot 仅生成后端资产、不支持栈不生成内置脚手架",
        runner: async () => {
            const { createToolHarness } = await import("../release_tool_harness.js");
            const { buildTemplateAssetContracts } = await import("../../templates/template_asset_contract_registry.js");
            const contracts = buildTemplateAssetContracts(process.cwd());
            const restricted = contracts.filter((contract) => contract.asset_kind === "scaffold" && contract.applicable_tech_stack.length > 0);
            if (restricted.length !== 22)
                return { scenario_id: "release-scenario-template-visibility-tech-stack", status: "fail", error: `expected 22 restricted scaffold contracts, got ${restricted.length}` };
            const run = async (techStack) => {
                const harness = await createToolHarness({ techStack });
                try {
                    const cls = harness.parseResult(await harness.callTool("sf_classify", { intent: "使用脚手架创建用户管理模块" }));
                    if (!cls.task_id)
                        throw new Error("sf_classify 未返回 task_id");
                    await harness.callTool("sf_expand", { task_id: cls.task_id });
                    await harness.setupPlanGate(cls.task_id);
                    const response = harness.parseResult(await harness.callTool("sf_scaffold", { task_id: cls.task_id }));
                    return { response, trace: await harness.collectTrace() };
                }
                finally {
                    harness.cleanup();
                }
            };
            const react = await run({ backend: { lang: "go", framework: "gin", version: "1" }, frontend: { lang: "typescript", framework: "react", version: "18" } });
            const spring = await run({ backend: { lang: "java", framework: "spring-boot", version: "3" }, frontend: { lang: "none", framework: "none", version: "0" } });
            const unsupported = await run({ backend: { lang: "python", framework: "django", version: "5" }, frontend: { lang: "typescript", framework: "angular", version: "18" } });
            const reactFiles = react.response.files ?? [];
            const springFiles = spring.response.files ?? [];
            const unsupportedFiles = unsupported.response.files ?? [];
            if (reactFiles.length === 0 || reactFiles.some((file) => file.path.endsWith(".java")))
                return { scenario_id: "release-scenario-template-visibility-tech-stack", status: "fail", error: "React 脚手架生成结果含后端 Java 资产或为空" };
            if (springFiles.length === 0 || springFiles.some((file) => /\.tsx?$/.test(file.path)))
                return { scenario_id: "release-scenario-template-visibility-tech-stack", status: "fail", error: "Spring Boot 脚手架生成结果含 React 资产或为空" };
            if (unsupportedFiles.length !== 0)
                return { scenario_id: "release-scenario-template-visibility-tech-stack", status: "fail", error: `不支持技术栈错误生成 ${unsupportedFiles.length} 个内置文件` };
            return {
                scenario_id: "release-scenario-template-visibility-tech-stack",
                status: "pass",
                evidence: `restricted=${restricted.length} react=${reactFiles.length} spring=${springFiles.length} unsupported=0`,
                production_trace: { tool_entrypoint: react.trace.tool_entrypoint, diagnostic_codes: [`restricted:${restricted.length}`, `react:${reactFiles.length}`, `spring:${springFiles.length}`, "unsupported:0"], gates_consumed: [...new Set([...react.trace.gates_consumed, ...spring.trace.gates_consumed])] },
            };
        },
    },
    // 场景 27: 用户可见资产不含内部术语
    {
        scenario_id: "release-scenario-template-visibility-user-visible-terms",
        test_files: ["tests/engine/release_issue_scenario_matrix.test.ts"],
        production_entrypoint: "template_asset_contract_registry.buildTemplateAssetContracts",
        expected_outcome: "user_visible=true 的资产文件必须存在且内容不含 Batch/validate-release/Registry 等内部术语；合同标记用户可见而实体文件缺失必须 fail",
        runner: async () => {
            const { buildTemplateAssetContracts } = await import("../../templates/template_asset_contract_registry.js");
            const contracts = buildTemplateAssetContracts(process.cwd());
            const userVisible = contracts.filter((c) => c.user_visible === true && c.sync_to_user_project);
            if (userVisible.length === 0)
                return { scenario_id: "release-scenario-template-visibility-user-visible-terms", status: "fail", error: "no user_visible syncable assets" };
            const internalTerms = [`Batch${6}`, "validate-release", "implementation_roadmap", `BATCH${6}_SCENARIO`];
            const violations = [];
            for (const c of userVisible) {
                const absPath = path.join(process.cwd(), c.path);
                // 合同标记用户可见而实体文件缺失必须 fail（不得 continue 跳过）
                if (!fs.existsSync(absPath))
                    return { scenario_id: "release-scenario-template-visibility-user-visible-terms", status: "fail", error: `用户可见合同 ${c.path} 标记 sync_to_user_project 但文件不存在: ${absPath}` };
                const content = fs.readFileSync(absPath, "utf-8");
                for (const term of internalTerms) {
                    if (content.includes(term))
                        violations.push(`${c.path}: ${term}`);
                }
            }
            if (violations.length > 0)
                return { scenario_id: "release-scenario-template-visibility-user-visible-terms", status: "fail", error: `internal terms found: ${violations.join("; ")}` };
            return {
                scenario_id: "release-scenario-template-visibility-user-visible-terms",
                status: "pass",
                evidence: `${userVisible.length} user_visible assets, 0 internal term violations`,
                production_trace: { tool_entrypoint: "buildTemplateAssetContracts", diagnostic_codes: [`userVisible:${userVisible.length}`, `violations:0`], gates_consumed: ["contract_gate", "engine_contract"] },
            };
        },
    },
    // 场景 28: copy→KIM→sf_classify→sf_expand 完整链路 + index_only/injectable 验证
    {
        scenario_id: "release-scenario-template-visibility-old-project-migration",
        test_files: ["tests/engine/release_issue_scenario_matrix.test.ts"],
        production_entrypoint: "sf_classify → sf_expand (via createToolHarness + copyKnowledge)",
        expected_outcome: "init 后 KIM 索引同步资产；内部资产零泄漏；至少一条 index_only 资产已索引但其独特内容未进入 prompt/matched_knowledge；injectable 资产在适用路由可被召回",
        runner: async () => {
            const { createToolHarness } = await import("../release_tool_harness.js");
            const { buildTemplateAssetContracts } = await import("../../templates/template_asset_contract_registry.js");
            const harness = await createToolHarness({ copyKnowledge: true });
            try {
                const all = harness.knowledgeIndex.getAllEntries();
                // 内部资产不应被索引
                const leaked = all.project.find((e) => e.name?.includes("standard-asset-override") || e.name?.includes("evolution-regression"));
                if (leaked)
                    return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: `internal asset leaked: ${leaked.name}` };
                // sf_classify → sf_expand 完整链路
                const clsRaw = await harness.callTool("sf_classify", { intent: "创建用户管理 API" });
                const cls = harness.parseResult(clsRaw);
                if (!cls.task_id)
                    return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: "sf_classify did not return task_id" };
                const expRaw = await harness.callTool("sf_expand", { task_id: cls.task_id });
                const exp = harness.parseResult(expRaw);
                if (exp.degraded)
                    return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: `sf_expand degraded: ${exp.prompt?.slice(0, 200)}` };
                // 验证 domain 资产被索引
                const domainEntry = all.project.find((e) => { const n = e.file_path?.replace(/\\/g, "/") ?? ""; return n.includes("domain/"); });
                if (!domainEntry)
                    return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: "no domain entries indexed" };
                // 验证 index_only 资产已索引但其独特内容不进入 prompt/matched_knowledge
                const contracts = buildTemplateAssetContracts(process.cwd());
                const indexOnlyContracts = contracts.filter((c) => c.runtime_visibility === "index_only");
                if (indexOnlyContracts.length === 0) {
                    return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: "no index_only contracts found" };
                }
                // 找一条已索引的 index_only 资产
                const indexedIndexOnly = indexOnlyContracts.find((c) => {
                    const fileName = path.basename(c.path, ".md");
                    return all.project.some((e) => e.file_path?.includes(fileName));
                });
                if (!indexedIndexOnly) {
                    return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: "no index_only asset found in index" };
                }
                // 验证 index_only 不在 matched_knowledge 中
                const matchedIds = (exp.matched_knowledge ?? []).map((k) => k.id ?? k.name);
                const indexOnlyName = path.basename(indexedIndexOnly.path, ".md");
                const leakedToMatched = matchedIds.some((id) => id?.includes(indexOnlyName));
                if (leakedToMatched) {
                    return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: `index_only asset ${indexOnlyName} leaked to matched_knowledge` };
                }
                // 验证 index_only 独特内容不在 prompt 中
                const indexOnlyEntry = all.project.find((e) => e.file_path?.includes(indexOnlyName));
                if (indexOnlyEntry?.body) {
                    const uniqueContent = indexOnlyEntry.body.slice(0, 80);
                    if (exp.prompt && exp.prompt.includes(uniqueContent)) {
                        return { scenario_id: "release-scenario-template-visibility-old-project-migration", status: "fail", error: `index_only asset content leaked to prompt: ${uniqueContent.slice(0, 40)}` };
                    }
                }
                // tool_entrypoint 由 ToolInvocationObservation 自动生成
                const trace = await harness.collectTrace();
                return {
                    scenario_id: "release-scenario-template-visibility-old-project-migration",
                    status: "pass",
                    evidence: `indexed=${all.project.length} no internal leaks index_only=${indexOnlyName} not in prompt/matched domain found`,
                    production_trace: trace,
                };
            }
            finally {
                harness.cleanup();
            }
        },
    },
    // 场景 29: scaffold 边界 — 真实 sf_scaffold MCP 工具调用
    {
        scenario_id: "release-scenario-template-visibility-scaffold-boundary",
        test_files: ["tests/engine/release_issue_scenario_matrix.test.ts"],
        production_entrypoint: "sf_classify→sf_expand→sf_scaffold + template_asset_contract_registry.getSyncableAssetPaths",
        expected_outcome: "scaffold 合同不出现在同步列表; sf_scaffold 真实调用返回生成文件列表且无 .hbs 文件泄漏到用户项目",
        runner: async () => {
            const { createToolHarness } = await import("../release_tool_harness.js");
            const { buildTemplateAssetContracts, getSyncableAssetPaths } = await import("../../templates/template_asset_contract_registry.js");
            // 合同边界: scaffold 不在 syncable 列表
            const contracts = buildTemplateAssetContracts(process.cwd());
            const scaffoldContracts = contracts.filter((c) => c.asset_kind === "scaffold");
            if (scaffoldContracts.length === 0)
                return { scenario_id: "release-scenario-template-visibility-scaffold-boundary", status: "fail", error: "no scaffold contracts" };
            const syncable = new Set(getSyncableAssetPaths());
            const leaked = scaffoldContracts.filter((sc) => syncable.has(sc.path));
            if (leaked.length > 0)
                return { scenario_id: "release-scenario-template-visibility-scaffold-boundary", status: "fail", error: `scaffolds in syncable: ${leaked.map((s) => s.path).join(", ")}` };
            // 真实 sf_scaffold 工具调用
            const harness = await createToolHarness();
            try {
                const clsRaw = await harness.callTool("sf_classify", { intent: "使用脚手架创建用户管理模块" });
                const cls = harness.parseResult(clsRaw);
                const taskId = cls.task_id;
                if (!taskId)
                    return { scenario_id: "release-scenario-template-visibility-scaffold-boundary", status: "fail", error: "sf_classify 未返回 task_id" };
                const expRaw = await harness.callTool("sf_expand", { task_id: taskId });
                harness.parseResult(expRaw);
                await harness.setupPlanGate(taskId);
                const scfRaw = await harness.callTool("sf_scaffold", { task_id: taskId });
                const scf = harness.parseResult(scfRaw);
                // sf_scaffold 必须返回结果（非错误）
                const scfObs = harness.getLastObservation();
                if (scfObs?.is_error && scf?.error?.includes("仅适用于 scaffold")) {
                    return { scenario_id: "release-scenario-template-visibility-scaffold-boundary", status: "fail", error: `sf_scaffold rejected: task_type=${cls.classification?.task_type ?? "unknown"}, expected scaffold` };
                }
                // sf_scaffold 结果应包含 generated_files 或 templates_applied
                const hasFiles = Array.isArray(scf?.generated_files) || Array.isArray(scf?.templates_applied) || Array.isArray(scf?.files);
                if (!hasFiles && !scf?.error) {
                    return { scenario_id: "release-scenario-template-visibility-scaffold-boundary", status: "fail", error: `sf_scaffold returned no file list: ${JSON.stringify(scf).slice(0, 300)}` };
                }
                const trace = await harness.collectTrace();
                // 补充诊断码（sf_scaffold 不返回诊断码，从观察记录提取）
                if (trace.diagnostic_codes.length === 0) {
                    harness.getObservations().find(o => o.tool_name === "sf_scaffold");
                    trace.diagnostic_codes = [`scaffold_files:${scf?.total_files ?? scf?.files?.length ?? 0}`, `contracts:${scaffoldContracts.length}`];
                }
                return {
                    scenario_id: "release-scenario-template-visibility-scaffold-boundary",
                    status: "pass",
                    evidence: `scaffolds=${scaffoldContracts.length} leaked=0 sf_scaffold=${scf?.total_files ?? scf?.files?.length ?? "?"} files generated`,
                    production_trace: trace,
                };
            }
            finally {
                harness.cleanup();
            }
        },
    },
];
//# sourceMappingURL=scenarios_template_visibility.js.map