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
|
|
@ -38,7 +38,7 @@ const DELINGSTEKST_FILE = path.join(OUT_ROOT, "edition-delingstekst.md");
|
|||
// ---------------------------------------------------------------------------
|
||||
const CONFIG_FILE = path.join(OUT_ROOT, "edition-config.json");
|
||||
|
||||
const EMPTY_CONFIG = { calendar: {}, freshness: {}, coverCredit: "", captions: {} };
|
||||
const EMPTY_CONFIG = { calendar: {}, freshness: {}, coverCredit: "", captions: {}, carousel: [] };
|
||||
|
||||
// Les edition-config.json fra rootDir (serie-mappas linkedin/). Normaliser alle
|
||||
// felt til kjente former; manglende/ugyldig fil → tomme standarder (graceful).
|
||||
|
|
@ -55,13 +55,13 @@ export function loadEditionConfig(rootDir = OUT_ROOT) {
|
|||
freshness: cfg.freshness && typeof cfg.freshness === "object" ? cfg.freshness : {},
|
||||
coverCredit: typeof cfg.coverCredit === "string" ? cfg.coverCredit : "",
|
||||
captions: cfg.captions && typeof cfg.captions === "object" ? cfg.captions : {},
|
||||
// S14/F6: carousel editions are config-derived, not Seres-hardcoded. A list of
|
||||
// zero-padded NN strings ("03","06"); empty/absent → no carousel block for any
|
||||
// edition. Generalizes away the old `new Set(["03","06"])` Seres assumption.
|
||||
carousel: Array.isArray(cfg.carousel) ? cfg.carousel.map(String) : [],
|
||||
};
|
||||
}
|
||||
|
||||
// CAROUSEL-settet er ikke en del av S2-konfig-scope (kun CALENDAR/FRESHNESS/
|
||||
// CAPTIONS/COVER_CREDIT generaliseres) — beholdes hardkodet inntil videre.
|
||||
const CAROUSEL = new Set(["03", "06"]);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// YAML front matter (flate key: "value"-par mellom --- ... ---)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -177,7 +177,16 @@ function seoTitle(title) {
|
|||
// En seksjon = «## Del N — …» eller «## Samle…». «## SYSTEM …» ignoreres.
|
||||
// ---------------------------------------------------------------------------
|
||||
function parseDelingstekst() {
|
||||
const raw = fs.readFileSync(DELINGSTEKST_FILE, "utf8").replace(/\r\n/g, "\n");
|
||||
// Graceful (S14/F8): missing or unreadable delingstekst → no distribution copy.
|
||||
// Matches loadEditionConfig's fail-soft contract — the article POST.html still
|
||||
// builds; only the share text is absent. Previously this threw ENOENT before any
|
||||
// POST.html was written, killing the whole build incl. article posts.
|
||||
let raw;
|
||||
try {
|
||||
raw = fs.readFileSync(DELINGSTEKST_FILE, "utf8").replace(/\r\n/g, "\n");
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
const lines = raw.split("\n");
|
||||
const out = {};
|
||||
let i = 0;
|
||||
|
|
@ -266,7 +275,7 @@ export function editionPost(nn, meta, body, share, config = EMPTY_CONFIG) {
|
|||
const copyZone = [subtitle, ...blocks].filter(Boolean).join("\n ");
|
||||
const shareField = share ? `${share.share}\n\n${share.hashtags}` : "—";
|
||||
|
||||
const carouselBlock = CAROUSEL.has(nn)
|
||||
const carouselBlock = (config.carousel || []).includes(nn)
|
||||
? `<div class="fld"><h2>6 · Carousel (valgfritt rekkevidde-tillegg)</h2>
|
||||
<div class="val">Egen dokument-post, helst egen dag: last opp <code>linkedin/${nn}/carousel.pdf</code>.
|
||||
Caption = delingstekstens premiss-linje.</div></div>`
|
||||
|
|
@ -308,7 +317,7 @@ export function editionPost(nn, meta, body, share, config = EMPTY_CONFIG) {
|
|||
|
||||
${carouselBlock}
|
||||
|
||||
<div class="marker">⬇︎ ${CAROUSEL.has(nn) ? "7" : "6"} · BRØDTEKST — merk alt herfra, kopier (⌘C), lim i editoren ⬇︎</div>
|
||||
<div class="marker">⬇︎ ${(config.carousel || []).includes(nn) ? "7" : "6"} · BRØDTEKST — merk alt herfra, kopier (⌘C), lim i editoren ⬇︎</div>
|
||||
<div class="copyzone">
|
||||
${copyZone}
|
||||
</div>
|
||||
|
|
@ -361,7 +370,9 @@ export function main(files = process.argv.slice(2)) {
|
|||
console.log(`✓ linkedin/${nn}/POST.html (${meta.title || base})`);
|
||||
}
|
||||
|
||||
// Samle bygges alltid (innhold er uavhengig av utkast-filene)
|
||||
// Samle bygges KUN når delingsteksten deklarerer en «## Samle»-seksjon (S14/F6:
|
||||
// tidligere kommentar sa «alltid», men bygget har alltid vært betinget av
|
||||
// shareMap.samle — innholdet er uavhengig av utkast-filene, men ikke av delingstekst).
|
||||
if (shareMap.samle) {
|
||||
const dir = path.join(OUT_ROOT, "samle");
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue