Prompt file imported from imaksligais/atmina-lv (
.claude/commands/dienas-rutina.md). Copyright stays with the author.
Dienas rutīna — atmina.lv
Pass/fail kritēriji dienas pārskatam —
wiki/operations/quality-bars.md. Izlasi PIRMS glabāšanas/publicēšanas, ne pēc. CLAUDE.md § Quality Bars sauc šo failu par kanonisko nesēju; līdz 2026-08-09 uz to saistīja 1 no 17 nesējiem.
Orchestrate today's routine. Canonical state first, then editorial steps. Respect the guardrails below — they encode prior incidents, not preferences.
0. Status
.venv/Scripts/python.exe -c "from src.routine import print_routine; print_routine()"
Pasts (redakcija@atmina.lv) nav rutīnas solis: SessionStart hook (scripts/mail_tool.py remind) jaunas sesijas sākumā atgādina, ja tās dienā iesūtne nav skatīta; pa dienu operators pats palūdz paskatīties (operatora lēmums 2026-10-18). Atbildes — tikai ar operatora «jā».
Never bare python — on the operator machine it resolves to a foreign venv, and the failure mode is a PARTIAL WRITE, not a clean error (CLAUDE.md § Commands).
TypeSafe matcher veto — izslēgts (operatora lēmums 2026-10-12). scripts/morning_ingest.py noklusējums ir off (os.environ.setdefault; gate tests/test_morning_ingest_veto_default.py): divās ēnas nedēļās (2026-09-18…10-12) veto regulāri noraidīja īstus politiķus (backlog/dati-db.md § 2026-09-18 (a)). Ikdienas typesafe_veto_report.py vairs nav rutīnas solis. ATMINA_TYPESAFE_VETO=shadow|enforce joprojām pārspēj noklusējumu vienam procesam. Saišu priekšlikumi (5. solis, saites_proposals.py) TypeSafe lieto atsevišķi un paliek.
Read wiki/index.md for backlog/folder contract. The 11 steps (+ 2b «backlog») live in src/routine.py; the agents in .claude/agents/*.md.
Timing guardrails (HARD)
- Ingest runs all day. A morning "0/N analizēti" is EXPECTED, not a backlog warning (CLAUDE.md § Standing Decisions — Timing).
- Analysis + brief are afternoon-only (≥15:00 LV). Do NOT dispatch
@brief-writeror run extraction before ~15:00 — 2026-05-15 incident: a brief dispatched before noon was rejected.
Steps (afternoon)
-
Ingest — confirm new documents landed (step shows count). Don't force it in the morning.
scripts/morning_ingest.pyexit code1/KOPSAVILKUMS: N/5below 5 = a step failed (named in the summary) — re-run it; exit 0 alone is the clean signal. 1b. Vēstneša paraksta-pāri (pirms ekstrakcijas plāna) —.venv/Scripts/python.exe scripts/stamp_vestnesis_signatures.py(dry-run). Ja paraksta-pāru > 0:--write-rollback→ commitēdata/rollback_vestnesis_stamp_<datums>.sql(git commit -F) →--apply(atsaka bez commitēta rollback vai ar citu pāru kopu). Raksta tikai pāraextracted_at, nekadreviewed_at(src/vestnesis_stamp.py). -
Pozīciju analīze — fan out
@claim-extractorin parallel sub-agents. Nepako ar roku: palaid.venv/Scripts/python.exe scripts/plan_extraction.py --days 2un dispatchē pa tā rindām — katrs#Nir viens apakšaģents,kārta Ksecība ir obligāta (nākamo kārtu sāc tikai pēc iepriekšējās pabeigšanas),rtaģenti (tīrie «RT @» doki) iet pēdējā kārtā. Noteikumi (08-19 pakošana ≤2 doki līdz 8 dokiem, 08-25 viens pid = viens aģents kārtā, >12 doki → secīgas kārtas, 09-23 RT pakošana, 10-08 ≤10 aģenti kārtā) dzīvosrc/extraction_plan.py; kopsavilkuma rindu rāda arīprint_routine. Kopš 2026-10-08 plāns katrā kārtā ieliek ne vairāk kā 10 paralēlus aģentus, jo katrs aģents ielādē embedding modeli (10-07 rutīnā 19 aģenti vienā kārtā izsmēla atmiņu); pārējie pāriet uz nākamo kārtu. Ja aģentu atskaitēs apstrādāto pāru kopskaits nesakrīt ar plānapāri, STOP un salīdzini. Each claim still carries its own politician'sopponent_id— cross-attribution inside a shared agent is the batch failure mode to watch. Scale wide; if diacritic validation trips in any context, stop that context and start a fresh one (CLAUDE.md T4). Circuit breakers: cap batch size (≈12 docs / 12 politicians) + theNEEDS_REVIEWgate to avoid indirect-reference saves (kanoniskie noteikumi:.claude/agents/claim-extractor.md). Every claim needs asource_urlor it is dropped — and the drop IS reported, as amissing_source_urlentry insave_analysis()'s returnedfailureswithstatus="partial". It goes to stderr and never raises, so it is easy to miss but not silent: readfailuresand treat every entry as a real lost claim (T3). Kopš 2026-09-30save_analysis()atsaka arī pozīciju bez burtiskasupportfragmenta no ŠĪ dokumenta (missing_support/support_not_in_source—src/support.py); aģentam tā jāizlabo un jāsaglabā vēlreiz, un atskaitē saglabāto skaitam jāsakrīt ar iecerēto. 2b. Veci neizskatīti pāri — ja solis «2b» ir ◐ (vairāk parSTALE_PAIRS_MAX= 20 vecu pāru), palaid.venv/Scripts/python.exe scripts/plan_stale_sweep.pyun dispatchē pa plāna gabaliem tikai Opus@claim-extractorar brīfuwiki/operations/stale-sweep-brief.md(≤12 doku gabalā). Runbook:wiki/operations/operacijas.md§ Veci neizskatīti pāri. 2c. Dienas NEEDS_REVIEW pozīcijas — pēc ekstrakcijasprint_routinerāda «Jāpārbauda šodien: N». Katru no tām izvērtē ar advisor. SkaidrajāmreasoningmarķieriNEEDS_REVIEW:aizstāj arIzvērtēts YYYY-MM-DD: <pierādījums no avota>. Sākotnēji: …(sākotnējā tekstā marķiera vārdu neatkārto — trigeris rindu atkal atzīmētu), ar pāra rollback (escalation 8). Neskaidrās (apmelošanas risks, neatšifrējams konteksts) atstāj operatoram: skelets tās izlaiž pats un nosauc komentārāNEEDS_REVIEW IZLAISTAS.Divi pastāvīgi soļi (institucionalizēti 2026-08-20 pēc 08-17 eksperimentiem + 08-19 pilnā palaidiena):
-
±5 d aģentūras dublikātu pārbaude — katrā dispatch promptā. Pirms glabāšanas aģents pārbauda
get_existing_claims(pid, stated_around=<raksta datums>)(logs pēcstated_at±5 d, ne pēccreated_at— kopš 2026-09-24) — tas pats izteikums no cita avota jau glabāts → NEGLABĀ, piemin atskaitē. Drošības tīkls pēc glabāšanas:save_analysis()atgrieztajāfailuressarakstā ierakstspossible_duplicate(tikai ziņo, nebloķē) — aģents to nolasa un izvērtē atskaitē (logs ±5 d ir operatora lēmums 2026-08-18; ±2 d bija par šauru — Sprūda #689743 izslīdēja). Zināmās robežas: neiet RT-skip slotos un neredz cita pid dublikātus. 08-19: ≥5 reāli dublikāti noķerti, 0 FP. -
Junction-atgūšana PĒC ekstrakcijas (orkestratora solis). Citētie runātāji ar
role='mentioned'nekad neienākget_pending_politiciansrindā (reviewed_at ir per-dokumenta) — pēc galvenā fan-out palaid apsekojumu un dispatchē atgūšanas aģentu atrastajiem pāriem:.venv/Scripts/python.exe scripts/recovery_survey.py --days 2 # --json mašīnlasāmai izvadeiTas ir agrākais rokas SQL kodā (
src/quoted_speaker.py::recovery_survey, kopš 2026-09-24): joslainversion=pending_quoted_mentioned(runā tikaimentioned), joslabeside_subject= runājošsmentionedblakus subjektam (extracted_at IS NULL, bezinactive/journalist/organization, nominatīva forma ± citāta signāls),aklā zona= doki bez subjekta (tikai skaits). Rinda arpārbaudīti N pārirādās arīprint_routine()izvadē. Kāpēc tikai inversijas josla nepietiek: 09-23 vakarā tā deva 0, bet atgūšana saglabāja 9 pozīcijas no 11 pāriem — visi dokos, kur runāja arī subjekts. Jautājuma verbi: kopš 2026-09-24 atgūšanas apsekojums skaita arī «jautāja/jautā/prasīja/prasa/sprieda/norādot» (RECOVERY_EXTRA_SIGNALS, tikai atgūšanai), tāpēc deputāts, kas sēdē tikai jautā, parādās; auditafind_inversionsbāzes signālu sarakstā (CITATION_SIGNALS) šo verbu joprojām nav — tas apzināti iesaldēts. Platformu kods nefiltrē, tāpēc parādās arīvestnesispāri (MK sēžu protokoli) — Vēstnesis ir ārpus ekstrakcijas rindas, un lēmums par tiem ir operatora, ne atgūšanas aģenta. 08-17 raža: 4 pozīcijas no 16 pāriem.Tavs apsekojuma vaicājums NEDRĪKST atkārtot koda filtru. 2026-08-26 tas to izdarīja — orkestrators pats rakstīja
platform='web', tieši to pašu, kas tolaik bijafind_inversionsiekšā —, un LTV1 līderu debašu vakars aizgāja garām: astoņi releja tvīti ar atribuētiem izteikumiem, 0 claims. Divi vārti ar vienu aklo zonu nav divi vārti. Kods kopš 2026-08-27 sedzweb+twitter+x_mention(DEFAULT_INVERSION_PLATFORMS), unrecovery_survey()platformu nefiltrē vispār — ja raksti papildu vaicājumu, arī tu nefiltrē; joslas salīdzini pēcby_platform, ne izslēdz.Un tvītu joslu lasi citādi nekā web joslu: 08-27 mērījumā ~puse jauno tvītu atradumu bija viltus pozitīvi, un tos dominē SATĪRA (izdomāti «citāti» īstiem politiķiem). Strukturāla filtra tam nav — satīras kontu tur
relationship_type='tracked'deputāts. Šķirotājs ir@claim-extractorsatīras/RT vārti; nekad neuzskati šīs joslas pāri par apstiprinātu pozīciju.
Model pin — pass it explicitly on every fan-out. All project agents carry
model: opusin frontmatter, but that pin does not reach a plainagentType: 'general-purpose'call, and sub-agents must never inherit a Mythos-tier session model (CLAUDE.md § Standing Decisions, operator decision 2026-07-21). When dispatching viaWorkflow/agent(), passmodel: 'opus'on each call; when dispatching a named project agent viaAgent, the frontmatter pin covers you. Orchestration itself may run on any tier. -
-
Pretrunu pārbaude — for each new claim,
search_similar_claimswith a directionalclaim_type_filter; candidates go to@devils-advocateFIRST; store only survivors viasrc.tools.store_contradiction(..., reviewed=True, headline=…)(headline— konkrētais jautājums OG kartītei, ≤60 zīmes, bez vērtējuma) (confirmed=0— unpublished until the operator confirms; CLAUDE.md escalation 3). Also check rhetoric-vs-action. See/deep-checkfor the deeper fan-out. Pēc medību pabeigšanas — arī pie 0 atradumiem — pieraksti izpildes pēdu (store_contradictionraksta tikai atradumus; bez šīs rindas godīgā nulle nav atšķirama no „netika palaists", un_check_contradictionsziņosmissing):Tvērums no datiem (kopš 2026-10-15, audits
docs/audits/2026-10-15-pretrunu-parklajums/). Pirms medībām:.venv/Scripts/python.exe -c "from src.db import get_db; from src.briefs import current_routine_day; from src.routine import hunt_scope; print(hunt_scope(get_db(), current_routine_day()))"voter_idssadali grupās pa ≤5 un dispatchē ⌈balsotāji/5⌉@contradiction-hunterparalēli, katram SKAIDRU pid sarakstu ar viņu dienas claim id — nekad «aktīvākos» (tā 30 d palika nepārbaudīti 13 balsotāji). Cena ≈ 39k Opus tokenu uz politiķi (~600k dienā pie ~16 balsotājiem). Pēdu raksti TIKAI tā:.venv/Scripts/python.exe -c "from src.routine import log_hunt_trace; log_hunt_trace(claim_ids=[...], politician_ids=[...], found=<M>, rejected_candidates=[...])"claim_ids= pozīcijas, kurām izpildīta embedding pāreja (ekstrakcijas aģenti);politician_ids= balsotāji, kuriem hunter izpildīja strukturālo pāreju.dateNERAKSTI ar roku — to liekcurrent_routine_day()(01:20 naktī ar roku rakstīts datums iezīmēja nākamo dienu kā izpildītu,logs#656431).print_routinerāda «pozīcijas N/M, balsotāji K/L»;partial= vēl jāpārbauda nosauktie balsotāji. Ārpus rutīnas medības:day='<darbs>-YYYY-MM-DD'.rejected_candidatesnav neobligāts (kopš 2026-09-04, operatora lūgums). Kandidāti, ko aģents izvērtēja un noraidīja, līdz tam dzīvoja tikai apakšaģenta atskaitē — tā aiziet orkestratoram un pazūd līdz ar sesiju; 09-04 tā pazuda seši pilnībā izvērtēti kandidāti ar pamatojumu. Operators tos lasaserve.pypanelī «Pretrunu kandidāti» un reizēm izlemj kādu publicēt. Lauku līgums un piemērs:.claude/agents/contradiction-hunter.md§ Step 5 —claim_newunwhyir obligāti, pārējie papildinājumi; atslēgu nosaukumus nemaina (vārts:tests/test_dashboard_candidates.py). -
Devils-advocate — already done inside step 3 (verify-then-store, 2026-10-12). Here only rows
print_routinestill shows as unreviewed (stored withoutreviewed=True, e.g. by hand) go through@devils-advocate. -
Spriedzes — first run
.venv/Scripts/python.exe scripts/saites_proposals.py --days 1(TypeSafe pass; writes onlytension_proposals+tension_judged). Review each pending proposal against its source document: real link →scripts/saites_accept.py <id> --topic "<kanoniskā tēma>" --description "<latviski, bez #NN>" [--type spriedze|uzbrukums|atbalsts]; not a link →scripts/saites_accept.py <id> --reject. The model'srelationis a hint only —spriedzevsuzbrukumsis your call. Then register any further tensions you find as before (≥2 politicians with new positions). The routine reporter shows pending proposals in the Spriedzes line. -
Konteksta piezīmes (tendences) — append-only; check existing notes first, never overwrite (destroys the evolution signal). Piezīmes teksts iziet B formu (≤120 vārdi, 4+ nosaukti aktieri sarakstā, ne semikolu virtenē) — tā nonāk pārskatā verbatim kā
<div class="context-box">, tāpēc gara virtene šeit kļūst par palagu publiskajā lapā (2026-09-01 § Imigrācija: 206 vārdu piezīme). Sk.brief-shared-rules.md§ Prozas bloku forma. -
Dienas pārskats —
@brief-writer. Same-day refresh = UPDATE the existingdaily_briefrow, never a full rewrite (feedback_daily_brief_update_not_rewrite); checkcontext_notesfirst. Re-saving an EARLIER day's brief is not free: a backdated claim counts in one day only, and_BRIEF_DAY_CLAIM_SQL(src/briefs.py) drops it from today's brief once itsstated_atday has a brief written or refreshed AFTER the claim'screated_at— so re-saving yesterday's brief silently removes such claims from today's. -
Featured image —
@graphics-designeronce the brief is approved. -
Wiki sync —
wiki_sync(); never hand-edit generated wiki pages.
LV-style guardrails (every claim / brief / synthesis / tweet)
Avoid anglicisms and check grammar + stylistics before store/publish (feedback_check_grammar_stylistics): never "ataka" (→ uzbrukums), "polemika" (→ diskusija/domstarpības), "melīšana" (→ melošana). Diacritics intact.
Publish gate (HARD — do NOT auto-publish)
Before any deploy of a brief: manual proofread the full text (verb forms, sg/pl, truncations, capitalization — lint_lv_style 0-issues is NOT sufficient — run @quality-reviewer § H «Valoda un lasāmība» and quote its table with the denominator; since 2026-09-06 that section is the proofread artefact, not the orchestrator's memory), confirm the featured image, then AskUserQuestion for go-ahead (feedback_brief_publish_pause). Never auto-publish.
Quality gate (HARD — before render, not after)
Run bash scripts/check.sh, then dispatch @quality-reviewer over the day's output. CLAUDE.md § Quality Bars names both as the render+deploy carrier and calls the reviewer a hard gate: it is the last check on data integrity, source links, completeness and neutrality before anything reaches the public site. A green check.sh alone does not clear this step — it validates the build, not the editorial content.
If the routine wraps up after 05:00 LV, or you review an earlier day, tell the reviewer which day it is reviewing. Its first code block binds ROUTINE_DAY = current_routine_day() (05:00 LV boundary, since 2026-09-25) and every check keys off it, so a run between 00:00 and 05:00 already reviews the right day. The default is wrong only when the routine wraps up after 05:00 or you review an earlier day — then pass ROUTINE_DAY explicitly (26 of 130 stored briefs have a subject day that differs from their created_at day). Say the covered date explicitly in the dispatch (ROUTINE_DAY = '2026-08-14'), the same way check_routine(target_date) takes it. Two related behaviours since 2026-08-15, both deliberate: the reviewer resolves the brief through src.routine._daily_briefs_for() (subject date, not created_at), and a missing brief is now a FAIL, not a silent pass — if it reports "pārskats nav atrasts" on a day you did run the routine, the day you passed it is wrong, not the brief.
Render + deploy
-
Identify the changed domains and render narrowly:
.venv/Scripts/python.exe -m src.render --only=DOMAIN1,DOMAIN2(full path only for release/baseline regen —feedback_render_narrow_scope). After approving the featured image, the render MUST includedashboard— the landing page andanalizes.htmllisting cards live in that domain, so ablog-only re-render publishes a brief whose image shows only on the brief page itself (bitten live 2026-08-18). Always includestatic—about.html's corpus figures andsitemap.xmlare emitted by that domain alone, so omitting it silently publishes stale counts and a sitemap missing the day's brief (~5s; 2026-08-01 audit found about.html three days stale). -
Pēc korektūras un operatora atļaujas — ieraksti publicēšanas karogu:
.venv/Scripts/python.exe scripts/approve_publish.py <YYYY-MM-DD>(nedēļas pārskatamnedela-<YYYY-MM-DD>). Kopš 2026-08-18 deploy preflight prasapublish_approvalsrindu KATRAIblog/pārskata lapai — attēla apstiprinājums vairs nav pietiekams, jo tas pierāda tikai hero izvēli, ne to, ka teksts drīkst iet ārā. Bez rindas deploy apstājas arpublish-gate: ... nav publicēšanas apstiprinājuma. Atsaukšana:--revoke. Atļauja der vienam publicējumam — pārģenerēts brief tajā pašā dienā PATUR rindu (atslēga ir lapas slugs), tāpēc pēc būtiskas pārrakstīšanas atsauc un apstiprini no jauna. -
Attēla variantu vārti (quality-bars § Dienas pārskats — „varianti eksistē un live atgriež HTTP 200"). Šis kritērijs līdz 2026-09-05 nebija nevienā promptā vai prasmē;
src/graphics/cli.pyvariantu ģenerēšanas kļūmi apzināti norij (# never block approval on variant gen), tāpēc vienīgais nesējs ir šis solis. Divi mērījumi, abi ar saucēju.(a) Pirms deploy — vai visi četri varianti ir uz diska?
_ensure_image_variants()tos ģenerē rendera laikā, tāpēc palaid to PĒC narrow rendera:.venv/Scripts/python.exe -c " from pathlib import Path from src.image_variants import VARIANTS, variant_filename DAY = '2026-09-05' # rutīnas diena, ne kalendārā # Pamata PNG dzīvo ārpus deploy saknes (output/images/briefs); renders uz # output/atmina/images/briefs kopē TIKAI variantus — PNG glob deploy kokā # deva 0 un nokāva vārtu (2026-09-06), tāpēc PNG meklē avota mapē. src = Path('output/images/briefs') out = Path('output/atmina/images/briefs') pngs = sorted(p for p in src.glob(f'{DAY}*.png')) print(f'pamata PNG: {len(pngs)}') assert pngs, 'nav neviena PNG — attēls nav ģenerēts vai diena nepareiza' missing = [variant_filename(p.name, v) for p in pngs for v in VARIANTS if not (out / variant_filename(p.name, v)).exists()] print(f'attēlu varianti: pārbaudīti {len(pngs) * len(VARIANTS)}, trūkst {len(missing)}') assert not missing, missing "Trūkstošs variants = renders nav pieskāries
blog/dashboarddomēnam vai--note-idpalaists ārpus rendera. Pārrenderē, nekopē failus ar roku.(b) Pēc deploy — vai live atbild 200?
curl -Ikatram hero + og + card + thumb URL; jebkas cits par 200 ir 404-hero klase, kas 2026-08-02 divos publicētos pārskatos palika nepamanīta:DAY=2026-09-05 # rutīnas diena, ne kalendārā B=output/atmina/images/briefs for f in $B/$DAY*-hero.webp $B/$DAY*-og.jpg $B/$DAY*-card.webp $B/$DAY*-thumb.webp; do u="https://atmina.lv/images/briefs/$(basename "$f")" code=$(curl -s -o /dev/null -w '%{http_code}' -I "$u") echo "$code $u" doneZiņo SKAITU, ne tikai atradumu: „pārbaudīti N URL, 200: N, cits: 0". Denominators 0 vai 1 nozīmē, ka vārti ir salauzti, nevis ka viss ir tīrs (CLAUDE.md § „A gate that cannot fail is not evidence").
-
Deploy with
bash scripts/deploy.sh --no-delete(Cloudflare since 2026-09-16: ALWAYS the full local tree;--no-deleteis an accepted no-op kept for consistency. A narrow render leaves other domains' pages in the tree from the previous build, so the tree stays complete — never delete fromoutput/atmina/by hand before a deploy). After deploy:.venv/Scripts/python.exe scripts/verify_host.py --base https://atmina.lv→ expect visas zaļas (N/N). A real deploy writeslogs.action='deploy'(version id), so routine step «11. Deploy» turns ✓;missing/stalethere = the approved brief is not live yet. Dry-run first:deploy.sh --dry-run --no-delete. -
New EXTERNAL resource host → CSP allowlist, or it dies silently on the live site. The live
script-srchas no'unsafe-inline', and every external host (script, style, font, image,connect) must be added to the allowlist inassets/_headers. Local preview serves no headers, so it cannot catch this — the resource simply fails in the browser after deploy, with no build error and no test failure. Two tests cover the source side:tests/test_no_inline_js.py(inline-JS half, all 36 templates + curated pages) andtests/test_csp_external_hosts.py(external-host half — since 2026-08-15 it also scanscurated/and the vendoredassets/cuelume/, which it previously skipped). Neither can verify the LIVE headers or a host that only appears at runtime, which is why the manual confirmation below stays in this checklist. If today's render touched a template, a curated page orassets/*, confirm no new external host appeared before deploying, and verify it live after (CLAUDE.md § No inline JavaScript).
