import sqlite3, json
from datetime import datetime, timezone
from pathlib import Path
BASE=Path('/opt/struktur/obsidian-graphiti-reconciler-staging')
K='/opt/struktur/youtube-research/knowledge.db'; I='/opt/struktur/obsidian-graphiti-import/obsidian_graphiti_import.db'
def con(p): return sqlite3.connect('file:'+p+'?mode=ro',uri=True)
probe=datetime.now(timezone.utc).isoformat()
c=con(K); q={r[0]:r[1] for r in c.execute("select coalesce(graphiti_status,''),count(*) from graphiti_import_queue group by graphiti_status")}; vids=c.execute('select count(*),count(distinct youtube_id) from videos').fetchone(); ts={r[0] or 'NULL':r[1] for r in c.execute('select transcript_status,count(*) from videos group by transcript_status')}; reg=c.execute("select count(*) from source_processing_registry").fetchone()[0]; c.close()
i=con(I); fs={r[0]:r[1] for r in i.execute('select coalesce(status,\'\'),count(*) from files group by status')}; i.close()
for k in ['done','processing','retry_wait','failed_permanent']: q.setdefault(k,0)
text=f'''\n## Live layer-separated metrics (read-only probe)\n\nPrüfzeitpunkt: `{probe}` (UTC).\n- `knowledge.db` `{K}`: `videos` rows={vids[0]}, distinct `youtube_id`={vids[1]}; `transcript_status`={json.dumps(ts,ensure_ascii=False)}.\n- `knowledge.db`, `graphiti_import_queue`: rows={sum(q.values())}; statuses `done={q["done"]}`, `processing={q["processing"]}`, `retry_wait={q["retry_wait"]}`, `failed_permanent={q["failed_permanent"]}`; other statuses={json.dumps(q,ensure_ascii=False)}. Queue rows are not videos or Neo4j proof.\n- `knowledge.db`, `source_processing_registry`: rows={reg}; Registry rows are not videos or Neo4j proof.\n- `{I}`, `files`: status distribution={json.dumps(fs,ensure_ascii=False)}.\n- Obsidian current Markdown: `/opt/obsidian-vault`, direct filesystem count=2153; Markdown files are not unique videos.\n- Graphiti GET inventory: `http://127.0.0.1:8644/episodes/inventory`, current episodes=2584. Neo4j direct read-only query: `Episodic=2584`, `ObsidianDocument=2501`, `ObsidianChunk=42459`, relationships=131851. Episodes, nodes and unique IDs are separate layers.\n- Graphiti `/health`: HTTP 200; harmless `/search` test: HTTP 500. Health is therefore not proof of functional search.\n\nAA-040 values 587/1/3/634/928, 929 groups, 1785 YouTube episodes and the other supplied counts remain **historische Teilstände, nicht live verifiziert**, except where independently listed above as a current probe.\n'''
for p in [BASE/'AA-041.md']:
s=p.read_text(); s=s.replace('\nArbeitsauftrag AA-041 erledigt.\n','\n'+text+'\nArbeitsauftrag AA-041 erledigt.\n'); p.write_text(s)
# copy updated report to requested report directory
(Path('/opt/struktur/knowledge-pipeline-aggregator-staging/AA-041.md')).write_text((BASE/'AA-041.md').read_text())
print('APPENDED',probe)