/**
 * 门禁检查模块 — Phase 21: 模板卫生与用户项目知识消费验证。
 *
 * 检查脚手架模板是否包含未落地占位标记，
 * 验证用户可见模板不泄漏内部施工语义，
 * 验证项目知识可被消费、选中、冲突检测等。
 */
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { safeReadCached } from "./helpers.js";
import { errorMessage } from "../../core/helpers.js";
/**
 * 读取文件并按行拆分。
 */
function safeReadLinesCached(filePath, cache) {
    const content = safeReadCached(filePath, cache);
    return content ? content.split("\n") : [];
}
export async function check(ctx, hardFail, info) {
    const rootDir = ctx.rootDir;
    const scaffoldDir = path.join(rootDir, "templates", "scaffolds");
    const scaffoldFindings = [];
    function scanScaffolds(dir) {
        if (!fs.existsSync(dir))
            return;
        for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
            const full = path.join(dir, entry.name);
            if (entry.isDirectory()) {
                scanScaffolds(full);
                continue;
            }
            if (!entry.isFile())
                continue;
            const rel = path.relative(rootDir, full).replace(/\\/g, "/");
            const lines = safeReadLinesCached(full, ctx.readCache);
            for (let i = 0; i < lines.length; i++) {
                if (/\b(?:TODO|FIXME|HACK|XXX)\b/.test(lines[i])) {
                    scaffoldFindings.push(`${rel}:${i + 1}`);
                }
            }
        }
    }
    scanScaffolds(scaffoldDir);
    for (const finding of scaffoldFindings) {
        hardFail("TEMPLATE_SCAFFOLD_PLACEHOLDER_LEAK", `脚手架模板仍包含未落地占位标记: ${finding}`, [finding.split(":")[0]], "template-hygiene", "历史门禁只检查模板是否注册，不检查用户生成代码是否带 TODO/FIXME", "清理脚手架占位标记并补行为测试");
    }
    try {
        const contractMod = await import(path.join(rootDir, "dist", "engine", "templates", "template_asset_contract_registry.js"));
        const contracts = contractMod.listTemplateAssetContracts?.() ?? [];
        const forbidden = /\b(?:validate-batch|ReleaseFix|problem-\d+)\b/i;
        for (const contract of contracts) {
            if (!contract.user_visible)
                continue;
            if (!contract.path || !/\.(md|ya?ml|hbs|json)$/i.test(contract.path))
                continue;
            const abs = path.join(rootDir, contract.path);
            const content = safeReadCached(abs, ctx.readCache);
            if (!content || !forbidden.test(content))
                continue;
            hardFail("USER_VISIBLE_TEMPLATE_INTERNAL_TRACE", `用户可见模板泄漏内部施工语义: ${contract.path}`, [contract.path], "template-hygiene", "旧门禁只检查内置资产可见性，不检查用户可见模板内容是否混入内部批次/问题编号", "清理用户可见模板内部语义");
        }
    }
    catch (e) {
        hardFail("TEMPLATE_CONTRACT_AUDIT_FAILED", `无法执行用户可见模板内容审计: ${errorMessage(e)}`, ["src/engine/templates/template_asset_contract_registry.ts"], "template-hygiene", "模板内容卫生必须可运行审计", "修复模板合同注册表或编译产物");
    }
    try {
        const obs = await import(path.join(rootDir, "dist", "engine", "contracts", "code_maintainability_observability_contract.js"));
        const ordinaryApiImplementationTriggers = obs.requiresCodeObservabilityContract?.("实现业务端业务办理列表接口", "code_change") === true;
        if (!ordinaryApiImplementationTriggers) {
            hardFail("CODE_OBSERVABILITY_BUSINESS_API_NOT_TRIGGERED", "problem-68: 普通业务接口实现未触发代码注释与日志契约，真实项目会绕过问题六十八", ["src/engine/contracts/code_maintainability_observability_contract.ts", "src/server/tools/index.ts"], "problem-68", "旧行为只覆盖支付/安全等关键词，未覆盖普通 Controller/API/Service 编码", "扩展触发条件并在 sf_expand 聚合返回工作包");
        }
    }
    catch (e) {
        hardFail("CODE_OBSERVABILITY_TRIGGER_AUDIT_FAILED", `问题六十八触发审计执行失败: ${errorMessage(e)}`, ["src/engine/contracts/code_maintainability_observability_contract.ts"], "problem-68", "触发判断必须可运行", "修复编译或导出");
    }
    const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "soloforge-project-knowledge-"));
    try {
        const goodDir = path.join(tmp, ".soloforge", "knowledge", "rules");
        fs.mkdirSync(goodDir, { recursive: true });
        fs.writeFileSync(path.join(goodDir, "API响应规则.md"), `---
id: demo-api-response-rule
asset_kind: hard_rule
routes:
  - code_change
  - artifact_generation
primary_triggers:
  - API
  - 接口
  - 响应
tech_stack:
  - spring
enforcement:
  level: hard_rule
---
# API 响应规则

必须使用项目统一响应结构，错误码、字段表和 OpenAPI 保持一致。
`, "utf-8");
        const projectKnowledge = await import(path.join(rootDir, "dist", "engine", "contracts", "project_knowledge_contract.js"));
        const lifecycleKnowledge = await import(path.join(rootDir, "dist", "engine", "contracts", "lifecycle_knowledge_contract.js"));
        const report = projectKnowledge.auditProjectKnowledge(tmp);
        if (report.hard_fail_count !== 0 || report.injectable < 1) {
            hardFail("PROJECT_KNOWLEDGE_RULE_NOT_CONSUMABLE", "用户项目 .soloforge/knowledge hard rule 未被识别为可消费规则", ["src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "旧链路只同步/索引内置模板，未证明用户项目自定义规则可被承载消费", "修复项目知识合同审计与选择逻辑");
        }
        const selected = projectKnowledge.selectProjectKnowledgeForTaskWithReasons(report, {
            intent: "实现业务端 API 接口统一响应",
            route: "code_change",
            tech_stack: ["spring"],
        });
        if (!selected.selected.some((decision) => decision.asset.id === "demo-api-response-rule")) {
            hardFail("PROJECT_KNOWLEDGE_RULE_NOT_SELECTED", "用户项目自定义 hard rule 未按意图/路由/技术栈被选中", ["src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "索引存在不等于任务会消费", "补齐 selectProjectKnowledgeForTask 行为和门禁");
        }
        const selection = projectKnowledge.selectProjectKnowledgeForTaskWithReasons(report, {
            intent: "实现业务端 API 接口统一响应 PHASE-001 SLICE-001 DD-A001 AC-001",
            route: "code_change",
            tech_stack: ["spring"],
            lifecycle_stage: "implementation",
            changed_files: ["backend/web/src/main/java/com/example/web/tenant/TenantUserController.java"],
            traceability_ids: ["PHASE-001", "SLICE-001", "DD-A001", "AC-001"],
        });
        const selectedDecision = selection.selected.find((decision) => decision.asset.id === "demo-api-response-rule");
        if (!selectedDecision || !selectedDecision.reason_zh || !Array.isArray(selectedDecision.matched_signals)) {
            hardFail("PROJECT_KNOWLEDGE_SELECTION_REASON_MISSING", "用户项目规则选择缺少 why selected / matched_signals 结构化解释", ["src/engine/contracts/project_knowledge_contract.ts", "src/engine/pipeline/intent_expander.ts"], "project-knowledge-consumption", "用户不知道为什么规则被选中就无法判断必须确认和修复路径", "让项目知识选择返回 SelectionDecision 而不是裸资产");
        }
        const unifiedDecision = lifecycleKnowledge.evaluateLifecycleKnowledgeDecision({
            projectPath: tmp,
            consumer: "sf_expand",
            intent: "实现业务端 API 接口统一响应 PHASE-001 SLICE-001 DD-A001 AC-001",
            route: "code_change",
            lifecycle_stage: "implementation",
            changed_files: ["backend/web/src/main/java/com/example/web/tenant/TenantUserController.java"],
            verified_files: ["backend/web/src/main/java/com/example/web/tenant/TenantUserController.java"],
            traceability_ids: ["PHASE-001", "SLICE-001", "DD-A001", "AC-001"],
            tech_stack: ["spring"],
            generation_traces: [
                { tool_name: "sf_classify", status: "passed", evidence_kind: "result" },
                { tool_name: "sf_expand", status: "passed", evidence_kind: "result" },
            ],
            record_trace: true,
        });
        if (unifiedDecision.contract_id !== "lifecycle_knowledge_contract/v1" ||
            !unifiedDecision.knowledge_selection.selected.some((decision) => decision.asset.id === "demo-api-response-rule") ||
            !unifiedDecision.authoritative_paths.traceability_matrix ||
            !Array.isArray(unifiedDecision.recovery_commands)) {
            hardFail("LIFECYCLE_KNOWLEDGE_DECISION_BUNDLE_INVALID", "统一生命周期知识决策包未同时携带权威路径、SelectionDecision、恢复命令和消费 trace", ["src/engine/contracts/lifecycle_knowledge_contract.ts", "src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "next/expand/verify/deliver 如果不消费同一决策包，会再次产生局部口径", "修复 evaluateLifecycleKnowledgeDecision 返回结构和入口消费");
        }
        const expander = await import(path.join(rootDir, "dist", "engine", "pipeline", "intent_expander.js"));
        const expandResult = await expander.expand({
            intent: "实现业务端 API 接口统一响应",
            classification: {
                task_type: "feature",
                risk: "low",
                complexity: "low",
                ambiguity: "low",
                affected_repos: ["backend"],
                mode: "autonomous",
                strategy: "full_pipeline",
            },
            projectPath: tmp,
            config: {
                name: "release-gate-project-knowledge",
                tech_stack: {
                    backend: { lang: "java", framework: "spring-boot", version: "3" },
                    frontend: { lang: "typescript", framework: "react", version: "18" },
                },
                product_profile: "saas",
                repos: [{ name: "backend", path: "backend", lang: "java", framework: "spring-boot", scope: ["backend/src/"] }],
                build_commands: {
                    backend: { build: "mvn compile", test: "mvn test", full: "mvn verify" },
                    frontend: { build: "npm run build", test: "npm test", full: "npm test" },
                },
                scope: { backend: ["backend/src/"], frontend: ["src/"] },
                _projectPath: tmp,
            },
            knowledgeIndex: { query: () => [], markUsed: () => { } },
            route_decision: {
                route: "code_change",
                execution_shape: "code_execution",
                mutation_allowed: true,
                input_materials: [],
                missing_required_inputs: [],
                scope: { read_only: false },
                constraints: [],
                language_policy: { primary: "zh" },
                confidence: 0.9,
                evidence: [],
                rejected_routes: [],
                conflicts: [],
                decision_version: 1,
            },
        });
        if (!expandResult.project_knowledge?.selected?.some((asset) => asset.id === "demo-api-response-rule")
            || !String(expandResult.prompt).includes("## 用户项目规则")
            || !String(expandResult.prompt).includes("选中原因")
            || !expandResult.project_knowledge?.selection_decisions?.some((decision) => decision.id === "demo-api-response-rule" && decision.reason_zh)) {
            hardFail("PROJECT_KNOWLEDGE_NOT_IN_EXPAND_MAINPATH", "用户项目规则未进入 sf_expand 真实 prompt 和结构化结果", ["src/engine/pipeline/intent_expander.ts", "src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "审计/选择通过不等于 MCP 主链路会消费", "将项目规则接入 sf_expand 并补主链路测试");
        }
        const nextPlanner = await import(path.join(rootDir, "dist", "engine", "workflow", "next_action_planner.js"));
        const nextPlan = await nextPlanner.planNextAction(tmp);
        if (!nextPlan.project_knowledge_context || nextPlan.project_knowledge_context.total < 1) {
            hardFail("PROJECT_KNOWLEDGE_NOT_IN_NEXT", "soloforge next 未携带项目知识上下文，用户无法知道下一步会应用哪些项目规则", ["src/engine/workflow/next_action_planner.ts", "src/bin/soloforge.ts"], "project-knowledge-consumption", "旧导航只看阶段，不看项目规则", "将项目知识上下文接入 next JSON 和人类输出");
        }
        if (nextPlan.project_knowledge_context?.selected?.length
            && !nextPlan.project_knowledge_context.selected.some((item) => item.reason_zh && item.enforcement)) {
            hardFail("PROJECT_KNOWLEDGE_NEXT_REASON_MISSING", "soloforge next 项目规则上下文缺少选中原因或强制级别", ["src/engine/workflow/next_action_planner.ts", "src/bin/soloforge.ts"], "project-knowledge-consumption", "next 只列规则名不能支撑用户确认哪些必须满足", "在 next 中展示 SelectionDecision 的 reason/enforcement/verification");
        }
        const badTmp = fs.mkdtempSync(path.join(os.tmpdir(), "soloforge-project-knowledge-bad-"));
        try {
            const badDir = path.join(badTmp, ".soloforge", "knowledge", "rules");
            fs.mkdirSync(badDir, { recursive: true });
            fs.writeFileSync(path.join(badDir, "未路由强规则.md"), `---
id: unrouted-hard-rule
asset_kind: hard_rule
enforcement:
  level: hard_rule
---
# 未路由强规则

必须使用统一响应。
`, "utf-8");
            const badReport = projectKnowledge.auditProjectKnowledge(badTmp);
            if (badReport.hard_fail_count === 0) {
                hardFail("PROJECT_KNOWLEDGE_UNROUTED_RULE_FALSE_PASS", "用户项目 hard rule 缺少 routes/primary_triggers 时错误放行", ["src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "规则文件存在不等于可稳定消费", "对未路由 hard rule fail-closed");
            }
        }
        finally {
            fs.rmSync(badTmp, { recursive: true, force: true });
        }
        const conflictTmp = fs.mkdtempSync(path.join(os.tmpdir(), "soloforge-project-knowledge-conflict-"));
        try {
            const conflictDir = path.join(conflictTmp, ".soloforge", "knowledge", "rules");
            fs.mkdirSync(conflictDir, { recursive: true });
            fs.writeFileSync(path.join(conflictDir, "响应A.md"), `---
id: response-a
asset_kind: hard_rule
routes: [code_change]
primary_triggers: [API]
conflict_group: api_response_format
rule_value: ApiResponse
enforcement:
  level: hard_rule
---
# 响应 A

必须使用 ApiResponse。
`, "utf-8");
            fs.writeFileSync(path.join(conflictDir, "响应B.md"), `---
id: response-b
asset_kind: hard_rule
routes: [code_change]
primary_triggers: [API]
conflict_group: api_response_format
rule_value: Result
enforcement:
  level: hard_rule
---
# 响应 B

必须使用 Result。
`, "utf-8");
            const conflictReport = projectKnowledge.auditProjectKnowledge(conflictTmp);
            if (!conflictReport.assets.some((asset) => asset.findings.some((finding) => finding.code === "PROJECT_KNOWLEDGE_RULE_CONFLICT"))) {
                hardFail("PROJECT_KNOWLEDGE_CONFLICT_FALSE_PASS", "项目 API 响应等规则冲突未进入用户确认", ["src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "旧项目知识审计只看是否可选中，不看项目规则之间互斥", "补冲突组/显式 conflicts_with 检测");
            }
        }
        finally {
            fs.rmSync(conflictTmp, { recursive: true, force: true });
        }
        const semanticConflictTmp = fs.mkdtempSync(path.join(os.tmpdir(), "soloforge-project-knowledge-semantic-conflict-"));
        try {
            const semanticDir = path.join(semanticConflictTmp, ".soloforge", "knowledge", "rules");
            fs.mkdirSync(semanticDir, { recursive: true });
            fs.writeFileSync(path.join(semanticDir, "响应ApiResponse.md"), `---
id: response-api-response
asset_kind: hard_rule
routes: [code_change]
primary_triggers: [API响应]
enforcement:
  level: hard_rule
---
# API 响应

必须使用 ApiResponse。
`, "utf-8");
            fs.writeFileSync(path.join(semanticDir, "响应Result.md"), `---
id: response-result
asset_kind: hard_rule
routes: [code_change]
primary_triggers: [接口响应]
enforcement:
  level: hard_rule
---
# 接口响应

必须使用 Result。
`, "utf-8");
            const semanticConflictReport = projectKnowledge.auditProjectKnowledge(semanticConflictTmp);
            if (!semanticConflictReport.assets.some((asset) => asset.findings.some((finding) => finding.code === "PROJECT_KNOWLEDGE_RULE_CONFLICT"))) {
                hardFail("PROJECT_KNOWLEDGE_SEMANTIC_CONFLICT_FALSE_PASS", "API 响应格式语义冲突依赖手写 conflict_group，未自动进入用户确认", ["src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "项目规则冲突不能要求用户预先正确标注才能发现", "补 API 响应/分页/日志/DB/权限语义冲突检测");
            }
        }
        finally {
            fs.rmSync(semanticConflictTmp, { recursive: true, force: true });
        }
    }
    catch (e) {
        hardFail("PROJECT_KNOWLEDGE_AUDIT_FAILED", `用户项目知识消费行为审计执行失败: ${errorMessage(e)}`, ["src/engine/contracts/project_knowledge_contract.ts"], "project-knowledge-consumption", "项目知识消费必须有可执行行为门禁", "修复项目知识合同模块和编译产物");
    }
    finally {
        fs.rmSync(tmp, { recursive: true, force: true });
    }
    info?.("  模板卫生与项目知识消费验证完成");
}
//# sourceMappingURL=checkTemplateKnowledgeHygiene.js.map