fix(linkedin): close dogfood friction (S14)
Close all 9 friction points from the S13 newsletter dogfood (operator elected to fix F6-F9 rather than defer): - F1: namespace all subagent_type calls in newsletter.md to linkedin-thought-leadership:<name> (4 sites + canonical note) - F2: document agent invocation form + reload requirement in CLAUDE.md + README.md (reload itself is an operator action) - F3: add edition-config / edition-delingstekst / edition-HANDOVER templates under config/ + wire into Steps 0 and 8 + footer - F4: reconcile draft path to <serie>/NN-utkast.md (series root) - F5: de-hardcode series root (explicit arg / LTL_SERIES_ROOT / default) - F6: config-derive carousel editions (remove Seres CAROUSEL set); correct samle comment - F7: build-html.mjs exits non-zero when zero HTML produced - F8: guard parseDelingstekst (graceful ENOENT) + correct Step 8 wording - F9: relocate agents/README.md -> docs/agents-capability-matrix.md Re-tested: 87/87 plugin tests pass; build-html/build-linkedin behavior re-verified live. Per-item outcomes logged in dogfood-S13-friction.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
adfa2085fc
commit
92e0a0b4f5
11 changed files with 339 additions and 54 deletions
|
|
@ -0,0 +1,56 @@
|
|||
<!--
|
||||
TEMPLATE — edition-HANDOVER.md (narrative production state for one edition)
|
||||
|
||||
Purpose : the human-readable narrative companion to edition-state.json. Where
|
||||
edition-state.json is the machine-readable resumption state (currentPhase,
|
||||
per-article status), this HANDOVER carries the *narrative* state a human
|
||||
(or the next session) reads to understand where the edition is, what is
|
||||
immutable, and what happens next. /linkedin:newsletter Step 0 reads it;
|
||||
every phase appends to §6.
|
||||
Decision: G — production lives in the series root, NOT the plugin. Copy this to
|
||||
<serie>/linkedin/edition-HANDOVER.md (or <serie>/HANDOVER.md) and fill in.
|
||||
DISTINCT from the plugin's own docs/BUILD-HANDOVER.local.md (which governs building
|
||||
the plugin). Never merge the two.
|
||||
|
||||
Section numbering is referenced by name in commands/newsletter.md:
|
||||
§1 where we are · §3–§5 brief/rules/calibration · §4 immutable rules + fact-check
|
||||
log · §5 persona calibration + conversion verdict · §6 next-session pointer.
|
||||
-->
|
||||
|
||||
# Edition HANDOVER — <series title>, article <NN> "<edition title>"
|
||||
|
||||
## §1 — Where we are
|
||||
- Current phase: <currentPhase, mirrors edition-state.json>
|
||||
- Article status: <pending | in-progress | locked | scheduled>
|
||||
- One-line state: <e.g. "draft complete, consistency pass next" or "locked, awaiting hook gate">
|
||||
|
||||
## §2 — Premise & angle
|
||||
- Premise (one clear claim): <…>
|
||||
- Angle / dramaturgical spine: <…>
|
||||
- Leader-takeaway (the one thing a busy reader keeps): <…>
|
||||
|
||||
## §3 — Brief & scope
|
||||
- Audience personas (active set; mark primær): <persona A (PRIMÆR), persona B, …>
|
||||
- Key points (2–4): <…>
|
||||
- Tone / voice anchor: <reference to assets/voice-samples + any edition-specific note>
|
||||
- Out of scope for this edition: <…>
|
||||
|
||||
## §4 — Immutable rules + fact-check log
|
||||
> Rules locked for this edition (do not relitigate mid-pipeline):
|
||||
- <immutable rule 1 — e.g. "no vendor names in the hook">
|
||||
- <immutable rule 2>
|
||||
|
||||
> Fact-check log (Step 5 — guilty-until-disproven; 🔴 must be empty before lock):
|
||||
| Claim | Risk | Source / verification | Status |
|
||||
|-------|------|-----------------------|--------|
|
||||
| <claim> | 🔴/🟡/🟢 | <primary source> | open / resolved |
|
||||
|
||||
## §5 — Persona calibration + verdicts
|
||||
- Pre-lock resonance sweep (Step 6): primær <name> → <JA / NEI + one-line reason>
|
||||
- Method/persona calibration notes: <any axis tuning, secondary-NO signals>
|
||||
- Post-lock conversion sweep (Step 9): primær <name> mode konverter → <JA / NEI>
|
||||
|
||||
## §6 — Next session
|
||||
- Next step: Step <N> — <name> (per the Step 0 resumption table)
|
||||
- Precise pointer: <e.g. "draft resumes at section 3" or "render POST.html, then hook gate">
|
||||
- Open questions for the operator: <…, or "none">
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"_doc": {
|
||||
"purpose": "Schema + starter for edition-config.json — the STATIC per-edition delivery metadata that render/build-linkedin.mjs reads (calendar, freshness, cover credit, captions). Complements edition-state.json (machine resumption state) and edition-delingstekst.md (distribution copy).",
|
||||
"decision": "G — production lives in the series root, NOT in the plugin. Copy this template to <serie>/linkedin/edition-config.json and fill it in. This file is the schema-defining TEMPLATE only.",
|
||||
"location": "<serie>/linkedin/edition-config.json (read relative to cwd = series root; OUT_ROOT = <cwd>/linkedin)",
|
||||
"graceful": "render/build-linkedin.mjs loadEditionConfig() falls back to empty defaults if this file is missing or malformed — every field below is optional. Provide it for a complete delivery page (calendar slot, freshness banner, cover credit, alt-text caption).",
|
||||
"keys": "Article keys are zero-padded strings mirroring edition-state.json + the NN-prefix of each NN-utkast.md draft: \"01\", \"02\", ..., plus \"samle\" for the collected post.",
|
||||
"fields": {
|
||||
"calendar[NN]": "{ dag: human date label e.g. \"Mandag 02.06\", klokke: \"HH:MM\" } — the scheduled slot shown on POST.html. Default if absent: { dag: \"—\", klokke: \"08:00\" }.",
|
||||
"freshness[NN]": "string — a freshness/recency note rendered in the amber banner (e.g. \"Tall fra Q1 2026; sjekk før publisering etter 01.07\"). Omit for no banner.",
|
||||
"coverCredit": "string — global cover-image credit line (\"Add credit and caption\" field). One value for the whole edition.",
|
||||
"captions[NN]": "string — per-article cover-image caption / alt text. Default if absent: \"—\".",
|
||||
"carousel": "list of zero-padded NN strings (e.g. [\"03\",\"06\"]) — the editions that ship an optional carousel/document post. POST.html shows a carousel block only for these NN. Empty/absent → no carousel block. (S14/F6: replaces the old hardcoded Seres set.)"
|
||||
}
|
||||
},
|
||||
"calendar": {
|
||||
"01": { "dag": "<Ukedag DD.MM>", "klokke": "08:00" },
|
||||
"samle": { "dag": "<Ukedag DD.MM>", "klokke": "08:00" }
|
||||
},
|
||||
"freshness": {
|
||||
"01": "<optional freshness note shown in the banner — omit the key for no banner>"
|
||||
},
|
||||
"coverCredit": "<cover-image credit line, or empty string>",
|
||||
"captions": {
|
||||
"01": "<cover-image caption / alt text for article 01>"
|
||||
},
|
||||
"carousel": []
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<!--
|
||||
TEMPLATE — edition-delingstekst.md (distribution copy for a newsletter edition)
|
||||
|
||||
Purpose : the per-edition LinkedIn distribution text that render/build-linkedin.mjs
|
||||
folds into each POST.html "all-in-one-place" deliverable. This is the
|
||||
feed copy the reader sees BEFORE "…see more" — the hook that earns the
|
||||
click (gated in /linkedin:newsletter Step 9).
|
||||
Decision: G — production lives in the series root, NOT the plugin. Copy this to
|
||||
<serie>/linkedin/edition-delingstekst.md and fill it in.
|
||||
Location: <serie>/linkedin/edition-delingstekst.md (cwd = series root).
|
||||
Graceful: render/build-linkedin.mjs degrades if this file is missing (no
|
||||
distribution copy is folded in; the article POST.html still builds).
|
||||
Provide it for a complete delivery.
|
||||
|
||||
GRAMMAR (exactly what parseDelingstekst() recognizes — do not improvise):
|
||||
- A section starts with a heading: "## Del N — <title>" (N = article number,
|
||||
mapped to zero-padded key "0N") OR "## Samle <…>" (the collected post,
|
||||
key "samle").
|
||||
- "## SYSTEM …" headings are ignored.
|
||||
- Inside a section, until the next "## " heading or a "---" line:
|
||||
* "**Første kommentar:** <text>" → first-comment text (one line).
|
||||
* a line beginning with "#" + non-space (e.g. "#KI #offentligsektor")
|
||||
→ the hashtag line.
|
||||
* a "> …" blockquote line → ignored (use it for NB/notes to yourself).
|
||||
* every other line → part of the share text (the hook + body shown in feed).
|
||||
Keys MUST match the NN-prefix of the draft (NN-utkast.md) and edition-config.json.
|
||||
-->
|
||||
|
||||
## Del 1 — <edition title>
|
||||
|
||||
<First line = the krok/hook: the single line that must stop the scroll. Keep the
|
||||
strongest claim or tension here; this is what shows before "…see more".>
|
||||
|
||||
<Then 2–4 short lines that pay off the hook and point at the article. Tighten,
|
||||
never pad — this is feed copy, not the article.>
|
||||
|
||||
**Første kommentar:** <the first-comment text — e.g. a link, a question to seed
|
||||
discussion, or the "full edition here" pointer. LinkedIn suppresses links in the
|
||||
body, so the link belongs here.>
|
||||
|
||||
#hashtag1 #hashtag2 #hashtag3
|
||||
|
||||
> NB to self (ignored by the renderer): note any freshness caveat or A/B variant
|
||||
> you want to remember for this edition.
|
||||
|
||||
---
|
||||
|
||||
## Samle <collected-post title, if shipping a roundup of the series>
|
||||
|
||||
<Hook for the collected/summary post. Same grammar. Omit this whole section if the
|
||||
edition has no samle post.>
|
||||
|
||||
**Første kommentar:** <first comment for the samle post>
|
||||
|
||||
#hashtag1 #hashtag2
|
||||
Loading…
Add table
Add a link
Reference in a new issue