Module-architectuur & uitwerking

Werk-model: de lagen, de bestanden en de doctrine, 1-op-1 geverifieerd tegen de code. Stand 3 juni 2026. Repo schwung-contentmotor → contentmotor.schwung.ai · publiceert naar Schwung.ai → schwung.ai/blog.

🔁 Engine — project-agnostisch 🔧 Project-specifiek (doctrine/config) ⚙️ Infra / extern 👤 Mens-poort

De keten — van idee tot live

1 · Trigger
Cron ⚙️cron-auto-suggest.js

schedule 0 7 * * 1,3,4,5 (ma/wo/do/vr 09:00 NL). Draait per project alleen als settings.cadans_per_week vandaag triggert; roteert bron-type. Maakt idee + run, triggert generate-background.

Handmatig 👤generate.js

Mens postt kern + invalshoek (+ inspiratie-doc). Maakt idee + run, triggert generate-background.

2 · Ideatie — de vonk 🔧
auto-idee-generator.js

Alleen in de auto-stroom. selectDomein() trekt random een van de 5 domeinen (gelijke kans); laadt domein-bewust (alleen het getrokken domein + onderwerpen-matrix, niet alle merk-bestanden); selectBronType() rouleert doctrine/scan_data/klantcases; reageert tegen een levende take, uitwisselbaarheidstest, geen plagiaat, dedup tegen recente titels. Levert {kern, invalshoek, vijand, domein, template}.

3 · Pijplijn — de motor 🔁 engine · generate-background.js → runPipeline()
1 Concept

Sonnet · skelet (H1, koppen, these)

2 Onderzoek

Sonnet + web_search · feiten mét bron

3 Draft

Sonnet · body, Frankwatching-toon

4 Polish

Opus · tegenlees-poort

5 Format

Sonnet · LinkedIn, title-tag, beeld-trigger

6 Schema

schema.org JSON-LD

pipeline.js: schoonModelMarkdown() stript front-matter / H1 / <mark> / em-dash · bronnenblok deterministisch uit feiten · anthropic.js = Sonnet 4.6 (draft) / Opus 4.7 (polish) · template-instructie uit _lib/templates/<type>.md (artikel·faq·definitie·vergelijking).

↓  wordt gevoed door de doctrine  ↓
Doctrine-laag 🔧 · doctrine-loader.js leest alleen wat in project.yaml staat, met prompt-cache
blog-delta.md — LEIDEND

Frankwatching-denkstuk op de canon; zin/onzin- + tegenlees-poort; geen em-dash; lengte 1.000-1.500

craft-frankwatching.md · scherpte.md

toon/ambacht · duiding boven beschrijving

toon · cliche-blacklist · bronvermelding · geo · onderzoek

schrijven/

