From a3d26b223c69559484faa6d4a4343a684c98194a Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Thu, 18 Jun 2026 14:10:17 +0200 Subject: [PATCH] =?UTF-8?q?fix(linkedin-studio):=20S4=20harden=20multiplat?= =?UTF-8?q?form=20=E2=80=94=20Step-0=20empty-source=20fallback=20+=20cross?= =?UTF-8?q?-platform=20buzzword=20line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit S4 close-out (2 edits / 1 command file · lint 81/0 · counts 29/19/26/6): - multiplatform.md:28 Step 0: empty-source fallback (drafts empty + no recent posts + nothing pasted -> stop, ask user to paste; never fabricate source) - multiplatform.md:45 Adaptation Templates: cross-platform voice-hygiene line (banned buzzwords = content rule #4; adaptation rewrites can introduce them) Class call: NOT post-emitting (output is Twitter/slides/YouTube) -> no LinkedIn predicate imposed; tested as guided/cross-format + routing sub-predicate. Routing (/linkedin:newsletter) + both reference files resolve; no subagents. Axis-c (buzzword) + axis-d (degradation) closed; axes a/b PASS. Local-only per push policy. --- commands/multiplatform.md | 9 +++++ docs/hardening/log.md | 77 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/commands/multiplatform.md b/commands/multiplatform.md index 4c73539..d46aa5f 100644 --- a/commands/multiplatform.md +++ b/commands/multiplatform.md @@ -24,6 +24,10 @@ Ask the user to provide their LinkedIn content or read from drafts: - Read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/drafts/` for recent content - Read `~/.claude/linkedin-studio.local.md` for recent posts +If none of these yields source content (empty/absent drafts, no recent posts, and nothing +pasted), **stop and ask the user to paste the LinkedIn post to adapt** — never fabricate +source content to adapt. + ## Step 1: Select Target Platform > **Long-form lives elsewhere.** Newsletters, blog posts, and essays are produced by @@ -38,6 +42,11 @@ Use AskUserQuestion: ## Adaptation Templates +> **Voice hygiene (all platforms):** adaptation rewrites the source — keep the adapted copy free +> of the banned corporate buzzwords (content rule #4: leverage, synergy, paradigm shift, thought +> leader, disruptive, value proposition, ecosystem, holistic approach) on every platform; don't +> let the rewrite introduce them. + ### LinkedIn → Twitter/X Thread ``` diff --git a/docs/hardening/log.md b/docs/hardening/log.md index 078577c..104a50b 100644 --- a/docs/hardening/log.md +++ b/docs/hardening/log.md @@ -386,3 +386,80 @@ perspective *extending* the source, not echoing it — the commodity-reaction gu - Disposition: **HARDENED** (3 edits, axis-c + axis-d) · 0 deferrals · axes a/b PASS. --- + +## Session 4 — Create III (`multiplatform`) + +> S4 status: re-grounded the proposed predicates against the actual `multiplatform.md` lines +> BEFORE talk (jerndisiplin: assert-after-read). Field-notes inbox **absent** at S4 → graceful +> no-op (SC-I). All `file:line` below are tool-grounded against the FINAL (post-edit) file. +> **Class call (operator-agreed):** NOT post-emitting — output is Twitter/slides/YouTube, so the +> LinkedIn content predicates (hook 110–140, length band, no-body-link, topic→5 pillars) do **not** +> apply per platform; imposing them here would be the exact category error the Create-III brief warns +> against. Tested as **guided/cross-format with a routing sub-predicate**. + +### /linkedin:multiplatform — adapt existing LinkedIn content to short-/cross-format (Twitter thread · slides · YouTube script) + +**INTENT.** `multiplatform` is the Create journey's **cross-format adapter**: take existing LinkedIn +content and re-shape it for other platforms (Twitter/X thread, presentation slides, YouTube script) — +*write once, publish everywhere*. It explicitly does **not** own long-form (newsletters / blog / essays +→ `/linkedin:newsletter`, the single long-form entry point). It is an *adapter*, not a post-generator: +the value-add is adaptation fidelity (each platform's own rules) + correct routing of out-of-scope +(long-form) requests, not LinkedIn-feed quality gating. + +**SIMULATE.** +- **Persona (ICP):** the author — a finished LinkedIn post in drafts, wants to reuse it as a Twitter thread. +- **Invocation:** `/linkedin:multiplatform` → pick "Twitter/X thread". +- **Walk:** Step 0 (`:21`) load source from `drafts/` + state → Step 1 (`:31`) AskUserQuestion pick + Twitter → Twitter template (`:53`, hook ≤**280** + 🧵 + one-idea tweets + CTA) → Step 2 (`:115`) save + `drafts/multiplatform/twitter-[slug].md` (`:118`) + clipboard, "Copied" (`:123`). **Promised artifact + produced.** +- **CONCRETE friction (axis d, adapter-specific — anchored, not a generated post):** `grep` for an + empty-source / fabrication guard in Step 0 → only matched l.23 "Ask the user to provide … or read + from drafts" — **no fallback** for the case where drafts is empty/absent, state has no recent posts, + AND the user pasted nothing. Risk: the model **fabricates a LinkedIn post to adapt** rather than + stopping. (Same failure class as S3's WebFetch-no-extraction gap.) +- **Second friction (axis c, voice hygiene):** the banned-buzzword list (content rule #4) is + *platform-agnostic voice hygiene*, not a char rule — and adaptation **rewrites** the source + (condense the hook to 280, expand to a script), which can introduce buzzwords absent from the + clean source. Pre-edit the file mentioned buzzwords **nowhere** (full read). Operator-judged in + scope (light line, not a full gate). + +**EVALUATE (4 axes).** +- **(a) intention fidelity — PASS.** Three adapters (Twitter/slides/YouTube), source-loading, + save + clipboard, long-form correctly delegated — delivers the description's promise. +- **(b) algorithm/platform bar — PASS.** Per-platform rules correct, **no LinkedIn-rule leak**: + Twitter "280 chars max" (`:53`); **no** `110`/`140` hook band anywhere in the file (grep NONE — + the desired result for a non-LinkedIn surface). Long-form routing intact (see (c)). +- **(c) MECHANICAL predicate (routing sub-predicate) — PASS; voice-hygiene sub-gap fixed.** Every + emitted `/linkedin:newsletter` (`:6/:34/:36/:132`) resolves to a real `commands/newsletter.md` + (verified, ls) ✓; both reference files resolve — `references/linkedin-formats.md` (`:128`) + + `engagement-frameworks.md` (`:129`) (verified, ls) ✓. **Buzzword voice-hygiene line absent** → + added (`:45`). +- **(d) agent-wiring + graceful degradation — wiring N/A; degradation GAP → fixed.** No subagents + invoked (`grep subagent_type` → NONE — nothing to resolve). **Graceful degradation:** no + empty-source/fabrication guard in Step 0 (grep matched only the weak "ask … or read from drafts") + → added a stop-and-ask-never-fabricate fallback (`:28`). + +**HARDEN (surgical — 2 edits, 1 file, both grep-confirmed landed).** +- `commands/multiplatform.md:28` — **added** Step-0 empty-source fallback: *"If none of these yields + source content (empty/absent drafts, no recent posts, and nothing pasted), **stop and ask the user + to paste the LinkedIn post to adapt** — never fabricate source content to adapt."* +- `commands/multiplatform.md:45` — **added** an "Adaptation Templates" voice-hygiene blockquote: + *"adaptation rewrites the source — keep the adapted copy free of the banned corporate buzzwords + (content rule #4: leverage, synergy, paradigm shift, thought leader, disruptive, value proposition, + ecosystem, holistic approach) on every platform; don't let the rewrite introduce them."* +- **Deferrals:** none. **Scope note (flagged, not touched):** Step 2's "Copied to clipboard." (`:123`) + is unconditional, but `clipboard-helper.mjs` is shared, tested infra used identically by ~9 content + commands — re-gating it here would be scope-creep into a shared pattern (surgical discipline). + +**VERIFY.** +- `bash scripts/test-runner.sh` → `Passed: 81 · Failed: 0 · Warnings: 0`; counts **29/19/26/6** + unchanged (`ls commands/*.md|wc -l` == 29, `ls agents/*.md|wc -l` == 19; `git diff --stat` = + `multiplatform.md` only, +9 insertions, 0 deletions). +- Before/after delta: axis (d) empty-source now degrades to "paste the post / never fabricate" + instead of an invented source; axis (c) the rewrite is now bound by the banned-buzzword list on + every platform. The 2 edits are the cause. +- Disposition: **HARDENED** (2 edits, axis-c + axis-d) · 0 deferrals · axes a/b PASS · class + confirmed non-post-emitting (no LinkedIn predicate imposed). + +---