Skip to content

Scheduled source evidence

The members and committee-reports rollups retain the exact responses used by ordinary scheduled runs. SpicyDocs still acquires and parses each source. Host adapters observe its CapturedBodyResponse results before later validation can fail; they do not add another HTTP client or parser.

Each run writes to output/source-evidence/<run-id>/. Its artifact/ directory contains a journal and content-addressed blobs written through SpicyDocs storage. Journal receipts include the source observation time, requested and resolved URLs, response status, encoding, content type, byte size, SHA-256, and request-body identity when present. recorded_at is the journal time and never replaces the source's observed_at. A capture may appear first at acquisition and again when used; these stages are not a request count. Identical bodies share one local blob.

Members retain both roster responses, including a successfully parsed current roster that later fails its required LIS identifier check. Reports retain used collection listings, package summaries, MODS metadata, bodies and hearing details. Successful metadata survives a later body or parse failure. The page observer retains a parsed listing or detail response before the source traversal checks count drift or overflow, so an offending page remains available even when the traversal never yields it. The journal records selected, deferred, unchanged and refused packages, the discovery window, page and package caps, and checkpoint time. Agenda requests remain outside this rollup's selected scope.

Credential refusals stop the run. Their bodies are excluded from storage; configured credential echoes in response or request bytes are also refused. Recorded URLs and errors pass through the source owner's credential scrubber. Storage or journal failures abort the build rather than becoming a skipped source record. Source responses retained before a later failure remain in the failed run's audit artifact.

Rulespec admits a separate spicy-regs-source-evidence artifact. It is an audit of observed responses, not a claim of complete source coverage or a supported SpicyDocs source-native release. The table generation remains Parquet-only. Its root inputs names the audit artifact with role source-evidence and the captured prior family with role prior-generation when one exists. This preserves the lineage of unchanged bodies and merged rows without changing their observation times.

The retained prior root must match the publication index's immutable pin. Its existing input references remain visible in the journal. A prior root with inputs=[] is explicitly recorded as an inherited raw-source evidence gap. This feature does not retroactively qualify it. Without a managed prior, any legacy or local merged inputs likewise remain unqualified.

The Congress.gov index tables close that gap by reading again. A held detail that no retained response backs is read once more, after the run's own new, changed and unread records and under the same per-run detail cap. At first that is every held detail: a legacy prior backs none, and neither does an evidenced prior whose journal states no remainder. Each run journals what is left as unevidenced in its congress-index-selection event. The next run inherits that remainder from the prior's evidence journal, which it reads through the prior root, the member manifest and the journal digest, without reading any blob. The remainder only shrinks, so each detail is re-read once. A held row the publisher no longer lists cannot be re-read, and stays in the remainder by name.

The same remainder carries a held row that an older detail shape read differently. Each selection event names its shape_version; since spicy-docs 0.54.0 that is lists=stated-001, under which a list the detail does not state is NULL. When the prior was journaled under another shape, or none, the rows the new shape can read differently join the remainder once. Under lists=stated-001 those are rows published before detail_read that hold [] in a list column. Columns a stored field already gives a held row are derived again on every row each run, with no request: detail_read from the old list marker, the RIN columns and nominations.is_civilian.

Before publishing a table pointer, the publisher requires the exact admitted audit input and re-admits it from storage. Missing, altered or failed evidence blocks publication. Existing generations with empty inputs remain readable and retain their original evidentiary limits.

The artifact root, member manifest and journal are stored under source-evidence/<artifact-digest>/. Each blob member blobs/sha256/<hex> is stored once, at source-evidence/blobs/sha256/<hex>, and shared by every artifact that cites the same bytes. A daily run that re-captures an unchanged source therefore sends, stores and reads back only its new bytes and small metadata. Consumers resolve a source-evidence input with the same public base URL: blobs/ members under source-evidence/, all other members under the artifact's digest prefix, then admit it with the input's expected pin. Artifacts published before September 25, 2026 keep their blobs under their own prefix and stay readable there.

A blob is created only from bytes whose SHA-256 equals its key, with Content-MD5 on every request and a create-only condition, and admission reads it back once. A blob that already exists is not downloaded again when its stored size and ETag equal those of the locally admitted bytes; rulespec then admits the artifact over those local bytes. Any other existing object is read back and refused unless its bytes match. This check cannot see a same-size replacement that also reproduces the MD5-based ETag, storage corruption that leaves the ETag unchanged, or a replacement between the check and a later reader's download. The shared prefix must be written only by this publisher. A full audit can still read every blob and admit it.

The reusable workflow always uploads output/source-evidence/ with the run audit, on success and failure. The directory is outside hidden .builds/ paths. Failed builds have no new table pointer; run-outcome.json records the run result outside the immutable artifact. A transport failure during the pointer request can have an uncertain publication result, which requires reading the public index again. Workflow artifacts follow the retention period configured in _rollup.yml; successfully published evidence remains under its immutable object-storage prefix.

Offline regression tests are in tests/test_source_evidence.py. They check literal input bytes against retained blobs and native output fields, failed postconditions, late parsing and listing failures, credential exclusion, unchanged resumes, caps, prior-root pins and publication refusal. They do not establish live population completeness or deploy this feature.