Skip to contents

The local evidence repository retains revision history and checks relationships between records, lifecycle events, and stored artifacts. Its backup and restore functions operate on that local evidence package. These mechanisms detect several forms of local modification; they do not establish qualified audit-record custody or a durable study repository.

Rendered integrity and recovery diagram

View in Full Screen

Records and identity

sqlite_evidence_repository() stores plots, revisions, attempts and outcomes, evidence entries, accepted artifact bundles, reviewer decisions, export receipts, command keys, and a status/version projection. Numbered SQL migrations apply in version order and record successful versions in schema_migrations. Foreign keys are enabled for each repository connection. SQL constraints enforce revision numbering, permitted status and decision values, unique command/event keys, and record relationships.

canonical_serialize() and canonical_hash() sort named list elements recursively and serialize compact JSON before hashing. Evidence details have a SHA-256 content hash. Reviewer decision records have a separate hash tied to a lifecycle event. These hashes serve different purposes; a lifecycle chain does not contain every repository row.

local_artifact_store() stores raw bytes at <root>/<first-two-hash-characters>/<sha256>. put() writes to a temporary file, checks its bytes, and moves it into place. Existing objects are verified before reuse. get() verifies an object’s hash before returning its bytes. Code and image artifacts therefore have byte-level identity independent of their display filenames.

Lifecycle writes and recovery

.append_lifecycle_event() creates a per-plot sequence spanning its revisions. Each event binds the plot and revision identifiers, sequence, event type, serialized payload, previous hash, and timestamp. The first event starts from a 64-character zero hash. A separate chain-root.json records the latest sequence and chain hash for each plot.

Repository commands use .with_repository_lock(): a companion SQLite .lock database holds a BEGIN IMMEDIATE transaction while the operation runs. Repository writes use database transactions. Lifecycle transitions compare expected_version with the current projection, so stale commands fail. Idempotency keys bind a canonical command hash: repeating the same command is tolerated, while reusing its key for different content fails.

The database transaction completes before the sidecar root is published. If publication fails, an exact replay of the previously registered command can reconcile the root. A new lifecycle command is blocked while roots disagree. Reconciliation refuses a root that is newer than the database or conflicts at the same sequence. This addresses a recoverable publication failure without silently accepting arbitrary rollback.

rebuild_projections() reconstructs revision status and version from initial statuses and ordered lifecycle payloads. It replaces the status projection; it does not reconstruct missing evidence, artifacts, attempts, reviewer decisions, or export receipts. Historical revision update and deletion methods explicitly reject those operations. Direct filesystem or SQL access remains outside these API restrictions.

What integrity verification checks

verify_integrity() checks foreign keys; per-plot event order and chain hashes; sidecar roots and persisted plot membership; reconstructed status/version projections; evidence-detail hashes; accepted-bundle hashes against revision hashes; reviewer hashes, decision identifiers, and lifecycle bindings; and receipt code/image hashes against revision hashes. When an artifact store is attached, it also verifies referenced code and image bytes.

require_artifacts = TRUE rejects a nonempty revision repository without an artifact store. The default is FALSE. The verifier does not independently recalculate plotting statistics, authenticate reviewers, or compare exported workspace files with receipts. Its evidence-entry hash checks do not prove that every expected evidence record exists. Analytical hashes are checked as recorded relationships, rather than through a separate content-addressed analytical artifact check.

Backup and restore sequence

backup(destination) requires an empty destination, holds the repository lock, and verifies source integrity with require_artifacts = TRUE. It stages a database copy using RSQLite::sqliteCopyDatabase(), copies or creates the sidecar root, and copies the artifact directory when attached. The staged repository is verified before publication.

backup-manifest.json records the database SHA-256, chain-root SHA-256, table row counts, and completion timestamp. It does not enumerate or hash every artifact file and does not sign the package. The staged directory moves into the destination after verification and manifest creation. A failed operation removes its staging directory.

restore_evidence_repository(backup_path, destination) checks the completion manifest, database/root hashes, and recorded row counts before copying into a new staging directory. It attaches an artifact store when the backup contains an artifacts directory, verifies the staged repository, and publishes to an empty destination only after success. It returns a repository pointing at the restored paths.

Restore coverage gap: restore calls verify_integrity() with its default argument. If the complete artifact directory is absent, no artifact store is attached and metadata verification can proceed. Corrupt referenced bytes in a present artifact directory are detected. The manifest therefore does not establish that a restored package contains all artifact bytes.

Cleanup and operational limits

cleanup_orphan_artifacts(minimum_age_seconds = 3600) locks the repository and preserves code/image hashes referenced by revisions. Cleanup considers only files matching the two-level content-addressed layout and removes unreferenced files old enough to meet the threshold. It does not implement evidence retention or study deletion. Its reference scope is the attached repository; sharing an artifact root with another repository would not protect that other repository’s references.

The application coordinator uses a temporary assurance-session- root unless repository_root is supplied. Backup, restore, cleanup, and recovery exist as callable operations; no automatic scheduled backup or cross-session discovery is established by this workflow. Persistent storage configuration, retention policy, access control, independently protected roots, and qualified target-environment custody remain unresolved.

Inspected test coverage

Repository tests cover chain corruption, event omission, root rollback, projection rebuilds, evidence/decision modification, publication retry, command reuse, migrations, serialized writes, and pending artifact-hash binding. Artifact tests cover corruption, reference-preserving cleanup, layout filtering, and resolved roots. Backup/restore tests cover round trips, modified/incomplete backups, concurrent lifecycle writes, empty repositories, required artifact reconciliation during backup, and corrupt artifact bytes during restore. These tests were inspected, not executed for this documentation change.

Related: execution evidence, correction traceability, and controlled export.