Reconstructed the missing 2.6.0-2.9.1 entries from GitHub Release notes,
closing the gap where CHANGELOG.md had stalled at 2.5.8 while releases
reached 2.9.x. Changelog is now complete and continuous through 2.9.9.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The in-repo CHANGELOG had drifted (last entry 2.5.8) while GitHub Releases
went to 2.9.7. Backfill the 2.9.2-2.9.8 entries I have full detail on and
flag the 2.6.0-2.9.1 gap. 2.9.8 also covers the existing gh_pushed
'N/A'->null data cleanup (PR #385).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the v2.9.4 source fix by cleaning the existing data. last_checked
is already INTEGER (applied by a pipeline run after v2.9.6), so this diff is
limited to the 281 gh_pushed values. Deterministic / CI-reproducible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The four logo hero cards use a 100px <img>; the Intelligence Digest card
uses an emoji <div class=hero-badge-icon> that was only ~32px tall, so it
rendered shorter. Give .hero-badge-icon a 100px flex-centered box (and
matching 12px bottom margin) so all five cards are the same height.
Cache-bust bumped to ?v=2.9.7.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
last_checked stored epoch seconds as REAL; SQLite's version-specific
REAL->text float printing rewrote ~every row on dump, so a locally
regenerated inventory.sql diverged from CI's. Normalize last_checked to
int on the entry before both the SQL record and the YAML dump, and make
the column INTEGER. All readers do coarse day-level staleness checks, so
sub-second precision is unused.
Verified: load->save idempotent for inventory.sql AND inventory.yaml, and
local (SQLite 3.40.1) output byte-identical to CI-equivalent python:3.11
(SQLite 3.46.1). The deterministic data regenerates on the next pipeline
save; committing only the code avoids conflicting with pipeline data churn.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
load_inventory_channels() selected entries via 'youtube_mosaic' in entry
(key presence). save_inventory() always writes a youtube_mosaic column
that the SQL round-trip materializes as an empty dict {} on EVERY entry,
so the filter matched all 18,647 inventory entries and emitted ~18.6k
inline <img> links on a single line. That produced a 4MB v2-docs/index.md
(and 2.9MB docs/index.md), hanging both V1 and V2 index pages in the
browser. A prior revert masked it; the next SQL-backed sync re-broke it.
Fix: only treat an entry as a mosaic channel when youtube_mosaic is a
non-empty dict with a logo image (136 curated channels). Regenerated both
index files via reorganize_mosaic — docs/index.md is now byte-identical
to the last known-good state (52KB); v2-docs/index.md back to 41KB.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The date field gh_pushed was written as the literal string 'N/A' when
GitHub data was unavailable (fast_enrich, gemini_utils). That non-date
string then reached datetime.fromisoformat() downstream. Now writes None
(which all readers already handle); gh_license keeps its 'N/A' string
sentinel since it is a displayed value, not a date.
Also hardens the twin fromisoformat() call in agentic_curator's MVQ
penalty check (guards N/A/None and wraps parsing) so a bad value can't
crash a curation batch.
Adds scripts/normalize_gh_pushed.py to clean the 281 existing inventory
entries (run in CI to avoid SQLite float-serialization churn — see
script docstring).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The V2 safety audit emitted hundreds of WARN lines and then aborted
entirely with 'NoneType object has no attribute lower':
- validate_mvq_compliance: gh_pushed of 'N/A' (and historic None/empty)
was passed to datetime.fromisoformat() -> 'Invalid isoformat string';
and gh_stars/stars stored as null hit 'None * int'. Now skips entries
with no usable push date and coalesces None before the star math.
- validate_linguistic_tagging: a null 'language' field caused
None.lower(), which aborted the whole report. Now coalesces to English.
- generate_audit_report: each mandate now runs in isolation so a single
bad entry can no longer kill the entire audit; failures are logged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>