Statische site (datasignalslab.com) plus de volledige funnel-machine uit
PROJECTINSTRUCTIE_v4.0.md: prijsladder, landingspagina's, e-mail,
performancemeting, programmatische SEO, X-queue en zelfverversing.
- Bouwen:
python3 build_static.py(schrijftpublic/, draait linkcheck encheck_consistency.pymee — de build faalt bij elke prijsafwijking of kapotte interne link). - Single source of truth:
config/pricing.json. Alle prijzen, limieten, productnamen en checkout-URLs komen hieruit. Nergens hardcoded.
Drie plekken, één richting:
- Werkkopie
~/apify/landing— hier wordt ontwikkeld, op feature branches. Niets wat hier staat gaat live zolang het niet opmainis gemerged en gepusht:git push hub main. - Hub
~/apify/landing-hub.git— kale repo, de enige bron voor live. - Live-checkout
~/apify/landing-live— schone checkout vanmain. Elke cron-taak loopt viascripts/run_live.sh, die eerstfetch + reset --hard origin/maindoet en dan de taak in de live-checkout draait. Ookproducts/deploy_reports.shbouwt en deployt uitsluitend hieruit. Runtime-state (logs, outbox, queue, secrets, performancedata) is ge-gitignored en overleeft elke sync;auto:-commits van de onderhouds- taken worden vanuit de live-checkout direct naar de hub teruggepusht.
Iets live zetten = mergen naar main in de werkkopie en git push hub main;
de eerstvolgende geplande taak of deploy pakt het op. Direct deployen:
bash ~/apify/products/deploy_reports.sh (haalt zelf de laatste main op).
Pauzeren: touch ~/apify/landing-live/PAUSE — alle geplande taken en
deploys slaan dan over (met logregel). Hervatten: rm ~/apify/landing-live/PAUSE.
scripts/blog_writer.py schrijft elke maandag (04:30) twee nieuwe artikelen
headless via de Claude CLI (alleen tekst, geen tools of permissies) en zet ze
in _queue/. Onderwerpen komen uit system/blog_topics.md; zakt de backlog
onder de 5, dan vult de writer hem zelf aan — de motor is daarmee oneindig.
Harde kwaliteitslat: front matter, minimaal 1200 woorden, geen verboden
woorden/em-dashes/puntkomma's, minstens 2 officiële bronlinks en een
rapport-CTA. Wat de lat na één herkansing niet haalt wordt NIET geplaatst
(draft in maintenance/state/blog_drafts/, faalmelding via het alertpad).
De bulk-artikelen van 5 juli staan in _queue/; scripts/blog_queue.py
publiceert er dagelijks maximaal 2 (BLOG_PER_DAG) met de echte
publicatiedatum van dat moment. De bestandsnaam krijgt de nieuwe datum, de
slug en dus de URL blijven gelijk. Volgorde: onderwerp-afwisselend
(congress / 13F / Form D), vastgelegd in het script. Elke publicatie is een
auto:-commit die naar de hub pusht; de dagelijkse deploy van 06:20 neemt
hem mee. Wachtrij leeg = skip-regel in de log. De blogindex toont de zes
nieuwste als "Newest guides", daaronder het volledige archief; de sitemap
volgt de werkelijke publicaties.
Het mandaat staat in system/CHARTER.md; scripts/ronde_generator.py leest
repo, logs, onderhoudsrapport en funnelcijfers, schrijft daaruit zelf een
opdracht in system/rondes/<datum>-<modus>.md en voert die headless uit
binnen laag 1/2. Laag 3 wordt uitsluitend als voorstel-branch klaargezet
(lib_pipeline.propose). Verslag per ronde in system/rondes/verslagen/,
samengevat in de healthmail, inclusief de eerstvolgende zelfgeschreven
opdracht zodat de eigenaar kan meelezen.
Exact commando en planning (cron, via de live-checkout):
15 9 * * 1 run_live.sh scripts/ronde_generator.py --wekelijks (max 30 min)
15 10 1 * * run_live.sh scripts/ronde_generator.py --maandelijks (max 90 min)
De generator roept claude -p (Claude Code CLI, headless) aan met het
charter plus de opdracht, met harde timeout en
--dangerously-skip-permissions (nodig voor headless commits; vangrails:
wegwerp-live-checkout, charter-grenzen in de prompt, alles per commit met
prefix auto-ronde: terug te draaien). Faalt of ontbreekt de CLI, dan
blijft de opdracht plakklaar staan en meldt de healthmail dat de ronde
alleen is KLAARGEZET.
De eenvoudigste route die met de bestaande stack werkt, zonder accounts of server:
scripts/pro_area_build.pybouwt bij elke deploypublic/pro/<token>/met de actuele edities van alle drie de rapporten plus het maandarchief. Het token is onraadbaar en staat inproducts/_report_urls.json(buiten deze repo)./pro/staat op noindex en in robots.txt op Disallow.- Het Pro-product in Lemon Squeezy levert die URL als productinhoud, precies
zoals de snapshots dat met
/r/-URL's doen. Aanmaken van het product kan alleen in het dashboard (de LS-API is read-only voor producten) — zieTODO.md. scripts/report_monthly.pyarchiveert op de 1e van de maand de nieuwe edities met datum en mailt Pro-abonnees dat de nieuwe versie klaarstaat.- Pro MCP-toegang: de MCP-server draait op Apify; de gratis limiet en de
Pro-verwijzing staan in
config/pricing.json. Uitgifte van een persoonlijke Pro-key staat als stap inTODO.md.
Twee plekken, allebei zonder handmatige actie:
Apify-schedules (op het Apify-platform, aangemaakt en zelfherstellend via
scripts/apify_schedules.py):
| Schedule | Cron (UTC) | Doel |
|---|---|---|
| ds-congress-weekdays | 10 6 * * 1-5 |
Congress-actor, werkdagen |
| ds-formd-daily | 20 5 * * * |
Form D-actor, dagelijks |
| ds-13f-weekly | 30 5 * * 2 |
13F-actor, wekelijks |
| ds-13f-filing-season-feb-aug-nov | 40 5 14-28 2,8,11 * |
13F dagelijks na kwartaaldeadline |
| ds-13f-filing-season-may | 40 5 15-29 5 * |
13F dagelijks na deadline 15 mei |
Cron op deze server (crontab -l, tijden lokaal; alle taken lopen via
run_live.sh in de live-checkout; alles pauzeren = touch ~/apify/landing-live/PAUSE):
| Tijd | Taak | Doel |
|---|---|---|
| 05:45 dagelijks | scripts/performance_build.py |
performancedata verversen (voor de deploy) |
| 06:05 maandag | scripts/apify_schedules.py |
Apify-schedules controleren/herstellen |
| 06:20 dagelijks | products/cron_refresh_deploy.sh formd |
Form D verversen + site deployen |
| 06:30 20 feb/mei/aug/nov | products/cron_refresh_deploy.sh 13f |
13F kwartaalrefresh |
| 06:40 1e vd maand | scripts/report_monthly.py |
maandedities archiveren + Pro-mail |
| 06:50 maandag | products/cron_refresh_deploy.sh congress |
Congress verversen + deploy |
| 07:10 maandag | scripts/digest_weekly.py |
digest naar de Free-lijst |
| 05:10 dagelijks | scripts/streams_refresh.py |
alle alertstromen lokaal verversen (products/stream*.json) |
| 07:20 dagelijks | scripts/alerts_pro.py |
Pro-alerts over alle stromen, gebundeld tot 1 mail per dag |
| 07:30 dagelijks | scripts/x_queue.py |
X-conceptposts (ma: threaddraft) naar social/queue/ |
| 05:30 1e vd maand | scripts/backup_monthly.py |
backup abonneelijst + config |
| 07:45 1e vd maand | scripts/maintenance_monthly.py |
onderhoudstaak laag 2 |
| 08:30 1e vd maand | scripts/healthmail_monthly.py |
healthmail naar het alertadres |
| 07:50 maandag | scripts/smoke_chain.py |
ketentest zonder betaling (checkout, /r/, /pro/, outbox, robots) |
| 05:50 dagelijks | scripts/stockact_late.py |
STOCK Act late-filing stroom afleiden uit de congress-data |
| 05:55 dagelijks | scripts/confluence_build.py |
confluence-gevallen berekenen en archiveren (voor proof en deploy) |
| 06:00 maandag | scripts/lobby_contracts.py |
Senate LDA-lobbyfilings kruisen met de contractstroom (beta) |
| 06:05 dagelijks | scripts/proof_chain.py |
daghash van de signaalset ketenen en verankeren (git-mirror + OpenTimestamps) |
| 08:40 1e jan/apr/jul/okt | scripts/quarterly_report.py |
kwartaalrapport State of Congress Trading (pagina + PDF) |
Elke taak logt naar maintenance/log/YYYY-MM.jsonl en meldt falen (na drie
pogingen met oplopende wachttijd) maximaal één keer per pipeline per dag aan
het alertadres uit config/pricing.json. Zonder RESEND_API_KEY loopt die
melding via Telegram plus emails/outbox/.
- Laag 1, automatisch: retries in
scripts/lib_pipeline.py, verwijderde Apify-schedules worden wekelijks opnieuw aangemaakt, de build faalt op kapotte interne links en prijsafwijkingen. - Laag 2, maandelijkse onderhoudstaak:
scripts/maintenance_monthly.py(links, sitemap, laadtijd, patch-updates, concurrent-hercontrole met peildatum, consistentiecheck). Resultaten in de healthmail. - Laag 3, voorstellen: wijzigingen aan prijzen, claims, mailteksten,
disclaimers en grote upgrades voert het systeem nooit zelf uit. Ze komen
als aparte branch (
voorstel/...) met beschrijving klaar te staan en worden in de healthmail gemeld. Uitvoeren pas na goedkeuring van de eigenaar. - Automatische wijzigingen uit laag 1 en 2 committen met prefix
auto:.
De selectieregels van de performancepagina (scoredrempel, meetmoment,
horizonnen, benchmark) in config/performance_rules.json wijzigen onder
geen enkele voorwaarde automatisch — niet via laag 1, niet via laag 2, en
ook niet als laag-3-voorstel op initiatief van het systeem. De regels zijn
verzegeld met een sha256; scripts/performance_build.py weigert te draaien
als de hash niet klopt. Een systeem dat zijn eigen meetregels bijstelt, poetst
zijn eigen trackrecord op en maakt het bewijs waardeloos. Wijziging kan alleen
door de eigenaar zelf, die dan ook de verzegeling bijwerkt
(python3 scripts/performance_build.py --seal).