Concept 7D Local Report and Artifact Delivery
Traceable local report definitions, immutable report artifacts, historical Run generation, content verification, retention, and delivery behavior.
Concept 7D makes reports first-class local evidence while preserving A.T.O.M's local-first boundary. The lifecycle is:
ScenarioRevision + Run(s)
↓
ReportDefinition
↓
GeneratedReportArtifact
↓
inspect · download · regenerate · retain · delete
The change does not add PostgreSQL, PostGIS, S3/MinIO,
authentication, remote jobs, or a server-side PDF service. It does not
change RF propagation, interference, optimization, Pareto membership,
recommendation semantics, atom-scenario-v1, Concept 6
readiness, ProjectV2 compatibility, workspace IndexedDB, or Concept 7C
Run History capture.
Validation debt closure
docs/requirements.txt already documents a hashed PyYAML
6.0.3 lock. The active system Python did not expose yaml,
so python3 docs/validate_docs.py initially failed with
ModuleNotFoundError. The repository policy was left
unchanged. A temporary virtual environment installed the documented lock
and produced:
documentation valid: 36 HTML pages and 41 API paths
That pre-change validation produced
documentation valid: 36 HTML pages and 41 API paths. After
generating the Concept 7C/7D reference pages and evidence links, the
final post-change validation produced
documentation valid: 38 HTML pages and 41 API paths. The
temporary environment is not part of the repository.
ReportDefinition
ReportDefinition is mutable project/domain metadata
stored through LocalRepository and carried through the
existing ProjectV2 compatibility projection. It describes a report
configuration, not generated bytes.
{
schema_version: 1,
report_id,
project_id,
scenario_id,
scenario_revision_id,
run_ids: [],
title,
sections: [],
presentation_options: {},
source_kind,
source_binding,
created_at,
updated_at
}source_kind is one of scenario_revision,
historical_run, comparison_runs, or the
explicit compatibility mode live_compatibility. A dirty
working draft is never silently associated with an older saved revision;
the compatibility path records that it is live/unbound. Historical
generation requires the exact durable ScenarioRevision and explicit
source Run(s).
GeneratedReportArtifact
GeneratedReportArtifact is immutable evidence metadata.
Its bytes are never overwritten. Regeneration reuses the
ReportDefinition/source references and creates a new
artifact_id, preserving the previous artifact.
Required metadata includes:
- artifact identity and report/project/scenario lineage;
- exact ScenarioRevision and Run IDs;
- format, media type, filename, byte size, storage reference, and availability;
- report schema version, generator version, and generated timestamp;
- SHA-256 content hash;
- provenance, report manifest, and warnings.
artifact_id and content_hash have different
meanings. The first identifies a generated evidence record; the second
verifies its bytes. Identical bytes from two generations may therefore
have different artifact identities.
The manifest records report_schema_version,
generator_version, ScenarioRevision fingerprint, Run IDs,
Run input fingerprints, dataset references/hashes, the final artifact
content hash, and generated_at. It is not an
atom-scenario-v1 hash.
Physical storage
Artifacts use a separate IndexedDB database:
atom-artifacts
├── metadata (versioned metadata envelopes keyed by artifact_id)
└── bytes (immutable report bytes keyed by artifact_id)
This is additive storage. Generated bytes do not enter
atom-planning-workspace, ProjectV2, or
atom-run-history. Listing reads only the metadata store; an
artifact body is hydrated only for explicit download/verification. A
LocalArtifactStore contract exposes
putArtifact, getArtifact,
getArtifactMetadata, listArtifacts,
deleteArtifact, and hasArtifact, plus local
project cleanup and quota inspection.
The current safety ceiling is 64 MiB per artifact. It is a
browser-safety ceiling, not an eviction threshold. When
navigator.storage.estimate() is available, the store warns
at 80% projected use and fails explicitly at quota. It never silently
evicts evidence. When quota APIs are missing, storage proceeds and
reports no estimate.
Content integrity and failure states
Bytes are hashed with SHA-256 before persistence. On read, the store verifies both hash and byte size.
| State | Meaning | User behavior |
|---|---|---|
available |
Metadata and bytes verify | Download is enabled |
missing |
Metadata exists but bytes are absent | Download is blocked; regeneration is explicit |
corrupt |
Bytes fail hash or size verification | Bytes are never served as valid evidence; regeneration is explicit |
Malformed metadata is isolated from the list. A corrupt artifact does not prevent other report rows from loading. A generation failure does not create a valid-looking partial artifact. If report generation succeeds but retention fails, the current Markdown download or print action remains available and the UI says that local retention failed.
Report source resolution
resolveReportSource is a pure boundary. It checks that
the selected Runs belong to the same project, scenario, and exact
ScenarioRevision. It records source fingerprints and dataset references
and emits an evidence boundary:
- ScenarioRevision input values are source evidence;
- compact Run summaries/details are retained Run evidence;
- retained public optimization baseline/Pareto/recommendation data remains visible;
- large RF arrays and private ledgers remain outside the report source;
- unavailable historical detail is stated rather than recomputed.
The existing report renderer remains the semantic center.
buildHistoricalPlanningReport adapts durable source records
into the existing report model; it does not call a backend endpoint or
rerun RF/optimizer work.
Historical simulation
The Run's compact simulation and coverage summaries populate the RF result and gap sections. The report includes the ScenarioRevision and Run IDs and explicitly says that detailed signal surfaces and ray geometry were not retained when those arrays are absent.
Historical optimization
The Run's public baseline, Pareto solutions, recommendation, effective priorities, constraints, search policy, and selected solution identity populate the network report. The optimizer is not rerun. The report keeps the existing distinctions between baseline, recommended, selected, and Pareto alternatives.
Multi-Run reports
The source boundary accepts compatible Runs from one exact ScenarioRevision, including baseline-plus-optimization or simulation-plus-optimization combinations. Unrelated revisions are rejected. The current UI generates a single selected historical Run from Run History; the resolver is ready for explicit comparison definitions without auto-combining arbitrary project history.
Format path
There is one report model and two renderers:
report model
├── renderMarkdownReport → Markdown artifact
└── renderHtmlReport / renderPrintableReport → HTML / print artifact
The current browser print workflow remains available. The retained print-ready artifact is HTML and can be downloaded/printed later; no server PDF service is introduced.
UI workflow
The existing Report tool now has a compact retained-reports section. Each row shows title, scenario, format, timestamp, byte size, and availability. Selecting a row reveals the artifact ID, ScenarioRevision, source Runs, SHA-256, generator, and evidence status. Actions are:
- download stored bytes without regeneration;
- regenerate from the same ReportDefinition/source bindings as a new artifact;
- delete the artifact only;
- inspect missing/corrupt status without claiming evidence is available.
Run History rows for succeeded Runs expose
Generate report. That action prebinds project, scenario,
exact ScenarioRevision, and the selected Run, then stores a Markdown
artifact. The source Run remains immutable and the ScenarioRevision
remains unchanged.
Current-state Markdown and print actions remain compatible. They
create a ReportDefinition and retain an artifact when possible. If the
working draft is dirty, the definition is explicitly
live_compatibility with no older revision silently
attached.
Deletion and export boundaries
Deleting an artifact removes only its metadata and bytes. It does not delete its ScenarioRevision, source Run, or ReportDefinition. Deleting a ReportDefinition is intentionally separate and is not implemented as an implicit artifact purge. Project deletion cleans project artifacts through the separate store before removing the project workspace; scenario deletion leaves historical report evidence intact.
ProjectV2 export remains a planning-workspace export, not a full evidence archive. A future portable evidence bundle may contain report bytes, metadata, Run summaries, fingerprints, and a checksum manifest. Concept 7D does not add a large ZIP format.
Privacy and determinism
The report path inherits existing measurement privacy boundaries and does not include credentials, hidden browser state, local filesystem paths, or raw private measurement identifiers. Current map viewport and drawer state are excluded from durable historical input.
For a fixed ReportDefinition, ScenarioRevision, Runs, report schema, and generator version, semantic report content is stable. Generated timestamps and artifact IDs are evidence metadata and may differ across regeneration. No historical report silently reconstructs missing RF output.
Evidence files
- Pre-change baseline
- Report source map
- Artifact storage decision
- Artifact schema
- Artifact type vocabulary
- Storage size and quota policy
- Test evidence
- Post-change comparison
Concept 7E recommendation
Inspect actual report/source friction next, with priority on Scenario and Branch UX productization. The artifact boundary is now explicit; a large evidence bundle or backend durable repository should wait until users demonstrate a need for cross-device sharing, collaboration, or retained large RF outputs. PostgreSQL/PostGIS is not the automatic next phase.