26 Commits
Author SHA1 Message Date
flan c6f8274921 Add CI status badge to README 2026-08-04 02:45:43 +00:00
flan 79aded950d Remove README badge wall; tone down emphasis 2026-08-03 23:43:00 +00:00
flan 68f158761d Add CI badge (GitHub workflow) 2026-08-03 19:51:43 +00:00
flan 770f79f7b4 Add project badges 2026-08-03 19:28:34 +00:00
flan 3eb9db3fef Add sponsor badges to README 2026-08-03 19:09:25 +00:00
flan 21dddca640 Add donation links (GitHub Sponsors, Ko-fi) 2026-08-03 17:24:39 +00:00
flan 3b9a4c4ae9 Merge pull request 'Fix stale version stamp, ingest SSRF, non-atomic artifact write' (#1) from fix/repo-review-2026-07-19 into main 2026-07-19 16:53:57 -04:00
flan 6a27bd1654 Fix version drift, SSRF-able article fetch, and non-atomic artifact writes
- __version__ was stuck at 0.8.2 after the 0.8.3 release, so every ratings
  and manifest artifact carried the wrong pipeline_version; bumped to match
  pyproject.toml and added a regression test pinning the two together.
- fetch_article_text followed an RSS entry's <link> wherever it pointed,
  including through redirects, with no check on the resolved address. A
  compromised or malicious feed could aim it at an internal address (cloud
  metadata, LAN service). The host and every redirect hop are now resolved
  and rejected if they land on a private, loopback, link-local, or otherwise
  non-public range.
- artifacts.write_json wrote the target path in place, so serve.py (a
  separate process reading the same releases volume) could read a partially
  written or truncated artifact. It now writes to a temp file in the same
  directory and swaps it into place with os.replace.
2026-07-19 20:44:41 +00:00
flan 29afcd00b1 fix: pin embedding inference to one torch thread
torch's multi-threaded CPU forward pass intermittently raised SIGILL under
CPU contention on the deployment host; the collector swallowed it as
non-fatal, so that cycle silently computed no new embeddings. Pinning
inference to a single thread avoids the racy parallel kernel and removes
multi-threaded reduction nondeterminism, so recomputed vectors are
bit-reproducible.

Bump 0.8.2 -> 0.8.3: a vector recomputed after this change can differ from a
pre-0.8.3 one at ~1e-6 and move a borderline article across the cluster
threshold, which may shift a rating.
2026-07-19 18:57:49 +00:00
flan 444ea4fd94 v0.8.2: audit pass-3 remediation
Restore batch atomicity: explicit BEGIN guard before per-entry savepoints
(python-sqlite3 opens no transaction for SAVEPOINT, so pass-2's savepoints
committed items individually on the autocommit path — regression pinned by
an empirical rollback test). validate records thin-sample raters distinctly
from missing raters with the reason, in artifact and CLI. 67 tests.
2026-07-10 21:25:31 +00:00
flan 4a15dc8950 v0.8.1: audit pass-2 remediation
Savepoints make content+metadata writes atomic per entry; cached_embed runs
no DDL and never commits a caller's batch; cycle windows derive from UTC;
ratings pin the reference-corpus state; thin-sample raters are recorded
gate failures with automation-safe exit codes; snapshot fails loudly on
missing content; audit refuses empty stores and appends custody-heads.jsonl
history; repair adopts orphans as visible 'adopt' batches; timestamps
validated tz-aware UTC at insert; cleanup sweep (shared publish path,
README drift, dead code, encoding pins). 65 tests.
2026-07-10 21:16:07 +00:00
flan e1e38e664c Merge audit-hardening: v0.8.0 audit remediation 2026-07-10 16:58:26 +00:00
flan 8cefa325a6 v0.8.0: full-codebase audit remediation
Custody: rows+chain commit atomically per batch; audit gains reverse check
(unchained content fails), opens read-only, refuses missing stores. Gate:
tie-averaged Spearman (order-invariant; old impl could flip the gate by
alphabetization), both raters required, peeks labeled + unservable + never
pass. Manifests: corpus_hash covers full records (manifest_version 2).
Embedding cache self-invalidating (text-hash + model@revision key, chunked
reads, one implementation). serve: --config, health survives bad timestamps,
routes generated from artifacts.KINDS. cycle command owns window policy;
compose ships the collector. artifacts.py pins byte-determinism (utf-8,
sorted keys) for every writer. congress.py (dead custody-bypassing draft)
deleted. stats.py + hardening regression suite; 60 tests.
2026-07-10 16:58:26 +00:00
flan 5c5632e01f v0.7.0: early-dev reset — fingerprint v3, pure-metadata articles (ADR-0005)
- payload covers title+body+summary: fixes v2 defect where same-headline
  paywalled articles with different summaries shared a fingerprint (and
  potentially one cached embedding); pinned by test
- articles table is pure reference metadata (title/summary columns removed)
- observed_at (feed appearance; snapshot window key) vs fetched_at (storage;
  custody record) + source provenance column
- migration machinery deleted; pre-v3 stores refused with clear error
- investigated+rejected: Wayback feed-replay backfill (0-4 capture-days/60
  for most feeds; uneven coverage would read as editorial choice)
2026-07-10 16:17:15 +00:00
flan db30b48e1d Merge db-custody: v0.6.0 content-addressed storage + custody chain 2026-07-10 15:50:25 +00:00
flan b8b97c5d05 v0.6.0: content-addressed store with append-only custody chain
- contents table: distinct texts stored once, zlib, SHA-256 keyed; wire copy
  dedups across outlets; measured 12.2MB -> 6.3MB on the real corpus
- custody_log/custody_items hash chain over every ingest/reference batch;
  migration chains the backlog as genesis; heads served at /custody
- tiltmeter audit: re-hash everything + walk chain; tamper tests go through
  a raw non-FK connection (the realistic attacker path)
- outlets dimension table, verbatim byline capture, HTML-stripped summaries,
  URL canonicalization (tracking params removed, original kept if different)
- transactional v1->v2 auto-migration preserving fingerprints verbatim: all
  published manifests stay verifiable; WAL mode
- METHODOLOGY D11 + ADR-0004; glossary + plain-language docs updated
2026-07-10 15:50:25 +00:00
flan 72e7a6ae2f v0.5.0: M3 gate machinery — validate and sweep commands
- validate: Spearman vs AllSides + Ad Fontes over shared outlets, fixed-seed
  permutation p-values, gate = rho >= 0.7 both AND reliable orientation;
  refuses unverified reference values unless --allow-unverified (peek only);
  emits validation-{snapshot}.json, served at /validation/{id}
- sweep: rescoring across clustering-threshold grid w/ rank-corr-vs-default
  stability; emits sweep-{snapshot}.json, served at /sweeps/{id}
- day-one diagnostics on the dry-run release recorded in CHANGELOG
2026-07-10 15:07:22 +00:00
flan a282849898 v0.4.1: health staleness scoped to configured outlets
Retired outlets aged out of the db alarmed forever; configured outlets
with no rows were invisible. Staleness now reads the config: retired
outlets excluded, never-collected outlets stale with null hours.
2026-07-10 07:39:06 +00:00
flan aa08bdeea8 Transcribe all 20 Ad Fontes bias scores into reference ratings
Fetched from each outlet's adfontesmedia.com source page 2026-07-10; all
entries verified: true with per-outlet source URLs. AllSides values remain
provisional pending browser verification (site blocks automated access).
2026-07-10 07:35:21 +00:00
flan 097cda9a95 v0.4.0: unattended-operation hardening
- /health: per-outlet staleness + stale_outlets list, degraded status
- feed audit swaps: CNN -> CBS News (legacy RSS serves 2022-2024 articles),
  WSJ -> Newsweek (Dow Jones froze public feeds Jan 2025); ratings +
  ownership entries updated, spectrum slots preserved
- HF cache at /opt/hf-cache world-readable: offline pipeline under any uid
- CI publishes version-tagged GHCR images
2026-07-10 07:31:58 +00:00
flan 12e4bb7f70 Fix /outlets crash on YAML date fields
json.dumps failed on date objects parsed from unquoted YAML dates
(ownership retrieved fields), killing the response mid-flight; serialize
with default=str and pin the case in the serve test fixture.
2026-07-10 07:19:04 +00:00
flan 2b81eef7ea Merge m2-scoring: v0.3.0 scoring, serving, Docker, ownership, factuality feasibility 2026-07-10 07:00:52 +00:00
flan 4d9200a785 v0.3.0: scoring assembly, API serving, Docker, ownership data, factuality feasibility
- orient: party-mean speech embeddings -> axis sign + reliability flag (ADR-0003)
- score/run: deterministic ratings.json + stories artifact; report: evidence pages
- serve: stdlib read-only API (/health /outlets /ratings /stories /manifests
  /evidence), CORS, path-traversal-safe
- Docker: CPU-torch image (1.23GB) w/ baked model for offline recompute; compose;
  CI builds every push, publishes GHCR from main
- outlets.yaml: sourced ownership data per outlet (verified flags); outlets doc
  regenerated w/ ownership columns
- docs/feasibility-factuality.md: anchor analysis, F1/F2/F3 architecture, gate
- fix: torch as direct dep pinned to CPU index (image was pulling CUDA, 6.7GB)
2026-07-10 07:00:52 +00:00
flan a7bd9b8c90 Merge m2-pipeline: v0.2.0 scoring pipeline foundations 2026-07-10 06:29:42 +00:00
flan 91e4fb63f6 v0.2.0: snapshot manifests, story clustering, selection signal, congressional reference corpus
- snapshot: deterministic tamper-evident manifests (hash-verified on load)
- embed: pinned all-MiniLM-L6-v2 CPU, headline+lede, fingerprint-keyed cache
- cluster: agglomerative/cosine cross-outlet stories + coverage matrix
- signals/selection: CA first axis + bootstrap CIs; synthetic-fixture tests
  (planted-bloc separation, determinism, CI behavior, underdetermined refusal)
- reference: CREC floor speeches via govinfo + voteview party join;
  ambiguous speakers dropped; fixture tests for attribution
- reference_ratings.yaml: validation-only rater values w/ verified flags
- ADR-0002 implementation choices; research.md 1.c adjacent living projects;
  D7 circularity note; 10s fetch timeout cap
2026-07-10 06:29:42 +00:00
flan d64a140add Scaffold v0.1.0: corpus collector, methodology spec, docs-enforcement CI
- ingest: 20 outlets' politics feeds -> full text -> SQLite w/ SHA-256 fingerprints
- METHODOLOGY.md: 10 decision blocks (decision/rationale+sources/alternatives/failure modes)
- docs: research foundations, plain-language walkthrough (CI-gated <= grade 10),
  glossary, ADR-0001, generated outlets table
- CI: pytest+ruff, docs gates (readability, glossary coverage, decision-block lint,
  outlets-table freshness), code/docs sync gate
- systemd user timer ingests every 6h (repo-external, documented in README quickstart)
2026-07-10 05:22:31 +00:00