private void appendQualityMetrics()

in doc-architect/doc-architect-cli/src/main/java/com/docarchitect/cli/ScanCommand.java [518:601]


    private void appendQualityMetrics(StringBuilder sb, ArchitectureModel model) {
        var report = model.qualityReport();

        sb.append("## 📊 Scan Quality Report\n\n");

        // Overall coverage
        sb.append("### Overall Coverage\n\n");
        sb.append(String.format("- ✅ **%d / %d files analyzed** (%.1f%%)\n",
            report.filesAnalyzed(),
            report.totalFilesInProject(),
            report.getCoveragePercentage()));

        if (report.filesSkipped() > 0) {
            sb.append(String.format("- âš ī¸  **%d files skipped** (%.1f%%)\n",
                report.filesSkipped(),
                (double) report.filesSkipped() / report.totalFilesInProject() * 100));
        }
        sb.append("\n");

        // Coverage by component
        if (!report.coverageByComponent().isEmpty()) {
            sb.append("### Coverage by Architecture Component\n\n");
            sb.append("| Component | Expected | Scanned | Coverage |\n");
            sb.append("|-----------|----------|---------|----------|\n");

            report.coverageByComponent().forEach((type, metrics) -> {
                String status = metrics.isHighCoverage() ? "✅" :
                               (metrics.isLowCoverage() ? "âš ī¸" : "");
                sb.append(String.format("| %s | %d | %d | %.0f%% %s |\n",
                    metrics.componentType(),
                    metrics.expectedFiles(),
                    metrics.scannedFiles(),
                    metrics.coveragePercentage(),
                    status));
            });
            sb.append("\n");
        }

        // Confidence levels
        if (report.getTotalFindings() > 0) {
            sb.append("### Confidence Levels\n\n");
            int total = report.getTotalFindings();

            if (report.getHighConfidenceFindings() > 0) {
                double pct = (double) report.getHighConfidenceFindings() / total * 100;
                sb.append(String.format("- **HIGH (AST-based):** %d findings (%.0f%%)\n",
                    report.getHighConfidenceFindings(), pct));
            }
            if (report.getMediumConfidenceFindings() > 0) {
                double pct = (double) report.getMediumConfidenceFindings() / total * 100;
                sb.append(String.format("- **MEDIUM (Regex-based):** %d findings (%.0f%%)\n",
                    report.getMediumConfidenceFindings(), pct));
            }
            if (report.getLowConfidenceFindings() > 0) {
                double pct = (double) report.getLowConfidenceFindings() / total * 100;
                sb.append(String.format("- **LOW (Heuristic):** %d findings (%.0f%%)\n",
                    report.getLowConfidenceFindings(), pct));
            }
            sb.append("\n");
        }

        // Quality gaps
        if (report.hasGaps()) {
            sb.append("### âš ī¸ Known Gaps\n\n");

            var errors = report.getGapsBySeverity(com.docarchitect.core.model.GapSeverity.ERROR);
            var warnings = report.getGapsBySeverity(com.docarchitect.core.model.GapSeverity.WARNING);
            var infos = report.getGapsBySeverity(com.docarchitect.core.model.GapSeverity.INFO);

            if (!errors.isEmpty()) {
                errors.forEach(gap ->
                    sb.append(String.format("- ❌ **%s:** %s\n", gap.scannerId(), gap.message())));
            }
            if (!warnings.isEmpty()) {
                warnings.forEach(gap ->
                    sb.append(String.format("- âš ī¸  **%s:** %s\n", gap.scannerId(), gap.message())));
            }
            if (!infos.isEmpty()) {
                infos.forEach(gap ->
                    sb.append(String.format("- â„šī¸  **%s:** %s\n", gap.scannerId(), gap.message())));
            }
            sb.append("\n");
        }
    }