domeinen/* · merk/pilaren/* · merk/routes

terrein + inhoud

output-format.md

= merkREFLECTIE-vorm — NIET voor blog

DOCTRINE-HERKOMST.md: deze map is een kopie/snapshot van de merkscan-doctrine, bewust ontkoppeld; handmatige sync. Runtime leest de motor uitsluitend deze map.

4 · Preview & poort 👤 mens beslist · preview.html + status.js (mail met token-link)
🚀 GOpublish.js

→ publiceren

✏ Pas aan — 3 niveausrevise.js

tekst · structuur & argument · volledig + nieuw onderzoek

✕ Verwerp
↓ Pas aan  |  ↓ GO
5 · Feedback / revise 🔁 · revise.js → revise-background.js
tekst

runRevisiePipeline · polish, behoudend

structuur & argument

runRevisiePipeline · polish met volle vrijheid (koppen, these, titel)

volledig

runPipeline · hele pijplijn opnieuw, vers onderzoek; feedback in invalshoek

Titel komt via een TITEL:-regel terug in concept_output, zodat publicatie + mail de nieuwe kop oppakken.

6 · Output & publicatie
GitHub-adapter 🔁 swappableadapters/output-github.js

Direct commit (geen PR) src/blog/<slug>.md + front-matter (triggerWoord, kicker, keywords) + cover-png naar Schwung.ai.

Netlify build ⚙️

Eleventy → schwung.ai/blog (~90s)

Social ⚙️link-linkedin.js

LinkedIn-URL terugkoppelen aan artikel

Bestand-map — functions (14)

FunctionTypeDoetHangt af van
cron-auto-suggest.js⚙️ cronDagelijkse auto-stroom; cadans-check; maakt idee+run; triggert pijplijnsupabase, auto-idee-generator
generate.js👤Handmatig idee → run → trigger backgroundsupabase
generate-background.js🔁Draait runPipeline, slaat run op, preview-mailpipeline, supabase, mail
revise.js👤Maakt revisie-run (scope), triggert background (token)supabase, preview-token
revise-background.js🔁runRevisiePipeline (tekst/structuur) of runPipeline (volledig)pipeline, supabase, mail
publish.js🚀Commit naar GitHub (token), bouwt front-matter, published-mailsupabase, output-github, preview-token, mail, doctrine-loader
link-linkedin.jsLinkedIn-post-URL koppelen aan gepubliceerd artikeloctokit, supabase, preview-token, doctrine-loader, output-github
status.jsPolling: volledige run-data voor de previewsupabase
queue.jsAI-voorgestelde ideeën (queued/generating/draft)supabase
history.jsAlle ideeën (incl. gepubliceerd/verworpen)supabase
settings.js🔧GET/PUT project-settings (cadans, woordcount, toon, AI-bronnen)supabase
extract-text.jsTekst uit een geüpload inspiratie-document
refresh-sitemap-cache.js⚙️ cronWekelijks (0 4 * * 1): ververst de schwungreclame.nl cross-link-cachesitemap-cache
debug.jsInspecteert filesystem, env-vars en doctrine-loadernode:fs

Bestand-map — _lib (engine + helpers)

ModuleTypeRol
pipeline.js🔁runPipeline (6 stappen) + runRevisiePipeline (scope) + schoonModelMarkdown + splitTitelEnBody + bronnenblok
doctrine-loader.js🔁loadProject / loadDoctrine (leest project.yaml) / buildSystemPrompt (cache_control)
anthropic.js🔁callClaude; Sonnet 4.6 / Opus 4.7; web_search; kosten-telemetry; extractJson
schema-builder.js🔁buildSchemaOrg (Article/FAQPage/Definitie) + validatie
auto-idee-generator.js🔧bedenkAutoIdee, selectDomein, selectBronType, shouldTriggerToday
adapters/output-github.js🔁publishToGithub (direct commit of PR) + slugify + front-matter-builder
sitemap-cache.js⚙️loadVerifiedCrossLinks uit externe_paginas_cache (7-daagse refresh)
preview-token.js⚙️HMAC-token: beveiligt revise / publish / link-linkedin
supabase.js⚙️DB-client + helpers (getRun, insertRun, updateRun, insertIdee, updateIdee, getProject)
mail.js⚙️sendPreviewMail / sendPublishedMail / getNotifyEmail
templates/*.md🔧4 template-types: artikel (denkstuk-default), faq, definitie, vergelijking
blog-template/*🔁Eleventy-installatieseed (blog-artikel.njk, blog.njk, blog.11tydata.js) voor Schwung.ai

Data & extern ⚙️

Supabase — 5 tabellen

  • projects — slug, naam, actief, settings (cadans_per_week, word_count, toon_spectrum, ai_bron_*)
  • ideeen — kern, bron, status, template, social_keuzes, inspiratie
  • runs — pipeline_status, pagina_markdown, concept_output, onderzoek_output, schema_org, kosten
  • auto_runs — logboek van de cron-stroom
  • externe_paginas_cache — geverifieerde schwungreclame.nl-links (cross-links)

Externe diensten

  • Anthropic — Sonnet 4.6 (concept/onderzoek/draft/format) · Opus 4.7 (polish) · web_search · prompt-cache
  • GitHub — Schwung-nick/Schwung.ai (publicatie via Octokit)
  • Netlify — host, 2 cron-schedules, Eleventy-build
  • LinkedIn · mail (preview/published)

De regels (doctrine in het kort)

Methode/keten met poorten: vonk (mens: domein + levende take) → zin/onzin-poort → onderzoek → opzet → interne pitch → schrijven → tegenlezen → opleveren. Mens beslist op de vonk en het eindoordeel.
Natuurlijk Nederlands vóór punch — overruled alle stijl-devices. Geen geforceerde "X, geen Y"; geen em-dash als interpunctie.
~90% vakinhoud, ~10% beleving — mechanisme/model/how-to; aangrenzende discipline als analyse-instrument, geen metafoor.
Koppen 3–9 woorden, inhoudswijzers — lees alleen de koppen = ken het stuk. Geen "Wat opvalt".
Scherpte = duiding boven beschrijving · één these met een echte vijand · sterkste tegenwerping verslagen.
Vijf domeinen: branding · arbeidsmarkt · marketing · creatie · web-ai (web-ai is het te-remmen domein).
Geen harde sell — nette merk-nudge mag. Primaire bronnen, nooit verzonnen cijfers.
Kopie-model: doctrine = snapshot van de merkscan-canon, ontkoppeld; runtime leest alleen de eigen map.

Wat kan los — voor herbruikbaarheid

De grote scheidslijn zit al in het ontwerp: een project-agnostische engine tegenover een per-project doctrine/config. Kandidaten om los te trekken:

BlokNuLos te trekken als
Engine 🔁pipeline.js + doctrine-loader.js + anthropic.js + schema-builder.js@schwung/contentmotor-core — package dat elk project draait
Doctrine 🔧doctrines/<project>/ + project.yamlConfig-contract: nieuwe klant = nieuwe doctrine-map, zelfde engine
Output-adapter 🔁adapters/output-github.jsAdapter-interface: GitHub, WordPress, of andere bestemming
Publicatie-pakket_lib/blog-template/* (Eleventy)Los "blog-thema"-pakket voor elke Eleventy-site
Review-UI 👤preview.html + status/revise/publishLosse review-app over projecten heen
Functions ⚙️netlify/functions/*Dunne HTTP-wrappers om de engine — vervangbaar per host

Kortom: de motor is al bijna een core + plug-ins-model. De winst van lostrekken zit in de engine (1× onderhouden, N projecten) en de adapters (andere bestemmingen zonder de engine te raken).