Definition
A report is a structured output that aggregates, summarizes, and presents data for human interpretation and decision-making. Reports transform raw data (logs, metrics, scan results, test outputs) into actionable information by applying formatting, filtering, aggregation, and contextual explanation. They serve as the communication bridge between automated systems and human stakeholders.
In the Prismatic Platform, reports are generated at multiple levels: quality gate reports summarize codebase health, OSINT execution reports document intelligence findings, Perimeter reports present security ratings and compliance status, DD pipeline reports consolidate due diligence analysis, and session reports capture development activity for continuity across conversations. Reports are the primary output artifact of nearly every platform subsystem -- they are the "deliverable" that makes automated analysis useful.
The platform treats reports as first-class artifacts: they are versioned, immutably stored, and cross-referenced with their generating commands and input data. This auditability is critical for compliance-oriented use cases like Czech CER (Critical Entity Regulation) reporting and M&A due diligence where report provenance must be traceable.
Core Concepts
Report Taxonomy
The platform generates several distinct report categories, each serving different stakeholders:
| Report Category | Generated By | Audience | Format | Storage |
|---|---|---|---|---|
| Quality Gate | mix quality.gates | Developers | Markdown/JSON | .claude/reports/ |
| Health Score | mix health.score | CI/CD pipeline | JSON | stdout |
| Doctrine Compliance | mix check.doctrines | CI/CD + developers | Text/JSON | stdout |
| OSINT Execution | OSINT tool adapter | Analysts | JSON/HTML | ETS audit trail |
| DD Contradiction | DD analysis pipeline | Investigators | HTML | ~/Desktop/ |
| Security Rating | Perimeter EASM | Security team | JSON/PDF | Database |
| Session Debrief | /debrief command | Developer (next session) | Markdown | .claude/session-context/ |
| Regression Test | Bug fix workflow | Code reviewers | Markdown | Commit message |
| CER Compliance | /cer-report command | Compliance officers | PDF/HTML | Audit archive |
| M&A Due Diligence | /ma-report command | Deal team | HTML/PDF | Deal workspace |
| Promo Enhancement | mix promo.enhance | Content team | Markdown | stdout |
| Statistics Validation | mix prismatic.stats.validate | Platform maintainers | Text | stdout |
Report Pipeline Architecture
Every report follows the same four-stage pipeline:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ COLLECT │ -> │ AGGREGATE │ -> │ FORMAT │ -> │ DELIVER │
│ │ │ │ │ │ │ │
│ PostgreSQL │ │ Statistics │ │ Markdown │ │ File system │
│ ETS tables │ │ Trends │ │ JSON │ │ stdout │
│ Git history │ │ Comparisons │ │ HTML │ │ Email │
│ File system │ │ Rankings │ │ PDF │ │ Dashboard │
│ Meilisearch │ │ Anomalies │ │ CSV │ │ API │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Each stage is independent and composable. The same collected data can be aggregated differently for different audiences, formatted in multiple output formats, and delivered through various channels -- all without modifying the collection logic.
Report Immutability
Reports are immutable once generated. This is a fundamental architectural decision:
| Property | Rationale | Implementation |
|---|---|---|
| Immutable content | Audit trail integrity | Write-once storage, no update APIs |
| Timestamped | Temporal context for findings | generated_at in every report |
| Input-referenced | Reproducibility | Store input parameters and data hashes |
| Version-tagged | Schema evolution | report_version field in all reports |
| Author-attributed | Accountability | Generator module and trigger recorded |
Technical Deep Dive
Report Builder Architecture
The platform's report generation follows a builder pattern with composable sections:
defmodule PrismaticReporting.ReportBuilder do
@moduledoc """
Builds structured reports from platform data sources.
Supports multiple output formats and delivery targets.
Reports are immutable artifacts with full provenance tracking.
Each report records its generating module, input parameters,
and data hashes for reproducibility.
## Examples
iex> sections = [%{title: "Summary", content: "All checks passed", data: %{}}]
iex> {:ok, report} = PrismaticReporting.ReportBuilder.build("Quality Report", sections)
iex> String.starts_with?(report, "# Quality Report")
true
"""
require Logger
@type format :: :markdown | :json | :html | :csv
@type section :: %{
title: String.t(),
content: String.t(),
data: term(),
severity: :info | :warning | :critical | nil
}
@type report_metadata :: %{
generated_at: DateTime.t(),
generator: module(),
version: String.t(),
input_hash: String.t(),
format: format()
}
@spec build(String.t(), list(section()), format(), keyword()) :: {:ok, String.t()}
def build(title, sections, format \\ :markdown, opts \\ []) do
generator = Keyword.get(opts, :generator, __MODULE__)
version = Keyword.get(opts, :version, "1.0.0")
metadata = %{
generated_at: DateTime.utc_now(),
generator: generator,
version: version,
input_hash: compute_input_hash(sections),
format: format
}
header = format_header(title, metadata, format)
body = Enum.map_join(sections, "\n\n", &format_section(&1, format))
footer = format_footer(metadata, format)
report = header <> "\n\n" <> body <> "\n\n" <> footer
Logger.info("Report generated",
title: title,
sections: length(sections),
format: format,
size_bytes: byte_size(report)
)
:telemetry.execute(
[:prismatic, :report, :generated],
%{sections: length(sections), size_bytes: byte_size(report)},
%{title: title, format: format, generator: generator}
)
{:ok, report}
end
@spec build_multi_format(String.t(), list(section()), list(format())) :: %{format() => String.t()}
def build_multi_format(title, sections, formats) do
Map.new(formats, fn fmt ->
{:ok, report} = build(title, sections, fmt)
{fmt, report}
end)
end
defp format_header(title, metadata, :markdown) do
"""
# #{}
**Generated**: #{DateTime.to_iso8601(.)}
**Generator**: `#{inspect(.)}`
**Version**: #{.}
**Input Hash**: `#{.}`
"""
end
defp format_header(title, metadata, :json) do
Jason.encode!(%{
title: title,
metadata: %{
generated_at: metadata.generated_at,
generator: inspect(metadata.generator),
version: metadata.version,
input_hash: metadata.input_hash
}
})
end
defp format_header(title, metadata, :html) do
"""
<header>
<h1>#{html_escape()}</h1>
<div class="text-sm text-gray-400">
<p>Generated: #{DateTime.to_iso8601(.)}</p>
<p>Version: #{.}</p>
</div>
</header>
"""
end
defp format_header(title, _metadata, :csv) do
"# #{}\n"
end
defp format_section(%{title: t, content: c, severity: severity}, :markdown) do
severity_badge = case severity do
:critical -> "**[CRITICAL]** "
:warning -> "**[WARNING]** "
_ -> ""
end
"## #{}#{}\n\n#{}"
end
defp format_section(%{title: t, content: c, data: d}, :json) do
Jason.encode!(%{section: t, content: c, data: d})
end
defp format_section(%{title: t, content: c, severity: severity}, :html) do
severity_class = case severity do
:critical -> "border-red-500 bg-red-900/20"
:warning -> "border-yellow-500 bg-yellow-900/20"
_ -> "border-gray-700 bg-gray-800"
end
"""
<section class="p-4 border rounded-lg mb-4 #{}">
<h2 class="text-lg font-bold">#{html_escape()}</h2>
<div class="prose">#{}</div>
</section>
"""
end
defp format_section(%{title: t, content: c}, :csv) do
"\"#{}\",\"#{String.replace(, "\"", "\"\"")}\"\n"
end
defp format_footer(_metadata, :markdown), do: "---\n*Report generated by Prismatic Platform*"
defp format_footer(_metadata, :json), do: ""
defp format_footer(_metadata, :html), do: "<footer class=\"text-sm text-gray-500 mt-8\"><hr/><p>Report generated by Prismatic Platform</p></footer>"
defp format_footer(_metadata, :csv), do: ""
defp compute_input_hash(sections) do
:crypto.hash(:sha256, :erlang.term_to_binary(sections))
|> Base.encode16(case: :lower)
|> binary_part(0, 12)
end
defp html_escape(text) do
text
|> String.replace("&", "&")
|> String.replace("<", "<")
|> String.replace(">", ">")
end
endSpecialized Report Types
Quality Gate Report
defmodule PrismaticReporting.QualityGateReport do
@moduledoc """
Generates quality gate assessment reports across all platform domains.
Integrates with mix quality.gates for automated reporting.
"""
alias PrismaticReporting.ReportBuilder
@quality_domains ~w(compilation credo tests coverage docs specs forbidden_patterns
naming structure complexity documentation performance)a
@spec generate(keyword()) :: {:ok, String.t()}
def generate(opts \\ []) do
format = Keyword.get(opts, :format, :markdown)
sections =
@quality_domains
|> Enum.map(&assess_domain/1)
|> Enum.sort_by(fn s -> severity_order(s.severity) end)
summary = build_summary(sections)
all_sections = [summary | sections]
ReportBuilder.build("Quality Gate Assessment", all_sections, format,
generator: __MODULE__,
version: "2.0.0"
)
end
defp assess_domain(domain) do
# Domain-specific assessment logic
%{
title: "#{|> to_string() |> String.capitalize()} Domain",
content: "Assessment for #{}",
data: %{domain: domain, score: 0.0, issues: []},
severity: :info
}
end
defp build_summary(sections) do
critical = Enum.count(sections, &(&1.severity == :critical))
warnings = Enum.count(sections, &(&1.severity == :warning))
passed = Enum.count(sections, &(&1.severity == :info))
%{
title: "Executive Summary",
content: """
| Status | Count |
|--------|-------|
| Passed | #{} |
| Warnings | #{} |
| Critical | #{} |
| **Total** | **#{length()}** |
""",
data: %{critical: critical, warnings: warnings, passed: passed},
severity: if(critical > 0, do: :critical, else: if(warnings > 0, do: :warning, else: :info))
}
end
defp severity_order(:critical), do: 0
defp severity_order(:warning), do: 1
defp severity_order(_), do: 2
endRegression Test Report
The mandatory regression test report is required after every bug fix per NO MERCY doctrine:
defmodule PrismaticReporting.RegressionReport do
@moduledoc """
Generates mandatory regression test reports for bug fixes.
Required by NO MERCY doctrine -- every bug fix must include:
- Bug description and root cause
- Test file and test name
- Validation that test fails before fix, passes after
- NMND/NWB/NCLB compliance markers
"""
@spec generate(map()) :: String.t()
def generate(%{bug: bug, test_file: test_file, test_name: test_name} = report) do
"""
## Regression Test Report
| Field | Value |
|-------|-------|
| **Bug Fixed** | #{} |
| **Root Cause** | #{Map.get(, :root_cause, "N/A")} |
| **Test Added** | `#{}` :: `#{}` |
| **Validation** | Test fails before fix, passes after fix |
| **NMND Compliance** | No shortcuts, full implementation |
| **NWB Compliance** | Permanent fix, no backwards compatibility |
| **NCLB Compliance** | All claims validated, evidence updated |
| **Evidence Status** | Claims automatically synchronized |
"""
end
endSession Debrief Report
defmodule PrismaticReporting.SessionDebrief do
@moduledoc """
Generates session debrief reports for development continuity.
Stored in .claude/session-context/ with standardized naming.
"""
@spec generate(map()) :: {:ok, String.t(), Path.t()}
def generate(%{date: date, description: description} = session) do
filename = "#{Date.to_iso8601()}-#{slugify()}-session.md"
path = Path.join([".claude", "session-context", filename])
content = """
# Session Debrief: #{}
**Date**: #{Date.to_iso8601()}
**Duration**: #{Map.get(, :duration, "N/A")}
## Objectives
#{format_list(Map.get(, :objectives, []))}
## Actions Taken
#{format_list(Map.get(, :actions, []))}
## Files Modified
#{format_list(Map.get(, :files_modified, []))}
## Decisions Made
#{format_list(Map.get(, :decisions, []))}
## Recommended Next Steps
#{format_list(Map.get(, :next_steps, []))}
## Commits
#{format_list(Map.get(, :commits, []))}
---
*Auto-generated by Prismatic Platform session debrief*
"""
{:ok, content, path}
end
defp format_list([]), do: "- None recorded"
defp format_list(items), do: Enum.map_join(items, "\n", &("- #{&1}"))
defp slugify(text) do
text
|> String.downcase()
|> String.replace(~r/[^a-z0-9]+/, "-")
|> String.trim("-")
end
endReport Storage and Retrieval
defmodule PrismaticReporting.Storage do
@moduledoc """
Immutable report storage with provenance tracking.
Reports are write-once artifacts stored with metadata.
"""
require Logger
@reports_dir ".claude/reports"
@spec store(String.t(), String.t(), keyword()) :: {:ok, Path.t()} | {:error, term()}
def store(category, content, opts \\ []) do
timestamp = DateTime.utc_now() |> DateTime.to_unix()
format = Keyword.get(opts, :format, :markdown)
ext = format_extension(format)
filename = "#{}-#{}#{}"
path = Path.join(@reports_dir, filename)
with :ok <- File.mkdir_p(Path.dirname(path)),
:ok <- File.write(path, content) do
Logger.info("Report stored", path: path, size: byte_size(content))
{:ok, path}
end
end
@spec list(String.t()) :: list(Path.t())
def list(category) do
case File.ls(@reports_dir) do
{:ok, files} ->
files
|> Enum.filter(&String.starts_with?(&1, category))
|> Enum.sort(:desc)
|> Enum.map(&Path.join(@reports_dir, &1))
{:error, _} -> []
end
end
@spec latest(String.t()) :: {:ok, String.t()} | {:error, :not_found}
def latest(category) do
case list(category) do
[latest | _] -> File.read(latest)
[] -> {:error, :not_found}
end
end
defp format_extension(:markdown), do: ".md"
defp format_extension(:json), do: ".json"
defp format_extension(:html), do: ".html"
defp format_extension(:csv), do: ".csv"
endUsage in Prismatic Platform
Automated Report Generation
The platform generates reports at multiple trigger points:
| Trigger | Report Type | Command |
|---|---|---|
| Pre-commit hook | Doctrine compliance | mix check.doctrines --changed |
| CI pipeline | Full quality gate | mix quality.gates |
| Manual invocation | Health score | mix health.score |
| Bug fix workflow | Regression test | Embedded in commit |
| Session end | Debrief | /debrief command |
| OSINT execution | Tool results | Automatic on completion |
| DD analysis | Contradiction report | /dd-run command |
| Deployment | Smoke test | just smoke-test production |
| Scheduled | Statistics validation | mix prismatic.stats.validate |
OSINT Execution Reports
OSINT tool executions automatically generate structured reports stored in the ETS audit trail:
# OSINT execution produces a report automatically
{:ok, result} = PrismaticOsintCore.Toolbox.execute("czech-business-register", %{ico: "12345678"})
# Result includes: findings, execution_time, source_urls, confidence_scores
# Report stored in ETS with execution_id for audit retrievalDD Contradiction Reports
The DD analysis pipeline generates HTML contradiction reports with cross-referenced findings:
# DD contradiction analysis produces comprehensive HTML report
{:ok, report_path} = PrismaticDd.ContradictionAnalyzer.analyze_and_report(case_id,
output_dir: "~/Desktop/",
format: :html,
include_osint_verification: true
)
# Output: DD-Contradiction-Report-{case_name}.htmlCommon Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
| Manual report creation | Inconsistent format, missed sections | Automate with ReportBuilder |
| Mutable reports | Audit trail broken, can't reproduce | Write-once storage, no update APIs |
| Reports without actionable items | Data dump nobody reads | Every finding must include next steps |
| Missing provenance | Can't reproduce or verify report | Include generator, version, input hash |
| Reports too broad | Overwhelming, stakeholders ignore | Scope to specific audience and purpose |
| No severity classification | Can't prioritize findings | Tag sections as info/warning/critical |
Best Practices
- Automate report generation -- manual reports introduce delay and inconsistency; trigger reports as part of CI/CD and session lifecycle.
- Include actionable recommendations -- data without interpretation creates work; each finding should suggest next steps.
- Version report formats -- when report structure changes, version the format so consumers can adapt.
- Store reports immutably -- audit trails require that historical reports cannot be modified after generation.
- Keep reports focused -- a report that covers everything communicates nothing; scope each report to a specific audience and purpose.
- Include provenance -- every report must record its generator, input data hash, and generation timestamp.
- Use severity classification -- tag sections as info/warning/critical so readers can triage effectively.
- Support multiple formats -- the same data should be renderable as Markdown (dev), HTML (stakeholder), JSON (automation), and CSV (analysis).
- Cross-reference with evidence -- link findings to source data, commits, or telemetry events for verification.
- Track report generation metrics -- monitor how often reports are generated, their size, and which sections contain findings.
Related Terms
- Structured Log -- the raw data that feeds into reports
- Visualization -- graphical representation complementing textual reports
- Statistics -- the numerical summaries reports present
- Triage -- the prioritization process reports inform
- Dashboard -- real-time visualization complementing periodic reports
- Telemetry -- metrics collection feeding report aggregation
- KPI -- key performance indicators highlighted in reports
- Audit Logging -- event recording that reports summarize
- Quality Floor -- quality threshold enforced through gate reports
- Regression -- mandatory report format for bug fixes
See Also
- Quality Gates -- automated quality reporting
- Session Context -- development session reports
- OSINT Toolbox -- intelligence execution reports
- DD Pipeline -- due diligence analysis reports
Connect & Contribute
Created by Tomáš Korcak (korczis) | Open Source under GHL