{ "_doc": { "purpose": "Schema for edition-state.json — deterministic resumption state for a newsletter edition in production. Holds the current phase + per-article status so /linkedin:newsletter (Step 0) can resume exactly where a prior session stopped.", "decision": "G — production state lives in the serie-mappe (e.g. /Users/ktg/repos/maskinrommet/serier//linkedin/edition-state.json), NOT in the plugin. This file is the schema-defining TEMPLATE only; copy + fill it in the serie-mappe when producing an edition.", "complements": "edition-config.json (static: calendar, freshness, captions) and /STATE.md (human-readable narrative state, overwritten each phase per the ONE-system continuity rule — there is no edition-HANDOVER.md). edition-state.json is the machine-readable companion: deterministic resumption + the durable fact-check log, immutable rules, and persona verdicts that the old HANDOVER §4/§5 used to carry.", "lifecycle": "/linkedin:newsletter reads this in Step 0 and rewrites it at every phase transition. Article keys mirror edition-config.json (zero-padded strings: \"01\", \"02\", ..., or \"samle\").", "phases": [ "load-context — read /STATE.md, voice profile, persona library, series brief (Step 0)", "brief-calibration — angle, voice, audience personas, key points, leader-takeaway (Step 1)", "research — parallel scoped mandates → verified notes, triangulation (Step 2)", "skeleton-pitch — five-line skeleton (premise/problem/recommendation/payoff/forward) + section pitches, operator gate + persona-skjelett-sweep BEFORE prose (Step 2.5)", "spine-prose — one paragraph per section against the gated skeleton, operator gate BEFORE full expansion (Step 3a)", "draft — full prose expansion against the gated spine; may span sessions (Step 3b)", "consistency-quality — threads, premise→conclusion arc, AI-slop removal, formatting dose (Step 4)", "factcheck-sweep — risk-sorted, guilty-until-disproven, verification log (Step 5)", "persona-sweep-prelock — reader jury, primary wins, convergence to clean YES (Step 6)", "annotation — optional annotatable review HTML for a manual pass (Step 7)", "visual-assets — cover (+ optional inline figures) or carousel deck: brief → generate → operator-gate → approve, BEFORE lock so build-linkedin.mjs picks them up (Step 7.5)", "lock-delivery — LOCK → POST.html all-in-one-place deliverable (Step 8)", "hook-conversion-gate — persona gate on distribution text post-lock: would YOU click? (Step 9)", "scheduling — register edition in plugin queue/state for native LinkedIn scheduling (Step 10)" ], "articleStatusValues": ["pending", "in-progress", "locked", "scheduled"], "visualAssets": "Per-article visual-asset record written by Step 7.5 (visual-assets phase). Runs BEFORE lock because render/build-linkedin.mjs picks up linkedin/NN/cover.png + the edition-config credit/caption when it builds POST.html — generating images after lock would force a re-render. Shape: { format: \"standard\" | \"carousel\"; cover: { brief, route, candidates[], approved, status }; figures: [ { id, brief, placement, status } ]; carousel: null | { source, pdf, status } }. format \"standard\" = cover + optional inline figures (cover.png is mandatory per the KTG cover-directive); format \"carousel\" = typografisk deck via render/build-carousel.mjs instead of cover+inline (cover/figures stay empty). route: \"mcp-image\" (default, via mcp__mcp-image__generate_image) | \"external\" (DALL·E / Midjourney / photographer → linkedin/NN/cover-raw.png). status ladder: pending → briefed → generated → approved. candidates[] holds the cover-v-kandidat.png attempts; approved is the fixed approved name (\"cover.png\") once the operator-gate passes. figures[].id = \"fig1\"..; placement = section reference in NN-utkast.md (figures are referenced in the draft via ![alt](linkedin/NN/figN.png) and uploaded manually in the LinkedIn editor — build-linkedin.mjs does NOT embed them). Naming convention: cover.png (approved, fixed — what build-linkedin.mjs reads) | cover-v-kandidat.png (attempts) | cover-raw.png (optional external pre-edit source) | fig.png (inline). credit + caption are recorded in /linkedin/image-credit-caption.md and flow into edition-config.json coverCredit + captions[NN]." }, "schemaVersion": 1, "series": { "slug": "", "title": "" }, "currentArticle": "01", "currentPhase": "load-context", "updatedAt": "", "articles": { "01": { "title": "
", "phase": "load-context", "status": "pending", "immutableRules": null, "factcheckLog": null, "personaSweep": { "skeleton": null, "resonance": null, "conversion": null }, "visualAssets": { "format": "standard", "cover": { "brief": null, "route": null, "candidates": [], "approved": null, "status": "pending" }, "figures": [], "carousel": null }, "locked": false, "scheduled": null } } }