fix: deterministic inventory dump — last_checked as INTEGER

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>
This commit is contained in:
Nubenetes Bot
2026-06-19 15:37:51 +02:00
parent 6bed317cdc
commit 1fe0509433

View File

@@ -113,7 +113,7 @@ def save_inventory(inv: Dict, shard_file: str = None):
addition_method TEXT,
content_hash TEXT,
health_score REAL,
last_checked REAL,
last_checked INTEGER,
needs_ai_refresh BOOLEAN,
discovered_at TEXT,
last_ai_eval TEXT,
@@ -139,7 +139,16 @@ def save_inventory(inv: Dict, shard_file: str = None):
for url, entry in inv.items():
if not isinstance(entry, dict):
continue
# last_checked is a coarse staleness timestamp (epoch seconds). Normalize
# it to int on the entry itself — before building the SQL record AND the
# YAML dump below — so both serializations agree, and SQLite's
# version-specific REAL->text float formatting can't rewrite every row on
# dump. Sub-second precision is not used by any reader.
lc = entry.get("last_checked")
if isinstance(lc, float):
entry["last_checked"] = int(lc)
record = {col: entry.get(col) for col in columns if col not in ["hierarchy", "tags", "v1_locations", "v2_locations", "youtube_mosaic", "extra_metadata"]}
record["url"] = url
@@ -160,7 +169,7 @@ def save_inventory(inv: Dict, shard_file: str = None):
# Conversions
record["is_microservice"] = 1 if record.get("is_microservice") else 0
record["needs_ai_refresh"] = 1 if record.get("needs_ai_refresh") else 0
placeholders = ", ".join(["?"] * len(columns))
values = [record[col] for col in columns]
cursor.execute(f"INSERT OR REPLACE INTO resources ({', '.join(columns)}) VALUES ({placeholders})", values)