refactor(linkedin)!: rename plugin linkedin-thought-leadership → linkedin-studio (v3.0.0)
BREAKING CHANGE: the marketplace slug, the agent namespace (linkedin-studio:<agent>), and the runtime state-file path (~/.claude/linkedin-studio.local.md) all change. Reinstall required; existing state migrated in place (post metrics, streak, history preserved). The /linkedin:* commands are unchanged — the command namespace is set per-command in frontmatter and was always independent of the plugin slug. Functionality is byte-identical to v2.4.0; this release is pure identity. - dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json - agent namespace updated in commands/newsletter.md (only functional invoker) - state path updated in 4 hook scripts + topic-rotation prompt + state template - catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged) - docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md - historical records (CHANGELOG past entries, docs/ build artifacts, config-audit v5.0.0 snapshots) intentionally retain the old slug Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9df3de795c
commit
b6bb61246b
196 changed files with 164 additions and 138 deletions
164
plugins/linkedin-studio/docs/agents-capability-matrix.md
Normal file
164
plugins/linkedin-studio/docs/agents-capability-matrix.md
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
# Agent Capability Matrix
|
||||
|
||||
14 specialized agents for LinkedIn thought leadership. Each agent has a focused responsibility, defined model, and unique color for visual identification.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Agent | Model | Color | Primary Responsibility |
|
||||
|-------|-------|-------|----------------------|
|
||||
| content-optimizer | Sonnet | Blue | Optimize posts against algorithm signals |
|
||||
| strategy-advisor | Sonnet | Green | Growth strategy and phase-specific guidance |
|
||||
| analytics-interpreter | Sonnet | Yellow | Pattern discovery + weekly/monthly performance reports (interpret/report modes) |
|
||||
| engagement-coach | Sonnet | Magenta | 5x5x5 + first-hour tactics + CEA commenting + target selection |
|
||||
| content-planner | Sonnet | Cyan | Weekly/monthly content calendars |
|
||||
| network-builder | Sonnet | Teal | Strategic networking and outreach |
|
||||
| content-repurposer | Sonnet | Purple | Format conversion and evergreen refresh |
|
||||
| trend-spotter | Sonnet | White | Trending topics and opportunity scoring |
|
||||
| voice-trainer | Sonnet | Pink | Voice profile building and drift detection |
|
||||
| differentiation-checker | Sonnet | Gray | Originality scoring and commodity detection |
|
||||
| video-scripter | Sonnet | Violet | Video script creation with pacing and visual cues |
|
||||
| post-feedback-monitor | Haiku | Lime | Post-publish 48h monitoring and real-time interventions |
|
||||
| fact-checker | Opus | Brown | Factual-claim verification against primary/credible sources (longform) |
|
||||
| persona-reviewer | Opus | Olive | Reader-persona resonance + hook-conversion gate (longform) |
|
||||
|
||||
## Capability Matrix
|
||||
|
||||
Capabilities mapped across agents. **P** = Primary, **S** = Secondary/Supporting.
|
||||
|
||||
| Capability | optimizer | strategy | analytics | engage | planner | network | repurpose | trends | voice | diff-check | video | post-monitor | fact-check | persona-rev |
|
||||
|-----------|:---------:|:--------:|:---------:|:------:|:-------:|:-------:|:---------:|:------:|:-----:|:----------:|:-----:|:------------:|:----------:|:-----------:|
|
||||
| Post optimization | **P** | | | | | | | | | | | | | |
|
||||
| Hook analysis | **P** | | | | | | | | | | S | | | S |
|
||||
| Algorithm alignment | **P** | | | S | | | | | | | S | S | | |
|
||||
| Growth strategy | | **P** | | | S | | | | | | | | | |
|
||||
| Phase assessment | | **P** | | | | | | | | | | | | |
|
||||
| Trajectory analysis | | **P** | S | | | | | | | | | | | |
|
||||
| Audience analysis | | S | **P** | | | | | | | | | | | |
|
||||
| Pattern discovery | | | **P** | | | | | | | | | | | |
|
||||
| Performance reports | | | **P** | | | | | | | | | | | |
|
||||
| Content DNA | | | **P** | | | | | | S | | | | | |
|
||||
| Engagement coaching | | | | **P** | | S | | | | | | | | |
|
||||
| 5x5x5 method | | | | **P** | | S | | | | | | | | |
|
||||
| Comment strategy | | | | **P** | | | | | | | | | | |
|
||||
| CEA method | | | | **P** | | | | | | | | | | |
|
||||
| Target identification | | | | **P** | | S | | | | | | | | |
|
||||
| Content planning | | | | | **P** | | | S | | | | | | |
|
||||
| Mix enforcement | | | | | **P** | | | | | | | | | |
|
||||
| Gap analysis | | | | | **P** | | | | | | | | | |
|
||||
| Network building | | | | S | | **P** | | | | | | | | |
|
||||
| Connection scoring | | | | | | **P** | | | | | | | | |
|
||||
| DM templates | | | | | | **P** | | | | | | | | |
|
||||
| Format conversion | | | | | | | **P** | | | | S | | | |
|
||||
| Evergreen scoring | | | | | | | **P** | | | | | | | |
|
||||
| Content lifecycle | | | | | S | | **P** | | | | | | | |
|
||||
| Trend scanning | | | | | S | | | **P** | | | | | | |
|
||||
| First-mover assessment | | | | | | | | **P** | | | | | | |
|
||||
| Angle mapping | | | | | S | | S | **P** | | | | | | |
|
||||
| Voice profiling | | | | | | | | | **P** | | | | | |
|
||||
| Drift detection | | | | | | | | | **P** | | | | | |
|
||||
| Quarterly audit | | | | | | | | | **P** | | | | | |
|
||||
| Originality scoring | | | | | | | | | | **P** | | | | |
|
||||
| Commodity detection | | | | | | | | | | **P** | | | | |
|
||||
| Differentiation | | | | | | | | | | **P** | | | | |
|
||||
| Video scripting | | | | | | | S | | | | **P** | | | |
|
||||
| Script pacing | | | | | | | | | | | **P** | | | |
|
||||
| Visual cue notation | | | | | | | | | | | **P** | | | |
|
||||
| Post-publish monitoring | | | | | | | | | | | | **P** | | |
|
||||
| Velocity analysis | | | | | | | | | | | | **P** | | |
|
||||
| Factual verification | | | | | | | | | | | | | **P** | |
|
||||
| Primary-source check | | | | | | | | | | | | | **P** | |
|
||||
| Persona resonance | | | | | | | | | | | | | | **P** |
|
||||
| Hook-conversion gate | | | | | | | | | | | | | | **P** |
|
||||
|
||||
## Content Pipeline
|
||||
|
||||
How agents collaborate in the end-to-end content lifecycle:
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ trend-spotter│───▸│ content-planner │───▸│ diff-checker │
|
||||
│ (find topics)│ │ (plan + schedule) │ │ (originality │
|
||||
└─────────────┘ └──────────────────┘ │ gate ≥51/100) │
|
||||
│ └────────┬────────┘
|
||||
│ │
|
||||
┌──────▼──────┐ ┌───────┴────────┐
|
||||
│voice-trainer│ │ FORMAT SPLIT │
|
||||
│(voice check)│ └──┬──────────┬──┘
|
||||
└──────┬──────┘ │ │
|
||||
│ ┌───────▼───┐ ┌────▼─────────┐
|
||||
│ │video- │ │content- │
|
||||
└────────────▸│scripter │ │optimizer │
|
||||
│(scripts) │ │(text posts) │
|
||||
└───────┬───┘ └──────┬───────┘
|
||||
│ │
|
||||
└─────┬──────┘
|
||||
┌────────────────────────────┤
|
||||
│ │
|
||||
┌──────▼────────────┐ ┌────────▼───────┐
|
||||
│analytics- │ │ [PUBLISH] │
|
||||
│interpreter │ └────────┬───────┘
|
||||
│(interpret/report) │ │
|
||||
└───────────────────┘ ┌────────▼───────┐
|
||||
│engagement-coach│
|
||||
│(5x5x5 + first │
|
||||
│ hour + CEA │
|
||||
│ commenting) │
|
||||
└────────────────┘
|
||||
```
|
||||
|
||||
### Longform Quality Gates (newsletter)
|
||||
|
||||
For longform editions, two additional Opus agents run BEFORE lock:
|
||||
|
||||
```
|
||||
draft ─▸ fact-checker ─▸ persona-reviewer ─▸ LOCK ─▸ delivery
|
||||
(primary-source (resonance +
|
||||
verification) hook-conversion
|
||||
gate)
|
||||
```
|
||||
|
||||
### Parallel Support Agents
|
||||
|
||||
These agents operate independently and feed into the pipeline at multiple points:
|
||||
|
||||
```
|
||||
strategy-advisor ──────▸ Macro-level planning and phase guidance
|
||||
analytics-interpreter ─▸ Pattern discovery + periodic reports feeding back into planning
|
||||
network-builder ───────▸ Relationship building amplifying content reach
|
||||
content-repurposer ────▸ Post-publish: extends content lifecycle
|
||||
```
|
||||
|
||||
## Which Agent Do I Need?
|
||||
|
||||
| Scenario | Agent | Command |
|
||||
|----------|-------|---------|
|
||||
| "I want to write a post" | content-optimizer | `/linkedin:post` |
|
||||
| "What should I post about?" | content-planner, trend-spotter | `/linkedin:pipeline` |
|
||||
| "Make this post better" | content-optimizer | `/linkedin:post` |
|
||||
| "Is this original enough?" | differentiation-checker | `/linkedin:pipeline` |
|
||||
| "Plan my week's content" | content-planner | `/linkedin:batch` |
|
||||
| "Am I on track this week?" | — | `/linkedin:calendar` |
|
||||
| "How did I do this week?" | analytics-interpreter (report mode) | `/linkedin:report` |
|
||||
| "Analyze my LinkedIn data" | analytics-interpreter (interpret mode) | `/linkedin:analyze` |
|
||||
| "What's my LinkedIn strategy?" | strategy-advisor | `/linkedin:strategy` |
|
||||
| "Help me engage more" | engagement-coach | `/linkedin:strategy` |
|
||||
| "Who should I comment on?" | engagement-coach | `/linkedin:strategy` |
|
||||
| "Build my network" | network-builder | `/linkedin:strategy` |
|
||||
| "Does this sound like me?" | voice-trainer | `/linkedin:post` |
|
||||
| "Repurpose my best post" | content-repurposer | `/linkedin:pipeline` |
|
||||
| "What's trending in my field?" | trend-spotter | `/linkedin:pipeline` |
|
||||
| "Audit my content strategy" | analytics-interpreter, strategy-advisor | `/linkedin:audit` |
|
||||
| "How do I monetize?" | strategy-advisor | `/linkedin:monetize` |
|
||||
| "Create a video script" | video-scripter | `/linkedin:video` |
|
||||
| "Turn this post into a video" | video-scripter, content-repurposer | `/linkedin:video` |
|
||||
| "Script a talking head video" | video-scripter | `/linkedin:video` |
|
||||
| "Verify facts in this draft" | fact-checker | `/linkedin:newsletter` (longform) |
|
||||
| "Will this land with my readers?" | persona-reviewer | `/linkedin:newsletter` (longform) |
|
||||
|
||||
## Model Selection Rationale
|
||||
|
||||
| Model | Agents | Why |
|
||||
|-------|--------|-----|
|
||||
| **Opus** | 2 agents (fact-checker, persona-reviewer) | Longform judgment: factual verification, reader-persona resonance |
|
||||
| **Sonnet** | 11 agents | Complex reasoning: optimization, strategy, analysis, scoring, scripting, comment targeting |
|
||||
| **Haiku** | 1 agent (post-feedback-monitor) | Lighter task: post monitoring with anomaly detection |
|
||||
160
plugins/linkedin-studio/docs/brief-fullspektrum-innholdsmotor.md
Normal file
160
plugins/linkedin-studio/docs/brief-fullspektrum-innholdsmotor.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# Brief — LTL som fullspektrum LinkedIn-innholdsmotor (idé → publisering)
|
||||
|
||||
> **Til:** linkedin-studio-pluginens utviklingsrepo.
|
||||
> **Skrevet:** 2026-05-26, etter produksjon av den første kronikkserien (Seres-serien, 6 deler).
|
||||
> **Type:** retningsbrief — beslutningsgrunnlag før planlegging/bygging. Selvstendig (kan leses uten annen kontekst).
|
||||
|
||||
---
|
||||
|
||||
## 1. Hva vi skal oppnå
|
||||
|
||||
Løft LTL fra en kortform-fokusert plugin til **den komplette motoren for ALT LinkedIn-innhold,
|
||||
fra idé til publisering** — inkludert **nyhetsbrev/langform**, som i dag er pluginens svakeste område.
|
||||
|
||||
Én plugin skal eie hele kjeden: **idé → research → utkast → faktasjekk → review → hook/distribusjon →
|
||||
planlegging → publisering → analyse** — for alle formater: posts, carousels, reaksjoner, video, og
|
||||
**nyhetsbrev-editions**.
|
||||
|
||||
Begrunnelsen: scopet er LinkedIn. Da hører nyhetsbrev hjemme i LTL (det ER et LinkedIn-native format),
|
||||
sømmene mot en separat plugin koster mer enn de gir, og brukeren bruker uansett LTL for alt LinkedIn.
|
||||
|
||||
---
|
||||
|
||||
## 2. Kontekst: hvor dette innholdet lages
|
||||
|
||||
**`~/repos/maskinrommet`** er det private, lokale repoet der **ALT innhold produseres**. Der brukes
|
||||
**LTL-pluginen til all innholdsproduksjon**. Arbeidsdelingen:
|
||||
|
||||
- **maskinrommet** = arbeidsbenken (innhold, serier, og en delt `tools/`-mappe med deterministiske
|
||||
render-skript: `build-linkedin.mjs` → POST.html, `build-carousel.mjs`, `build-html.mjs` annoterbar
|
||||
HTML, `build-pdf.mjs` avis-PDF).
|
||||
- **LTL** = verktøyet/hjernen som driver produksjonen i det repoet.
|
||||
- **dette LTL-repoet** = der selve plugin-utviklingen skjer. Erfaringene kommer fra maskinrommet;
|
||||
endringene gjøres her.
|
||||
|
||||
Render-skriptene i `maskinrommet/tools/` er den mekaniske utføreren som LTL-pipelinen *kaller*.
|
||||
«LTL eier prosessen» krever ikke at LTL *hoster* render-skriptet — se åpen beslutning C.
|
||||
|
||||
---
|
||||
|
||||
## 3. Live-status (baseline å bygge for)
|
||||
|
||||
Nyhetsbrevet **Maskinrommet** er etablert på LinkedIn:
|
||||
- **Første post ute**, **30 abonnenter**, **6 nyhetsbrev i pipen** (Seres-serien, ferdig produsert).
|
||||
- Publisering er manuell (LinkedIn har ingen API for newsletter/long-form), men kan native-planlegges.
|
||||
|
||||
Dette er en reell, kjørende kadens — ikke en hypotese. Forbedringene skal støtte å produsere de neste
|
||||
seriene raskere og med samme kvalitet.
|
||||
|
||||
---
|
||||
|
||||
## 4. Erfaringsgrunnlag: hva en kronikk faktisk krever
|
||||
|
||||
Seres-serien (6 kronikker, ~10 sesjoner) avdekket at langform-produksjon ikke er «skriving» — det er en
|
||||
**research- og adversarial-review-pipeline**. Den faktiske flyten som ga kvalitet:
|
||||
|
||||
1. **Brief** — vinkel, tenkt stemme, målgruppe-personaer (med primær), nøkkelpoeng, tone, leder-takeaway.
|
||||
2. **Research** — flere parallelle, avgrensede mandater → verifiserte notater.
|
||||
3. **Faktasjekk-sweep** — risikosortert (🔴/🟡/🟢), parallelle WebSearch-agenter, «hver påstand skyldig
|
||||
til motbevist». Fanget ~13 feil — flere som egne research-filer hadde bommet på.
|
||||
4. **Skriving** — dramaturgisk rekkefølge, multi-sesjon med vedlikeholdt HANDOVER.
|
||||
5. **Konsistens + kvalitet** — på tvers av tekstene (gjentakelser, tone, premiss→konklusjon-bue).
|
||||
6. **Persona-/audience-sweep FØR lås** — 3 definerte leser-juryer leser read-only, primær trumfer.
|
||||
(«Lander poenget?»). Kjørt til konvergens (LØST/DELVIS/IKKE per flagg).
|
||||
7. **Hook-/konverterings-gate** — egen persona-gate på distribusjons-teksten: «ville DU klikket?»,
|
||||
hold tilbake leveransen (tall/case/grep), ikke konklusjonen.
|
||||
8. **Lås → leveranse** — POST.html «alt på ett sted» (dato, hook, hashtags, første kommentar,
|
||||
cover-caption, brødtekst som rik tekst).
|
||||
|
||||
**De tre suksessfaktorene:** front-loadet kontekst, skreddersydd annoteringsverktøy, vedlikeholdt
|
||||
single-source HANDOVER før hver kontekst-reset.
|
||||
|
||||
**Største prosessfeil å unngå:** persona-sweep ble kjørt ETTER lås → måtte åpne låste tekster. Den
|
||||
generaliserte malen MÅ ha persona-sweep FØR lås.
|
||||
|
||||
---
|
||||
|
||||
## 5. Byggeprinsipp: løft Voyage-mønstrene, ikke fork dem
|
||||
|
||||
Denne pipelinen mapper nesten 1:1 på **Voyage**-pluginen (trekbrief → trekresearch → trekplan →
|
||||
trekexecute → trekreview, med parallelle agenter + adversarielle reviewere + multi-sesjon).
|
||||
|
||||
**Men IKKE fork Voyage og IKKE reimplementer den blindt.** Voyage er kode-spesifikk (`file:line`,
|
||||
kode-reviewere, RULE_CATALOGUE). Løft i stedet **mønstrene** inn i LTL som et nytt **langform-/
|
||||
nyhetsbrev-spor** ved siden av de eksisterende kortform-kommandoene:
|
||||
|
||||
- faset pipeline med parallelle research-agenter
|
||||
- faktasjekk-sweep som eget steg
|
||||
- multi-persona adversarial jury (leser-personaer erstatter kode-reviewere; primær trumfer)
|
||||
- multi-sesjons-kontinuitet (HANDOVER-mønster)
|
||||
|
||||
**Delte agenter, variabel intensitet:** research- og faktasjekk-agentene skal kunne kalles på *lav*
|
||||
intensitet fra en kortform-post som siterer ett tall, og *full* sweep fra et nyhetsbrev. Voice og
|
||||
hook-gate deles på tvers — aldri to systemer.
|
||||
|
||||
Voyage er **referanse/inspirasjon**, ikke en avhengighet å vedlikeholde.
|
||||
|
||||
---
|
||||
|
||||
## 6. Hva LTL allerede har (ikke bygg på nytt)
|
||||
|
||||
- Skrive-workflows for kortform: `/linkedin:post` (vinkel→draft→kvalitet→refinement),
|
||||
`/linkedin:pipeline` (idé→draft→optimer→planlegg→engasjer→publiser→analyse), `batch`, `react`,
|
||||
`quick`, `carousel`, `video`, `templates`.
|
||||
- Voice-system (`config/user-profile.local.md`, voice-samples, voice-trainer-agent).
|
||||
- Hook-/optimaliserings-støtte (content-optimizer, differentiation-checker).
|
||||
- Planlegging/sporing/analyse (`calendar`, `publish`, `import`, `report`; queue.json; state-fil).
|
||||
- 16 agenter, fler-stegs-kommandoer, REMEMBER-kontinuitet — arkitekturen *kan* være vert for et tyngre
|
||||
pipeline-spor.
|
||||
|
||||
---
|
||||
|
||||
## 7. Gapet å fylle (LTLs svake punkt = langform/nyhetsbrev)
|
||||
|
||||
1. **Langform-/nyhetsbrev-pipeline** som eget kommandospor (idé→publisering for editions, ikke bare posts).
|
||||
2. **Research-orkestrering** — parallelle mandater, verifiserte notater.
|
||||
3. **Faktasjekk-sweep** — risikosortert, kildekritisk, verifiseringslogg, «skyldig til motbevist».
|
||||
4. **Multi-persona adversarial review FØR lås** — konfigurerbare leser-juryer, primær trumfer,
|
||||
konvergens-loop til rent JA.
|
||||
5. **Multi-sesjons-kontinuitet** — HANDOVER-mønster for produksjon som spenner flere økter.
|
||||
6. **Nyhetsbrev-leveranse** — edition-format (POST.html-stil «alt på ett sted»), delingstekst-system,
|
||||
ferskvare-flagg for tidssensitive tall, native planlegging.
|
||||
7. **Annoteringssteg** — integrer annoterbar review-HTML i flyten (render bor i maskinrommet/tools).
|
||||
|
||||
---
|
||||
|
||||
## 8. Åpne beslutninger (landes før bygging i dette repoet)
|
||||
|
||||
- **A. Pipeline som nye kommandoer vs. utvidelse av `pipeline`.** Eget `/linkedin:longform`/`:newsletter`-
|
||||
spor, eller utvid eksisterende `pipeline` med en «long-form»-modus?
|
||||
- **B. Agent-deling.** Bygges research/faktasjekk/persona-jury som nye delte agenter brukt på variabel
|
||||
intensitet av både kort- og langform? (Anbefalt.)
|
||||
- **C. Render-eierskap.** Forblir `build-linkedin.mjs`/`build-carousel.mjs` i `maskinrommet/tools/`
|
||||
(LTL kaller dem), eller flyttes LinkedIn-render inn i pluginen? (Anbefalt: bli i tools/ — render-familien
|
||||
deler fonts/identitet; pluginen holdes lean.)
|
||||
- **D. Personasett.** Defineres leser-personaene per prosjekt (fra målgruppen) eller som gjenbrukbare
|
||||
profiler i config?
|
||||
- **E. Faktasjekk-omfang.** Eget steg, eller integrert i research + review?
|
||||
|
||||
---
|
||||
|
||||
## 8b. Merknad: brukeren trenger onboarding i pluginen
|
||||
|
||||
Brukeren kjenner ikke pluginen sin godt ennå og vil «ta ut potensialet». Tilby tidlig i
|
||||
LTL-sesjonen: **`/linkedin`** (oversikt over alle kommandoer) + **`/linkedin:setup`**
|
||||
(personaliserings-score + fyll inn voice-samples/case/rammeverk/demografi/profil). Personalisering
|
||||
er nøkkelen — voice-matching, differensiering og hook-gate avhenger av ekte innmatede data.
|
||||
|
||||
## 9. Referanser (i `~/repos/maskinrommet` og memory)
|
||||
|
||||
- **Plan (supersedes-kandidat):** `~/repos/maskinrommet/planer/2026-05-26-kronikk-voyage-companion.md`
|
||||
— skrevet før denne retningen (foreslo separat companion). Behold som historikk; *denne briefen* er
|
||||
gjeldende retning.
|
||||
- **Erfaringskatalog:** produsert i sesjon 2026-05-26 (16 prosessfaser, suksessfaktorer, friksjon, verktøy).
|
||||
- **HANDOVER:** `~/repos/maskinrommet/serier/silvija-seres-motsvar/HANDOVER.md` (§3 leveranse, §4 regler, §5 metode).
|
||||
- **Memory (`~/.claude/projects/-Users-ktg-repos-svv/memory/`):**
|
||||
`project_kronikk_produksjonsprosess.md` (Voyage-mapping, persona-sweep-FØR-lås, kalibrering),
|
||||
`project_maskinrommet_newsletter.md`, `project_kronikk_faktasjekk_sweep.md`,
|
||||
`feedback_dokumentprosjekt_suksessfaktorer.md`, `feedback_hook_post_persona_gate.md`,
|
||||
`feedback_persona_audience_sweep.md`, `feedback_use_linkedin_plugin.md`,
|
||||
`feedback_post_html_single_sheet.md`.
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# Brief — LTL-plugin oppgradering: produksjonskvalitet og -hastighet
|
||||
|
||||
> **Status:** Feltkunnskaps-brief fra Maskinrommet (Seres-serien, 26–27.05.2026). Mater den 21-sesjoners oppgraderingen — komplementær til `brief-fullspektrum-innholdsmotor.md`. Dette er *kravgrunnlaget* (hvorfor + hva + suksesskriterier), ikke sesjonsplanen.
|
||||
>
|
||||
> **Kildeartefakter (i Maskinrommet-innholdsrepoet, ikke her):** `serier/silvija-seres-motsvar/HANDOVER.md §7`, `~/.claude/learnings/global-learnings.md` (2026-05-27), og `linkedin-plugin-endringsspec.md` (taktisk endringsliste — denne briefen er det strategiske laget over den).
|
||||
|
||||
---
|
||||
|
||||
## 1. Problem / nordstjerne
|
||||
|
||||
Å produsere **én kort artikkel** som er **100 % korrekt** *og* **treffer primærpersonaen** tok i praksis timer (Seres-serien Del 2). Det er ikke bærekraftig ved 3–7 artikler/uke.
|
||||
|
||||
**Nordstjerne:** en kort artikkel fra idé til publiseringsklar på **~30 min**, uten å ofre korrekthet eller persona-treff. Målet er å *front-loade og systematisere* verifisering og dømmekraft — ikke å skippe dem.
|
||||
|
||||
## 2. Evidensgrunnlag (hva som faktisk gikk galt)
|
||||
|
||||
Del 2 ble først skrevet som en **modell-katalog** (det forfatteren fant interessant), passerte review, og var nær publisering før den ble stoppet og skrevet helt om. Tidstyvene, kategorisert:
|
||||
|
||||
| Tidstyv | Type | Adresseres av |
|
||||
|---|---|---|
|
||||
| Dårlig utkast måtte skrives helt om | Skulle vært fanget | Blokkerende persona-gate (mål 1) |
|
||||
| Persona-gaten flagget tung friksjon, men ble lest som notat | Prosess-svikt | Gate som stryk/bestått (mål 1) |
|
||||
| Trippelsjekk av mange ferske påstander som egen sluttfase | Gjentakende | Påstands-ledger + verifisering ved skriving (mål 3) |
|
||||
| Flere gale påstander i en *bedre* tekst (titler, «standarder», studie-funn, scope, årstall) | Gjentakende | Verifiseringsdisiplin (mål 3) |
|
||||
| AI-slop / overhead / katalog-ras → mange ordpuss-runder | Gjentakende | Voice «unngå»-mønstre + mal (mål 2, 4) |
|
||||
| Build ↔ kilde i utakt (footgun) | Repo-spesifikt | Allerede fikset i innholdsrepoet (ikke plugin) |
|
||||
|
||||
**Tre tverklærdommer (skal styre designet):**
|
||||
1. Persona-review må **blokkere**, ikke annotere.
|
||||
2. Skriv for **leseren**, ikke forfatteren — ett konkret case > en katalog.
|
||||
3. **Sterkere narrativ ≠ riktigere fakta** — verifiser mer, ikke mindre.
|
||||
|
||||
## 3. Mål — hva den oppgraderte pluginen skal levere
|
||||
|
||||
1. **Blokkerende persona-gate.** 3 personaer (A IT-dir, B KI-leder, **C linjeleder = primær, trumfer**) leser KUN teksten. Returnerer **BESTÅTT/STRYK**, ikke kommentarer. ⛔ Hard fail = C «mistet meg», C eier ikke handlingen, sjargong-mur, eller modell-/navne-katalog. «JA med forbehold» = NEI. Kjøres ett pass på nær-ferdig utkast.
|
||||
2. **Voice «unngå»-mønstre i profilen.** Katalog-ras, fullstendighet-over-handling, selvrefererende overhead-åpninger, «ikke bare X, men Y», unødig tre-listing, påklistret oppsummering, hedging. Mål: utkastet treffer persona C på 1.–2. forsøk, ikke 5.
|
||||
3. **Påstands-ledger + verifiseringsdisiplin.** Hver faktapåstand føres med kilde + dato *mens* teksten skrives. Påstander datert etter modellens kunnskapsgrense **må websøkes**. Fast sjekkliste for hyppige feiltyper: persontitler (sluttet/byttet rolle), «standarder» som varierer per virksomhet, studier tilskrevet for sterke funn, kilde-scope (konkludert vs. utenfor scope), start-/utgivelsesår.
|
||||
4. **Artikkel-skjelett / mal.** Led med leserens problem; ett konkret (helst norsk) etterprøvbart case framfor en liste; lande på leser-eid handling; skill eksplisitt «dette eier du» vs. «dette ber du IT/fag om».
|
||||
|
||||
## 4. Eierskap (hvem gjør hva)
|
||||
|
||||
- **LTL-pluginen:** strategi, voice, hooks/caption, **persona-gate**, **påstands-ledger**, mal — alt som er innholds-/kvalitetsarbeid.
|
||||
- **Voyage (trek*):** orkestrering av lengre/fler-sesjons-løp der det trengs.
|
||||
- **Innholdsrepo (Maskinrommet):** produksjon/rendering (POST.html/PDF/carousel via `tools/`-scriptene). Render holdes utenfor pluginen.
|
||||
|
||||
## 5. Ikke-mål (scope-grenser)
|
||||
|
||||
- Ikke fjern menneskelig dømmekraft eller verifisering — målet er fart *med* korrekthet.
|
||||
- Rendering/typografi forblir i innholdsrepoet.
|
||||
- Analytics forblir CSV-basert (jf. eksisterende v1.3.0-beslutning — posting-only API).
|
||||
- Denne briefen lager ikke 21-sesjonsplanen; den gir kravene planen skal innfri.
|
||||
|
||||
## 6. Suksesskriterier (målbare)
|
||||
|
||||
- [ ] Tid idé→publiseringsklar for en kort artikkel: **~30 min** (ned fra timer).
|
||||
- [ ] Persona C gir **ekte JA** på 1.–2. pass (ikke «JA med forbehold»).
|
||||
- [ ] **Null** uverifiserte påstander datert etter kunnskapsgrensen ved publisering.
|
||||
- [ ] **Null** gale person-titler / falske «standard»-påstander / overdrevne studie-funn (de fem feiltypene i mål 3).
|
||||
- [ ] Persona-gaten **blokkerer** dokumentert (stryk → omskriv før godkjenning), ikke bare annoterer.
|
||||
|
||||
## 7. Åpne spørsmål / research-plan (start her i sesjon 1)
|
||||
|
||||
- Kartlegg gjeldende plugin-struktur (v1.2.0: ~27 kommandoer, 16 agenter, 9 hooks, 6 skills) — hvor hører gate/ledger/mal hjemme (ny agent? skill-steg i `pipeline`/`post`? hook?).
|
||||
- Hvordan formaliseres en *blokkerende* verdikt-retur i arbeidsflyten (agent → strukturert PASS/FAIL som stopper neste steg)?
|
||||
- Ledger-format: nytt asset (`assets/claims/…`)? Knyttet til `differentiation-checker`/research?
|
||||
- Sekvensering mot eksisterende v1.3.0-plan (posting-API) — uavhengig spor eller felles release?
|
||||
- Verifiser at voice-profilen (`config/user-profile.local.md`) har en «unngå»-seksjon å utvide.
|
||||
|
||||
---
|
||||
|
||||
*Skrevet fra produksjonserfaringen i Maskinrommet. Plugin-endringene utføres i LTL-repoet, ikke her.*
|
||||
402
plugins/linkedin-studio/docs/integration-test-guide.md
Normal file
402
plugins/linkedin-studio/docs/integration-test-guide.md
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
# Integration Test Guide: LinkedIn Studio Plugin
|
||||
|
||||
Manual integration testing scenarios for commands, agents, and hooks in the plugin.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before testing, ensure:
|
||||
- [ ] `~/.claude/linkedin-studio.local.md` exists (create from `config/state-file.template.md`)
|
||||
- [ ] Voice samples exist in `assets/voice-samples/authentic-voice-samples.md`
|
||||
- [ ] Quality scorecard exists at `assets/checklists/quality-scorecard.md`
|
||||
- [ ] Plugin is installed: appears in Claude Code's skill/command list
|
||||
|
||||
## /linkedin:pipeline — End-to-End Tests
|
||||
|
||||
### Test 1: Full Pipeline — Idea to Post
|
||||
|
||||
**Goal:** Execute the complete 8-step pipeline from ideation to publish-ready post.
|
||||
|
||||
**Steps:**
|
||||
1. Run `/linkedin:pipeline`
|
||||
2. Verify Step 0 loads: state file read, status displayed (posts/week, streak)
|
||||
3. Choose "Generate ideas for me" when prompted
|
||||
4. Verify 3 topic suggestions appear, drawn from `thought-leadership-angles.md`
|
||||
5. Select a topic → verify angle selection (2-3 options)
|
||||
6. Choose format → verify draft follows structure (hook/context/insight/implication/CTA)
|
||||
7. Verify optimization checks run:
|
||||
- Hook: 110-140 chars
|
||||
- Total: 1,200-1,800 chars
|
||||
- No external links in body
|
||||
- No corporate buzzwords
|
||||
8. Verify scheduling recommendation mentions CET times
|
||||
9. Verify 5x5x5 guidance is provided
|
||||
10. Verify copy-paste ready output with character count and hashtags
|
||||
11. Verify first-hour monitoring plan is shown
|
||||
12. Verify 48-hour check-in reminder appears
|
||||
|
||||
**Expected outcome:** A complete, publish-ready post with all quality checks passed.
|
||||
|
||||
**Hooks that fire:**
|
||||
- `SessionStart` → loads state
|
||||
- `UserPromptSubmit` → injects context
|
||||
- `PreToolUse (Write)` → quality gate + voice guardian (if draft is written to file)
|
||||
- `PostToolUse (Write)` → alternative hooks + posting time suggestion
|
||||
- `Stop` → state update + pre-publish reminders
|
||||
|
||||
### Test 2: Pipeline with Existing Topic
|
||||
|
||||
**Goal:** User provides their own topic, skipping ideation.
|
||||
|
||||
**Steps:**
|
||||
1. Run `/linkedin:pipeline`
|
||||
2. Choose "I have an idea already"
|
||||
3. Provide topic: "Why AI agents will replace workflows in 2026"
|
||||
4. Verify the topic is used directly (no override)
|
||||
5. Verify angle suggestions are relevant to the provided topic
|
||||
6. Complete the remaining steps
|
||||
|
||||
**Expected outcome:** Post is created on the user's topic, not a generated one.
|
||||
|
||||
### Test 3: Pipeline with State File Missing
|
||||
|
||||
**Goal:** Graceful handling when state file doesn't exist.
|
||||
|
||||
**Steps:**
|
||||
1. Temporarily rename `~/.claude/linkedin-studio.local.md`
|
||||
2. Run `/linkedin:pipeline`
|
||||
3. Verify: no crash, reasonable fallback (e.g., "No posting data found. Starting fresh.")
|
||||
4. Complete the pipeline
|
||||
5. Verify: state file is created after pipeline completes
|
||||
|
||||
**Expected outcome:** Pipeline works without state file, creates one at the end.
|
||||
|
||||
### Test 4: Pipeline — Draft Save Option
|
||||
|
||||
**Goal:** Verify "Save as draft for later" works.
|
||||
|
||||
**Steps:**
|
||||
1. Run `/linkedin:pipeline`
|
||||
2. Create a post
|
||||
3. At scheduling step, choose "Save as draft for later"
|
||||
4. Verify: no posting reminders (5x5x5, first-hour) are shown for drafts
|
||||
5. Verify: state file is NOT updated with post date (it's a draft, not published)
|
||||
|
||||
**Expected outcome:** Draft is saved without publishing-related actions.
|
||||
|
||||
---
|
||||
|
||||
## /linkedin:batch — End-to-End Tests
|
||||
|
||||
### Test 5: Full Batch — 3 Posts from One Theme
|
||||
|
||||
**Goal:** Create 3 posts from a single theme with varying angles and formats.
|
||||
|
||||
**Steps:**
|
||||
1. Run `/linkedin:batch`
|
||||
2. Verify Step 0 loads: state file, check for existing weekly plan
|
||||
3. Choose "One main theme"
|
||||
4. Provide theme: "The future of AI in public sector"
|
||||
5. Verify batch plan shows 3 posts with:
|
||||
- Different angles (not repetitive)
|
||||
- Mixed formats (not all the same)
|
||||
- Different target days
|
||||
6. Approve the plan
|
||||
7. Verify each post:
|
||||
- Follows structure (hook 110-140 chars, 1,200-1,800 total)
|
||||
- Has unique angle
|
||||
- Quick quality check passes
|
||||
8. Verify posts are saved to `assets/drafts/week-[WXX]/`
|
||||
9. Verify filenames follow pattern: `[day]-[topic-slug].md`
|
||||
10. Verify YAML frontmatter in each file (planned_date, pillar, angle, format, status)
|
||||
11. Verify summary shows content mix and pillar coverage
|
||||
12. Approve all drafts
|
||||
13. Verify posting schedule with recommended times
|
||||
|
||||
**Expected outcome:** 3 distinct posts saved in correct directory with proper metadata.
|
||||
|
||||
### Test 6: Batch — Content Pillar Mode
|
||||
|
||||
**Goal:** Batch using existing content pillar.
|
||||
|
||||
**Steps:**
|
||||
1. Run `/linkedin:batch`
|
||||
2. Choose "Content pillar"
|
||||
3. Select from user's defined pillars in skill file
|
||||
4. Verify posts are created around that pillar
|
||||
5. Verify angle variety (not same perspective repeated)
|
||||
|
||||
**Expected outcome:** All posts align with chosen pillar but explore different angles.
|
||||
|
||||
### Test 7: Batch — Revision Flow
|
||||
|
||||
**Goal:** Verify post revision during batch creation.
|
||||
|
||||
**Steps:**
|
||||
1. Run `/linkedin:batch` and create 3 posts
|
||||
2. At review step, choose "Revise a specific post"
|
||||
3. Ask for post #2 to be revised (e.g., "Make the hook more provocative")
|
||||
4. Verify: only post #2 is changed, others remain intact
|
||||
5. Verify: summary updates to reflect the revised post
|
||||
|
||||
**Expected outcome:** Individual post revision works without affecting other batch posts.
|
||||
|
||||
### Test 8: Batch — Drafts Directory Creation
|
||||
|
||||
**Goal:** Verify `assets/drafts/` directory is created when it doesn't exist.
|
||||
|
||||
**Steps:**
|
||||
1. Ensure `assets/drafts/` does not exist
|
||||
2. Run `/linkedin:batch` and complete the workflow
|
||||
3. Verify: `assets/drafts/week-[WXX]/` directory is created
|
||||
4. Verify: all posts are saved correctly
|
||||
|
||||
**Expected outcome:** Directory is created automatically, posts are saved.
|
||||
|
||||
---
|
||||
|
||||
## Cross-Command Integration Tests
|
||||
|
||||
### Test 9: Pipeline After Batch
|
||||
|
||||
**Goal:** Pipeline uses batch-created drafts.
|
||||
|
||||
**Steps:**
|
||||
1. First run `/linkedin:batch` to create 3 drafts
|
||||
2. Then run `/linkedin:pipeline`
|
||||
3. At ideation, choose "Use a planned topic"
|
||||
4. Verify: pipeline picks up a draft from the batch
|
||||
5. Complete pipeline with the batch draft
|
||||
6. Verify: state file is updated after publishing
|
||||
|
||||
**Expected outcome:** Pipeline can consume batch-created drafts seamlessly.
|
||||
|
||||
### Test 10: Batch Respects Weekly State
|
||||
|
||||
**Goal:** Batch adjusts recommendations based on current posting state.
|
||||
|
||||
**Steps:**
|
||||
1. Set state file to show 2 posts already published this week
|
||||
2. Run `/linkedin:batch` with goal of 3 posts/week
|
||||
3. Verify: batch suggests creating only 1 post (3 - 2 = 1 remaining)
|
||||
4. Or if configurable, verify batch mentions current progress
|
||||
|
||||
**Expected outcome:** Batch is aware of weekly posting status.
|
||||
|
||||
---
|
||||
|
||||
## Hook Integration Tests
|
||||
|
||||
### Test 11: Quality Gate Fires on Post Draft
|
||||
|
||||
**Goal:** Verify PreToolUse quality gate hook catches issues.
|
||||
|
||||
**Steps:**
|
||||
1. During pipeline or batch, intentionally create a post with:
|
||||
- Hook over 140 chars
|
||||
- External link in body
|
||||
- Corporate buzzword ("leverage")
|
||||
2. Verify: quality gate flags ALL issues
|
||||
3. Verify: issues are described specifically (not generic warnings)
|
||||
|
||||
**Expected outcome:** Quality gate catches all three violations with specific feedback.
|
||||
|
||||
### Test 12: Voice Guardian Detects AI Patterns
|
||||
|
||||
**Goal:** Verify voice guardian hook catches AI-sounding content.
|
||||
|
||||
**Steps:**
|
||||
1. During pipeline, create a post that starts with "In today's rapidly evolving landscape..."
|
||||
2. Verify: voice guardian flags the AI pattern
|
||||
3. Verify: specific rewrite suggestions are provided
|
||||
4. Verify: voice samples are referenced for comparison (if they exist)
|
||||
|
||||
**Expected outcome:** Voice guardian identifies AI patterns and suggests authentic alternatives.
|
||||
|
||||
### Test 13: Stop Hook Updates State
|
||||
|
||||
**Goal:** Verify session-end state update works correctly.
|
||||
|
||||
**Steps:**
|
||||
1. Run `/linkedin:pipeline` and create a post
|
||||
2. Note the topic and hook
|
||||
3. End the session (or let Stop hook fire)
|
||||
4. Read `~/.claude/linkedin-studio.local.md`
|
||||
5. Verify:
|
||||
- `last_post_date` = today
|
||||
- `last_post_topic` = the topic used
|
||||
- `posts_this_week` incremented
|
||||
- `current_streak` updated correctly
|
||||
- Recent Posts section has new entry
|
||||
|
||||
**Expected outcome:** State file accurately reflects the session's output.
|
||||
|
||||
### Test 14: PostToolUse Generates Alternative Hooks
|
||||
|
||||
**Goal:** Verify post-creation automation fires.
|
||||
|
||||
**Steps:**
|
||||
1. During pipeline or batch, write a post draft
|
||||
2. Verify: 3 alternative hooks are generated
|
||||
3. Verify: each alternative has character count shown
|
||||
4. Verify: optimal posting time is suggested
|
||||
5. Verify: 5x5x5 reminder appears
|
||||
|
||||
**Expected outcome:** Post-creation automation provides actionable suggestions.
|
||||
|
||||
---
|
||||
|
||||
## Agent Tests
|
||||
|
||||
### Test 15: Post-Feedback Monitor — Basic Monitoring
|
||||
**Command:** Trigger `post-feedback-monitor` agent
|
||||
**Steps:**
|
||||
1. Say "How is my latest post doing?"
|
||||
2. Agent should load algorithm-signals-reference and engagement-frameworks
|
||||
3. Agent should ask which post to monitor
|
||||
4. Provide sample metrics: 500 impressions, 15 reactions, 3 comments, 1 repost
|
||||
5. Agent should identify the current phase and provide benchmarks
|
||||
**Expected:** Structured output with metrics snapshot, velocity score, anomaly detection, and recommended actions
|
||||
**Validates:** Agent file loads correctly, context loading works, output format matches spec
|
||||
|
||||
### Test 16: Post-Feedback Monitor — Anomaly Detection
|
||||
**Command:** Trigger `post-feedback-monitor` agent
|
||||
**Steps:**
|
||||
1. Say "My post has 2000 impressions but only 5 reactions"
|
||||
2. Agent should detect "Impression-Engagement Gap" anomaly
|
||||
3. Agent should provide specific intervention recommendations
|
||||
**Expected:** Anomaly correctly identified with cause analysis and action plan
|
||||
**Validates:** Anomaly detection framework, intervention playbook
|
||||
|
||||
### Test 17: Post-Feedback Monitor — Golden Hour
|
||||
**Command:** Trigger `post-feedback-monitor` agent
|
||||
**Steps:**
|
||||
1. Say "I just posted 30 minutes ago, what should I do?"
|
||||
2. Agent should activate Golden Hour protocol
|
||||
3. Agent should provide time-sensitive action items
|
||||
**Expected:** Golden Hour specific advice (reply within 5 min, DM connections, first comment strategy)
|
||||
**Validates:** Phase detection, time-sensitive interventions
|
||||
|
||||
---
|
||||
|
||||
## Command Tests
|
||||
|
||||
### Test 18: A/B Test — Design New Test
|
||||
**Command:** `/linkedin:ab-test`
|
||||
**Steps:**
|
||||
1. Run the command
|
||||
2. Select "Design a new A/B test"
|
||||
3. Choose "Hook/Opening line" as the variable
|
||||
4. Follow the guided workflow
|
||||
**Expected:** Complete test plan with hypothesis, variants, execution schedule, success criteria
|
||||
**Validates:** Command loads, AskUserQuestion flow works, reference file loads, test plan file created
|
||||
|
||||
### Test 19: A/B Test — Analyze Results
|
||||
**Command:** `/linkedin:ab-test`
|
||||
**Steps:**
|
||||
1. First create a test plan (Test 18) and manually create a test file with sample data
|
||||
2. Run `/linkedin:ab-test` and select "Analyze test results"
|
||||
3. Select the test to analyze
|
||||
**Expected:** Results comparison table, significance assessment (20% rule), verdict, recommended next steps
|
||||
**Validates:** File scanning, data analysis, result formatting
|
||||
|
||||
### Test 20: Enhanced Report — Trends & Alerts
|
||||
**Command:** `/linkedin:report`
|
||||
**Steps:**
|
||||
1. Ensure at least 4 weeks of imported data exists
|
||||
2. Run `/linkedin:report` for the current week
|
||||
3. Verify trend analysis section appears after main report
|
||||
4. Verify alert detection section appears
|
||||
**Expected:** 4-week trend table, trend interpretation, performance alerts, algorithm alerts
|
||||
**Validates:** Trend CLI integration, alert thresholds, formatting
|
||||
|
||||
### Test 21: Enhanced Import — Anomaly Detection
|
||||
**Command:** `/linkedin:import`
|
||||
**Steps:**
|
||||
1. Ensure baseline data exists (previous imports)
|
||||
2. Import a new CSV export
|
||||
3. After import, verify anomaly detection runs
|
||||
**Expected:** Breakout posts flagged, patterns detected, intelligent next steps offered
|
||||
**Validates:** Anomaly detection rules, baseline comparison, conditional suggestions
|
||||
|
||||
### Test 22: Enhanced Report — Markdown Export
|
||||
**Command:** `/linkedin:report`
|
||||
**Steps:**
|
||||
1. Run `/linkedin:report` for any week with data
|
||||
2. Select "Export as Markdown" from options
|
||||
3. Verify file is saved to `assets/analytics/weekly-reports/YYYY-WXX-report.md`
|
||||
**Expected:** Clean markdown file with all sections (metrics, trends, alerts, top performers, recommendations)
|
||||
**Validates:** Export template, file creation, gitignore compliance
|
||||
|
||||
---
|
||||
|
||||
## Cross-Command Integration Tests
|
||||
|
||||
### Test 23: Router — New Commands Accessible
|
||||
**Command:** `/linkedin`
|
||||
**Steps:**
|
||||
1. Run `/linkedin`
|
||||
2. Verify A/B test appears in command menu
|
||||
3. Verify post-feedback-monitor appears in agent suggestions
|
||||
4. Say "I want to A/B test my hooks" — should route to `/linkedin:ab-test`
|
||||
5. Say "How is my post doing?" — should route to `post-feedback-monitor`
|
||||
**Expected:** All new commands and agents are accessible through the router
|
||||
**Validates:** Router updates, intent matching
|
||||
|
||||
### Test 24: Collaboration — Multi-Author Workflow
|
||||
**Command:** `/linkedin:collab`
|
||||
**Steps:**
|
||||
1. Run `/linkedin:collab` and complete readiness check
|
||||
2. Navigate to multi-author content coordination section
|
||||
3. Verify co-creation workflow templates are available
|
||||
4. Verify collaboration tracking section exists
|
||||
**Expected:** Multi-author workflow with 5 phases, shared draft guidelines, collaboration pipeline board
|
||||
**Validates:** New collab command sections (Step 7 and Step 8)
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **No automated testing:** These commands are conversational — they require human interaction at AskUserQuestion steps. Testing must be manual.
|
||||
|
||||
2. **State file format:** State file uses YAML frontmatter. Any malformed YAML will cause parsing issues. Always validate format after manual edits.
|
||||
|
||||
3. **Draft directory:** `assets/drafts/` and `assets/plans/` are created at runtime. They don't exist in the base plugin directory and won't appear until first use.
|
||||
|
||||
4. **Hook ordering:** PreToolUse has two hooks (quality gate + voice guardian). Both fire on every Write/Edit of content files. If one blocks, the user must fix the issue before proceeding.
|
||||
|
||||
5. **Content vs. config detection:** All prompt-based hooks include logic to skip non-content files. This relies on heuristic pattern matching (checking for `.local.md`, `.json`, script extensions, etc.). Edge cases may exist.
|
||||
|
||||
6. **Agent testing:** Agents (Tests 15-17) are triggered conversationally, not via slash commands. They require natural language input and cannot be invoked deterministically. Test by using the trigger phrases documented in the agent frontmatter.
|
||||
|
||||
7. **Structure validation:** Use `scripts/test-runner.sh` to validate file existence, frontmatter format, and router completeness. This is automated and complements the manual integration tests above.
|
||||
|
||||
## Test Results Log
|
||||
|
||||
Record results here when tests are executed:
|
||||
|
||||
| Test | Date | Result | Notes |
|
||||
|------|------|--------|-------|
|
||||
| 1 | | | |
|
||||
| 2 | | | |
|
||||
| 3 | | | |
|
||||
| 4 | | | |
|
||||
| 5 | | | |
|
||||
| 6 | | | |
|
||||
| 7 | | | |
|
||||
| 8 | | | |
|
||||
| 9 | | | |
|
||||
| 10 | | | |
|
||||
| 11 | | | |
|
||||
| 12 | | | |
|
||||
| 13 | | | |
|
||||
| 14 | | | |
|
||||
| 15 | | | |
|
||||
| 16 | | | |
|
||||
| 17 | | | |
|
||||
| 18 | | | |
|
||||
| 19 | | | |
|
||||
| 20 | | | |
|
||||
| 21 | | | |
|
||||
| 22 | | | |
|
||||
| 23 | | | |
|
||||
| 24 | | | |
|
||||
473
plugins/linkedin-studio/docs/plan-fullspektrum-innholdsmotor.md
Normal file
473
plugins/linkedin-studio/docs/plan-fullspektrum-innholdsmotor.md
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
# Plan — LTL som fullspektrum LinkedIn-innholdsmotor
|
||||
|
||||
> **Type:** Renoverings- og byggeplan for `linkedin-studio`-pluginen («LTL»).
|
||||
> **Skrevet:** 2026-05-26. **Status:** Til godkjenning før bygging. **Versjonsmål:** v1.2.0 → v2.0.0.
|
||||
> **Følger av:** [brief-fullspektrum-innholdsmotor.md](./brief-fullspektrum-innholdsmotor.md) (samme mappe).
|
||||
>
|
||||
> **LES §0 FØRST.** Denne planen utføres sesjon-for-sesjon. Hver sesjon starter med tom kontekst (etter `/clear`) og har KUN denne fila + `BUILD-HANDOVER.local.md` + kildene §0 peker til. Derfor er alle navn, stier og begreper definert eksplisitt i §0 — ingenting forutsettes kjent.
|
||||
|
||||
---
|
||||
|
||||
## 0. Orientering for en ny sesjon
|
||||
|
||||
Denne seksjonen gjør planen selvstendig. Hvis et begrep brukes senere uten forklaring, slå det opp her.
|
||||
|
||||
### 0.1 Repoer og absolutte stier
|
||||
|
||||
| Det | Hva | Absolutt sti | Rolle her |
|
||||
|-----|-----|--------------|-----------|
|
||||
| **LTL-pluginen** | Claude Code-plugin for LinkedIn thought leadership. v1.2.0. 27 kommandoer, 16 agenter, 9 hooks, 6 skills. | `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/linkedin-studio/` | **DETTE er repoet vi bygger i.** All plugin-endring skjer her. |
|
||||
| **Marketplace-rot** | Open-source plugin-marketplace (flere plugins + `shared/`). Distribueres via Forgejo: `git.fromaitochitta.com/open/ktg-plugin-marketplace` (aldri GitHub). | `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/` | Rot-`README.md` må oppdateres ved feature-endring (doc-plikt). |
|
||||
| **maskinrommet** | Privat, lokalt repo (INGEN git-remote) der operatøren produserer alt LinkedIn-innhold. Inneholder `tools/` (4 render-skript + `fonts/`) og `serier/<slug>/` (én innholdsserie per mappe). | `/Users/ktg/repos/maskinrommet/` | **Annet repo enn pluginen.** Skriving hit krever eksplisitt instruks (cross-repo). Vi leser render-skriptene herfra i S1. |
|
||||
| **Voyage-pluginen** | En annen plugin i samme marketplace. Implementerer en kontrakt-drevet, multi-sesjons pipeline for KODE-prosjekter: kommandoene `/trekbrief` → `/trekresearch` → `/trekplan` → `/trekexecute` → `/trekreview`, med parallelle spesialist-agenter og adversarielle reviewere. | `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/voyage/` | **Referanse/inspirasjon, IKKE avhengighet.** Vi løfter *mønsteret* (faset pipeline, parallelle agenter, adversariell review før lås, multi-sesjon), men kopierer ALDRI koden — Voyage er kode-spesifikk (`file:line`, kode-reviewere, RULE_CATALOGUE). Les den for mønster-inspirasjon. |
|
||||
| **svv-memory** | Memory-filer skrevet under produksjonen av Seres-serien (se 0.2). Kilden til prosess-erfaringen denne planen generaliserer. | `/Users/ktg/.claude/projects/-Users-ktg-repos-svv/memory/` | Les ved behov for dyp prosess-kontekst (se nøkkelfiler i 0.4). |
|
||||
|
||||
### 0.2 Hva «Seres-serien» er
|
||||
|
||||
Operatøren (Kjell Tore Guttormsen) produserte en kronikkserie på 6 deler — internt kalt «Seres-serien» eller «silvija-seres-motsvar» — i ~10 arbeidsøkter i mai 2026. Den utgjør de første utgavene av LinkedIn-nyhetsbrevet «Maskinrommet» (0.3). Serien ligger i `/Users/ktg/repos/maskinrommet/serier/silvija-seres-motsvar/`. Produksjonen avdekket en research- og review-tung prosess (0.4) som denne planen generaliserer til en gjenbrukbar pipeline. **«Seres-erfaringen»** = lærdommene derfra. Når planen sier «Seres beviste/avdekket X», betyr det «erfaringen fra denne serien viste X», dokumentert i svv-memory (0.1).
|
||||
|
||||
### 0.3 «Maskinrommet»-nyhetsbrevet
|
||||
|
||||
Operatørens ukentlige LinkedIn-nyhetsbrev, etablert 2026-05-25 (URL: `linkedin.com/newsletters/maskinrommet-7464605936645509120`). Status ved planskriving: ~30 abonnenter, 6 editions (Seres-serien) ferdig produsert og klare for utrulling. **LinkedIn-fakta** (verifisert): det finnes INGEN API for nyhetsbrev/long-form — publisering skjer ved manuell innliming av rik tekst i LinkedIns editor. Men LinkedIn har **native planlegging** (dato + klokkeslett i 15-min-intervaller), så hele utrullingen kan forhåndsplanlegges. Cover-bilde: 1920×1080. En **edition** = én utgave = én kronikk/artikkel + distribusjonspakke (hook-tekst, hashtags, første kommentar, cover).
|
||||
|
||||
### 0.4 De 16 prosessfasene (Seres-erfaringen, kondensert)
|
||||
|
||||
Dette er den faktiske flyten som ga kvalitet i Seres-produksjonen. Den nye nyhetsbrev-kommandoen (§5) er en kondensering av denne. Kilde: `…/svv/memory/project_kronikk_produksjonsprosess.md`.
|
||||
|
||||
1. **Kalibrerings-intervju** — tid/målgruppe/mål; ~3 spørsmål før skriving.
|
||||
2. **Front-loadet kontekst** — brief + research + sitatbank lest FØR skriving (suksessfaktor).
|
||||
3. **Research + verifiseringsplikt** — hver påstand mot primærkilde.
|
||||
4. **Serie-overgripende regler** etableres (stil/term/tone-låser).
|
||||
5. **Utkast** via Opus-subagent — én artikkel om gangen, med leserekkefølge-kontekst.
|
||||
6. **Kritisk-leser-simulering** — «fortjener dette publisering?» → konkret kritikk, ikke skryt.
|
||||
7. **Gap-tabell + gap-lukking** — hvert gap lukkes med *tightening/bytte, ikke utvidelse*; lengde flat.
|
||||
8. **Annoterings-loop** — render annoterbar review-HTML (verktøyet `build-html.mjs`, 0.6), marker → kommenter → eksporter.
|
||||
9. **Konsistens-pass** — tråder, pronomen-ordning, kryss-referanser, tall, dramaturgi.
|
||||
10. **Kvalitets-sweep** — ett klart leder-poeng + én konkret handling per tekst; premiss→konklusjon-bue (0.5).
|
||||
11. **Faktasjekk-sweep** — EGET steg; parallelle agenter; alle påstander «skyldig til motbevist» (0.5).
|
||||
12. **Lesbarhets-/formaterings-sweep** — MINIMALT (0.5, formaterings-dose).
|
||||
13. **Persona-/audience-sweep** — leser-jury tester om teksten LANDER (0.5).
|
||||
14. **Verifiserings-loop** — kjør personaene PÅ NYTT mot oppdatert tekst til rent JA (0.5, konvergens-loop).
|
||||
15. **LÅS → leveranse** — POST.html (0.5) og/eller avis-PDF.
|
||||
16. **Distribusjons-/hook-gate** — konverterings-gate på distribusjonsteksten, ETTER lås (0.5).
|
||||
|
||||
**Den enkeltstående største prosessfeilen i Seres:** persona-sweepen (fase 13) ble opprinnelig kjørt ETTER at tekstene var låst (fase 15), noe som tvang gjenåpning av låste tekster. Den generaliserte malen MÅ derfor ha persona-sweep FØR lås. (Dette er grunnen til rekkefølgen i prinsipp 5, §3.)
|
||||
|
||||
Nøkkelfiler i svv-memory for dypere kontekst: `project_kronikk_produksjonsprosess.md` (fasene + Voyage-mapping), `project_kronikk_faktasjekk_sweep.md`, `feedback_persona_audience_sweep.md`, `feedback_hook_post_persona_gate.md`, `feedback_post_html_single_sheet.md`, `project_maskinrommet_newsletter.md`.
|
||||
|
||||
### 0.5 Ordliste (begreper brukt senere)
|
||||
|
||||
- **Edition** — én utgave av nyhetsbrevet (0.3).
|
||||
- **Faktasjekk-sweep** — eget steg der hver faktapåstand (tall, navngitte eksempler, sitater, datoer, hvem-gjorde-hva) verifiseres mot primærkilde, etter prinsippet «**skyldig til motbevist**» (antas feil til den er verifisert; aldri fyll hull med gjetninger). I Seres ble ~15 feil fanget.
|
||||
- **«Altinn-feilen»** — det konkrete funnet som beviste at faktasjekk må være et eget steg: i et utkast ble Altinn brukt som eksempel på «bygd i eget hus / internt eierskap», men Accenture var i realiteten hovedleverandør — altså nær et mot-eksempel. Verken research-filene eller en subagents resonnement fanget det; operatørens egen hukommelse gjorde. Lærdom: vær kritisk til ALT, også det som «føles» riktig og det som står i egne research-notater.
|
||||
- **Persona-/audience-sweep** — en adversariell **leser-jury**: navngitte leser-personaer (definert fra målgruppen) leser den ferdige teksten *read-only* og dømmer om den LANDER (ikke om den er «riktig»). Personaene skriver ALDRI tekst — de gir retning; redaktøren (hovedkonteksten) holder pennen. Dette er kronikk-ekvivalenten til Voyages adversarielle kode-reviewere.
|
||||
- **«Primær trumfer»** — én persona er utpekt som primærleser. Ved konflikt mellom personaer vekter primær høyest. En *sekundær*-NEI på grunn av rolle-mismatch eller ekspertise-tak («dette vet jeg alt om fra før») er et SIGNAL om at gaten virker, ikke en svikt — godta den, ikke forvreng primærteksten for å jage den. En *primær*-NEI godtas derimot ikke.
|
||||
- **Konvergens-loop (LØST / DELVIS / IKKE)** — etter at jury-flagg er foldet inn i teksten, kjøres personaene PÅ NYTT for å bekrefte at endringene faktisk landet (ikke bare at teksten ble endret). Hvert tidligere flagg dømmes LØST / DELVIS LØST / IKKE LØST, til rent JA fra primær. I Seres tok dette 2 runder.
|
||||
- **Hook-/konverterings-gate** — en EGEN persona-gate, men på **distribusjonsteksten** (feed-hooken / «Tell your network»-teksten), spisset for konvertering: binær JA/NEI på «ville DU klikket videre?» — ikke «er den god». Kjøres ETTER lås (teksten bak er ferdig). **«Hold tilbake leveransen, ikke konklusjonen»**: en hook som gir bort tallet/caset/listen/grepet i feeden får ros, men ingen klikk; hold igjen beviset og la et åpent spørsmål peke inn i artikkelen. Konklusjonen kan stå.
|
||||
- **POST.html «alt på ett sted»** — ett selvforsynt publiseringsark per edition, generert av `build-linkedin.mjs` (0.6). Inneholder i publiseringsrekkefølge: dato + klokkeslett (+ ev. ferskvare-banner), tittel/SEO, cover-filnavn + credit + caption, delingstekst inkl. hashtags, første kommentar, ev. carousel-referanse, og brødteksten som rik tekst klar til innliming. Formålet: legg inn én edition i én operasjon uten å hoppe mellom filer.
|
||||
- **Ferskvare-flagg** — markering i POST.html av tids-sensitive tall (f.eks. en selskaps-verdsettelse) som MÅ re-verifiseres på publiseringsdagen. En publiseringsdag-sjekkliste, ikke en tekstsvakhet.
|
||||
- **voice-profil / voice-samples** — operatørens skrivestemme. Lagret i `config/user-profile.local.md` + `assets/voice-samples/` i pluginen. **Leses ALLTID før innholdsproduksjon** (eksisterende LTL-regel).
|
||||
- **360Brew** — LinkedIns rangerings-/anbefalingsmodell (oppdatering jan. 2026). LTL optimaliserer innhold mot dens signaler.
|
||||
- **5x5x5** — eksisterende LTL-engasjementstaktikk (kommenter/engasjer i et mønster rundt egen post). Ikke relevant for langform.
|
||||
- **CEA** — kommenteringsmetode i `comment-strategist`-agenten. Ikke relevant for langform.
|
||||
- **PreToolUse-gate / content-gatekeeper / voice-guardian** — eksisterende LTL-hooks som gir rådgivende kvalitets-/stemme-advarsler når kortform-innhold skrives. Kalibrert for feed-poster.
|
||||
|
||||
### 0.6 Render-skriptene (i `maskinrommet/tools/`, flyttes til pluginen i S1)
|
||||
|
||||
Fire zero-/lav-avhengighets Node-skript. Felles invariant: kjøres ALLTID med `cwd` = serie-mappa (output skrives relativt til `process.cwd()`); fonts lastes via skriptets `__dirname`. Kontrakt:
|
||||
|
||||
| Skript | Kall | Output | Avhengighet |
|
||||
|--------|------|--------|-------------|
|
||||
| `build-html.mjs` | `node …/build-html.mjs utkast/NN-*.md` | `review/NN-*.html` — selvstendig annoterbar HTML (marker → intent → kommentar → localStorage → eksport). **Dette er annoteringsverktøyet** (beslutning H, §7.5). | Zero-dep |
|
||||
| `build-linkedin.mjs` | `node …/build-linkedin.mjs utkast/0*.md` | `linkedin/NN/POST.html` + `linkedin/samle/POST.html` (0.5). Leser også `linkedin/edition-delingstekst.md`. **NB: har i dag hardkodet Seres-kalender/captions/ferskvare** — generaliseres i S2. | Zero-dep |
|
||||
| `build-carousel.mjs` | `node …/build-carousel.mjs linkedin/NN/carousel.md` | `carousel.pdf` (1080×1350) | Krever `weasyprint` på PATH |
|
||||
| `build-pdf.mjs` | `node …/build-pdf.mjs utkast/NN-*.md` | `pdf/NN-*.pdf` (avis-A4) | Krever `weasyprint` på PATH |
|
||||
|
||||
### 0.7 «Auditen» (gjennomført i sesjon S0, frosset inn i §4)
|
||||
|
||||
Før denne planen ble alle 27 kommandoene og 16 agentene i LTL lest og vurdert for overlapp, redundans og langform-relevans (sesjon S0, 2026-05-26). Konklusjonene er **frosset inn i §4 og §6** — en ny sesjon skal stole på dem og trenger IKKE kjøre auditen på nytt. Vil du verifisere ett enkelt konsoliderings-grep, les den aktuelle kommando-/agent-fila direkte (alle ligger i `commands/` og `agents/` i pluginen).
|
||||
|
||||
---
|
||||
|
||||
## 1. Sammendrag
|
||||
|
||||
LTL skal eie **hele kjeden for ALT LinkedIn-innhold** — fra kortform-post til nyhetsbrev-edition (0.3) — med samme kvalitetsnivå som Seres-erfaringen (0.2) viste er mulig. Pluginens svakeste område i dag er langform/nyhetsbrev: det finnes kun som referansestoff (`references/newsletter-strategy-guide.md`) og en nedstrøms-adaptasjonssnutt i `commands/multiplatform.md`, ikke som en førsteklasses idé→leveranse-flyt.
|
||||
|
||||
**Kjerneinnsikten fra auditen (0.7):** Vi kan legge til full langform-kapabilitet **og gjøre pluginen enklere samtidig**. Langform krever +1 kommando og +2 agenter, men auditen avdekket nok reell redundans til at vi netto **reduserer** overflaten gjennom konsolidering (§4). Resultatet: en plugin med færre, klarere kommandoer/agenter — pluss en ny tung kapabilitet.
|
||||
|
||||
**Den nye kapabiliteten** løfter mønstrene fra Voyage-pluginen (0.1) — faset pipeline, parallelle research-agenter, adversariell review, multi-sesjon — inn i LTL uten å kopiere Voyages kode. Leser-personaer (0.5) erstatter Voyages kode-reviewere; faktasjekk (0.5) blir et eget steg; persona-sweep kjøres FØR lås (0.4).
|
||||
|
||||
---
|
||||
|
||||
## 2. Beslutninger landet
|
||||
|
||||
Alle åpne beslutninger fra briefen (§8 der) pluss tre som dukket opp under planlegging. Disse er LÅST — en ny sesjon endrer dem ikke uten eksplisitt instruks fra operatøren.
|
||||
|
||||
| # | Beslutning | Valg | Kilde |
|
||||
|---|-----------|------|-------|
|
||||
| **A** | Kommando-struktur | **Én ny orkestrator-kommando** `/linkedin:newsletter` med interne faser + multi-sesjons-resumption. IKKE en suite med fem fase-kommandoer. IKKE en utvidelse av `commands/pipeline.md` (feil artefakt-kontrakt: pipeline er låst til feed-post-format). | Audit (0.7) + operatør |
|
||||
| **B** | Agent-deling | **Bare langform nå.** De eksisterende kortform-kommandoene røres ikke. Å dele de nye agentene inn i kortform på «variabel intensitet» utsettes til et eget, senere spor. | Operatør |
|
||||
| **C** | Render-eierskap | **Ship alle 4 render-skript (0.6) + fonts i pluginen** (under `render/`). Generaliser `build-linkedin.mjs`. maskinrommet blir konsument. Da får alle som laster ned LTL fra Forgejo hele produksjons-pipelinen. | Operatør |
|
||||
| **D** | Persona-sett | **Hybrid:** et gjenbrukbart persona-bibliotek i `config/`, der relevante personaer velges og justeres per prosjekt; primær (0.5) merkes per prosjekt. | Operatør |
|
||||
| **E** | Faktasjekk-omfang | **Eget steg** (ikke integrert i research eller review). «Altinn-feilen» (0.5) beviste at research-notatene bommer; faktasjekk må være en dedikert sweep. | Seres-erfaring (0.2) |
|
||||
| **F** | Renovering | **Konsolider redundans i samme runde** som langform bygges, slik at netto kommando-/agent-antall går NED (§4). | Audit (0.7) + operatør |
|
||||
| **G** | Produksjons-state | Edition-state/HANDOVER for en pågående nyhetsbrev-produksjon bor i **serie-mappa** (i maskinrommet), ikke i pluginens state-fil. Pluginen er en stateless motor; editions registreres i plugin-state KUN for kalender/scheduling. | Arkitektur (separasjon) |
|
||||
| **H** | Annoterings-renderer | `build-html.mjs` (0.6) generaliseres til en **førsteklasses plugin-kapabilitet for ALLE artefakter** (plan/brief/post/edition), ikke bare kronikker: tabeller, alle overskriftsnivåer, inline-kode, generisk frontmatter. Reference-impl ble produsert i sesjon S0b (§7.5). | Operatør |
|
||||
|
||||
---
|
||||
|
||||
## 3. Arkitektur-prinsipper
|
||||
|
||||
1. **Pluginen er motoren, maskinrommet er arbeidsbenken.** Innhold + produksjons-state for en serie bor i `/Users/ktg/repos/maskinrommet/serier/<slug>/` (0.1). Pluginen (0.1) leverer kommandoen, agentene, persona-biblioteket og render-skriptene. Ett unntak fra den tidligere modellen: render-skriptene flyttes INN i pluginen (beslutning C), men kjøres alltid med `cwd` = serie-mappa (0.6).
|
||||
2. **Gjenbruk mønstre, ikke kode.** Kopier *formen* på det som finnes: LTL-kommando-malen (YAML-frontmatter + nummererte `Step 0..N` + en `## Reference Files`-seksjon — se f.eks. `commands/pipeline.md`), agent-frontmatter-stilen (se f.eks. `agents/differentiation-checker.md`), hook-kompileringen (`hooks/hooks.template.json` + `hooks/prompts/*.md` + `python3 hooks/scripts/compile-hooks.py`), og det deterministiske state-mønsteret (`hooks/scripts/state-updater.mjs` + `queue-manager.mjs`). Fra Voyage-pluginen (0.1) løftes pipeline-*mønsteret* — ikke koden.
|
||||
3. **Langform-kvalitet håndheves av pipeline-fasene, ikke av PreToolUse-gaten.** De eksisterende kortform-hookene (content-gatekeeper / voice-guardian, 0.5) er kalibrert for feed-poster og forblir kortform-only (beslutning B). Langform får sin egen, tyngre review-maskineri: faktasjekk-sweep + persona-sweep + hook-gate (0.5). Langform-utkast trenger derfor IKKE ligge under `assets/drafts/` (der kortform-gatene fyrer) — de bor i serie-mappa i maskinrommet.
|
||||
4. **All agent-orkestrering skjer i forgrunn fra kommando-laget.** Research-, faktasjekk- og persona-fan-out gjøres via `Task`-kall fra selve kommandoen — ALDRI fra en nestet bakgrunns-agent. (Erfaring: en agent som spawnes i bakgrunn mister tilgang til `Task`/Agent-verktøyet og degraderer stille til gjetning i stedet for å parallellisere.)
|
||||
5. **Persona-sweep FØR lås.** Dette adresserer den største prosessfeilen i Seres (0.4). Den faste rekkefølgen er: utkast → konsistens → kvalitet → faktasjekk → persona-sweep → fold inn → LÅS → leveranse → hook-gate.
|
||||
6. **Forenkling er en førsteklasses leveranse**, ikke en bivirkning. Hvert nytt element måles mot om det øker eller senker total kompleksitet i pluginen.
|
||||
|
||||
---
|
||||
|
||||
## 4. Del 1 — Renovering (konsolidering)
|
||||
|
||||
Auditen (0.7) fant reell redundans. Grepene under reduserer overflaten og fjerner konkurrenter til den nye nyhetsbrev-kommandoen. Alle er **korreksjon-i-scope** (slå sammen / deleger eksisterende kapabilitet) — ingen ny funksjonalitet. Stol på funnene; vil du etterprøve ett grep, les de navngitte filene i `commands/` eller `agents/`.
|
||||
|
||||
### 4.1 Kommando-konsolidering (27 → ~23)
|
||||
|
||||
| Grep | Kommandoer (filer i `commands/`) | Begrunnelse | Risiko |
|
||||
|------|-----------|-------------|--------|
|
||||
| **SLÅ SAMMEN** | `templates.md` → en modus i `quick.md` | Begge bygger på de samme 8 posttypene, samme hooks-bank og samme tegnmål. Klareste redundansen i katalogen. | Lav |
|
||||
| **SLÅ SAMMEN** | `publish.md` → en handling i `calendar.md` | Begge leser samme kø (`queue.json`), viser overlappende lister; `calendar` ruter allerede til `publish`. | Lav |
|
||||
| **SLÅ SAMMEN** | `collab.md` + `speaking.md` → ny `outreach.md` | Strukturell tvilling: samme outreach-/pitch-paradigme og samme pipeline-tabell. Fjerner ~25–30 KB duplisert tekst. | Middels |
|
||||
| **ABSORBER** | `authority.md` → en seksjon i `strategy.md` | `authority` har ingen unik kjerne; den er sammensatt av biter fra strategy/audit/profile/multiplatform. | Lav |
|
||||
| **DEDUPLISER** | «trajectory»-logikk → bo kun i `strategy.md`; `audit.md` refererer dit | Identisk STATUS-tabell vedlikeholdes i dag to steder. | Lav |
|
||||
| **KANON** | `profile.md` blir kanonkilde for profil-alignment; `audit.md`/`analyze.md` peker dit | Samme profil-sjekk er re-implementert 4 steder. | Lav |
|
||||
| **TRIM** | analyse-delen (Step 6) i `import.md` → deleger til `report.md` | To rapport-generatorer kjører samme `trends`-CLI. | Lav |
|
||||
| **RECONCILE** | Flytt newsletter/blog-stien UT av `multiplatform.md` | Unngå to inngangsdører til langform når `/linkedin:newsletter` finnes. **Må skje sammen med Del 2 (S11).** | Middels |
|
||||
| **GATE** | I routeren `linkedin.md`: vis `monetize`/`outreach`/`collab` som «låses opp ved ~1K følgere» | Disse er aspirasjonelle for operatørens nåværende nivå (~30 følgere); skjuler kompleksitet uten å slette filer. | Lav |
|
||||
|
||||
### 4.2 Agent-konsolidering (16 → ~12, deretter +2 langform = ~14)
|
||||
|
||||
| Grep | Agenter (filer i `agents/`) | Resultat |
|
||||
|------|---------|----------|
|
||||
| **SLÅ SAMMEN** | `analytics-interpreter` + `performance-reporter` | 1 `analytics`-agent med to moduser (tolk / rapporter). Identiske datakilder. |
|
||||
| **SLÅ SAMMEN** | `engagement-coach` + `comment-strategist` | 1 `engagement`-agent (5x5x5 + first-hour + CEA-kommentering). |
|
||||
| **AVVIKLE → script** | `content-tracker` | Ren deterministisk plan-vs-kø-diff; hører hjemme i `calendar`/`state-updater.mjs`, ikke en LLM-agent. |
|
||||
| **AVVIKLE → script** | `personalization-scorer` | Ren placeholder-deteksjon; `hooks/scripts/personalization-score.mjs` finnes allerede. |
|
||||
| **VURDER** | `video-scripter` → `content-repurposer` | Marginal sammenslåing; la stå hvis dybden forsvarer egen fil (avgjøres i S19). |
|
||||
|
||||
**Nettoeffekt:** De 2 nye langform-agentene (`fact-checker`, `persona-reviewer`, §6) **finansieres** ved å avvikle de 2 deterministiske agentene. Pluss de to sammenslåingene → fra 16 mot ~14 agenter med klarere ansvarslinjer.
|
||||
|
||||
---
|
||||
|
||||
## 5. Del 2 — Langform-kapabiliteten (`/linkedin:newsletter`)
|
||||
|
||||
Én ny kommando-fil (`commands/newsletter.md`), med interne faser og multi-sesjons-resumption. Følger den eksisterende LTL-kommando-malen (prinsipp 2, §3). Re-kjøring av kommandoen oppdager edition-state (5.2) og fortsetter der forrige økt slapp.
|
||||
|
||||
### 5.1 Faser (kondensering av de 16 fasene i 0.4)
|
||||
|
||||
| Step | Fase | Hva | Agenter/verktøy |
|
||||
|------|------|-----|-----------------|
|
||||
| 0 | **Load context** | Les edition-state/HANDOVER (5.2) for resumption, voice-profil (0.5), persona-bibliotek (6.1), serie-brief | Read |
|
||||
| 1 | **Brief + kalibrering** | Vinkel, stemme, målgruppe-personaer (merk primær, 0.5), nøkkelpoeng, tone, leder-takeaway. Maks ~3 kalibrerings-spørsmål | AskUserQuestion |
|
||||
| 2 | **Research** | Parallelle, avgrensede mandater → verifiserte notater. Triangulering | **Task-fan-out** i forgrunn (prinsipp 4, §3) |
|
||||
| 3 | **Utkast** | Dramaturgisk rekkefølge, voice-matchet. Kan spenne flere sesjoner med vedlikeholdt HANDOVER | `content-repurposer` (utvidet) + Task |
|
||||
| 4 | **Konsistens + kvalitet** | Tråder, premiss→konklusjon-bue, leder-takeaway, AI-slop-fjerning, formaterings-dose (alt i §8) | inline + `references/longform-quality-rules.md` |
|
||||
| 5 | **Faktasjekk-sweep** | Risikosortert (🔴/🟡/🟢), «skyldig til motbevist», verifiseringslogg (0.5) | **`fact-checker`** (ny, parallell — 6.2) |
|
||||
| 6 | **Persona-sweep — FØR lås** | Leser-jury, primær trumfer, konvergens-loop til rent JA (0.5) | **`persona-reviewer`** (ny, kjøres én gang per persona — 6.3) |
|
||||
| 7 | **Annotering (valgfritt)** | Render annoterbar review-HTML for et manuelt pass | `render/build-html.mjs` (§7.5) |
|
||||
| 8 | **LÅS → leveranse** | POST.html «alt på ett sted» (0.5) | `render/build-linkedin.mjs` (0.6) |
|
||||
| 9 | **Hook-/konverterings-gate** | Persona-gate på distribusjonsteksten: «ville DU klikket?» — etter lås (0.5) | **`persona-reviewer`** i konverterings-modus (6.3) |
|
||||
| 10 | **Planlegging** | Registrer edition i pluginens kø/state for native scheduling (0.3) | `hooks/scripts/queue-manager.mjs` |
|
||||
|
||||
### 5.2 Edition-state / HANDOVER (beslutning G)
|
||||
|
||||
Produksjons-state for en pågående edition bor i serie-mappa (`/Users/ktg/repos/maskinrommet/serier/<slug>/`), ikke i pluginen. Følger HANDOVER-mønsteret fra Seres (referansefil: `/Users/ktg/repos/maskinrommet/serier/silvija-seres-motsvar/HANDOVER.md`):
|
||||
|
||||
- **§1 Hvor vi er nå** — status + låst artikkel-tabell
|
||||
- **§2 Publiseringskalender** — datoer
|
||||
- **§3 Leveransen** — POST.html-kontrakt + filkart over serie-mappa
|
||||
- **§4 Ufravikelige regler** — stil-/fakta-låser + faktasjekk-logg
|
||||
- **§5 Metode** — persona-kalibrering, gjenbrukbar prosess
|
||||
- **§6 Neste sesjon** — peker på neste handling
|
||||
|
||||
Kommandoen leser denne i Step 0 og oppdaterer den ved hver fase-overgang. Et lett `edition-state.json` (gjeldende fase + per-artikkel-status) kan komplettere for deterministisk resumption.
|
||||
|
||||
**Merk skillet mellom to HANDOVER-er:** (a) `docs/BUILD-HANDOVER.local.md` i pluginen styrer *byggingen av selve pluginen* (§9.2); (b) edition-HANDOVER i serie-mappa styrer *produksjonen av en nyhetsbrev-utgave*. De er ikke samme fil og blandes ikke.
|
||||
|
||||
### 5.3 Skill-plassering
|
||||
|
||||
Ingen ny skill opprettes. Langform legges som trigger/innhold i den eksisterende skillen `skills/linkedin-content-creation/SKILL.md` (som allerede dekker post/quick/batch/pipeline/templates/multiplatform).
|
||||
|
||||
---
|
||||
|
||||
## 6. Del 3 — Delte byggeklosser
|
||||
|
||||
### 6.1 Persona-bibliotek (beslutning D — hybrid)
|
||||
|
||||
- **`config/personas.template.md`** (+ en aktiv `config/personas.local.md`, gitignored via `*.local.md`): gjenbrukbare leser-profiler. Frø-personaene fra Seres (0.2): IT-divisjonsdirektør, KI-seksjonsleder, og linjeleder (primær).
|
||||
- Per persona dokumenteres: rolle, hva som kobler dem av, hva som overbeviser dem, ekspertise-nivå, sjargong-toleranse.
|
||||
- **Per-prosjekt-override:** `/linkedin:newsletter` velger relevante personaer fra biblioteket i Step 1 og merker primær i edition-briefen. (Om «primær trumfer», sekundær-NEI som signal osv., se 0.5.)
|
||||
|
||||
### 6.2 `fact-checker`-agent (ny — `agents/fact-checker.md`)
|
||||
|
||||
- **Modell:** Opus (verifiserings-resonnering; tillit-kritisk). **Tools:** `Read`, `WebSearch`.
|
||||
- **Mandat:** Gitt en bolk faktapåstander → verifiser hver mot primær-/troverdig kilde, etter «skyldig til motbevist» (0.5). Aldri fyll hull med gjetninger — flagg uverifisert eksplisitt. Returner en verifiseringslogg + risikosortering 🔴/🟡/🟢.
|
||||
- **Orkestrering:** `/linkedin:newsletter` (Step 5) lister alle påstander og fan-outer N parallelle `fact-checker`-kall (én per bolk) i forgrunn (prinsipp 4, §3), og samler loggene.
|
||||
- **Prompt-arketype:** kopier gate-strukturen fra `agents/differentiation-checker.md` (søk → vurder → gate-utfall; «søk før du dømmer» er ikke-forhandlbart) — men endre mandatet fra *originalitet* til *faktuell korrekthet*. (Bygg en ny fil; ikke utvid differentiation-checker — de to svarer på ortogonale spørsmål.)
|
||||
|
||||
### 6.3 `persona-reviewer`-agent (ny, parameterisert — `agents/persona-reviewer.md`)
|
||||
|
||||
- **Modell:** Opus (nyansert leser-simulering). **Tools:** `Read`.
|
||||
- **Mandat:** Les én persona-definisjon (fra 6.1) + teksten → døm på 6 akser: (1) holder hooken? (2) resonans — angår dette MEG? (3) tone — respektert vs. belært? (4) troverdighet — avvist som hype? (5) leder-takeaway + konkret handling? (6) lengde/driv? Returner topp-5 flagg med **retning, ikke ferdig omskriving**. Juryen skriver ALDRI tekst (0.5).
|
||||
- **To moduser i SAMME agent-fil** (parameter i kallet):
|
||||
- **Resonans-modus** (Step 6, FØR lås): «lander poenget for denne leseren?»
|
||||
- **Konverterings-modus** (Step 9, etter lås): binær JA/NEI på «ville DU klikket?» — kun på hook/distribusjonsteksten, ikke artikkelen bak (0.5).
|
||||
- **Konvergens-loop** (0.5): kommandoen kjører agenten på nytt mot oppdatert tekst og lar hver persona dømme LØST/DELVIS/IKKE per tidligere flagg, til rent JA fra primær.
|
||||
|
||||
### 6.4 Research-fan-out (ingen ny agent)
|
||||
|
||||
`/linkedin:newsletter` (Step 2) spawner parallelle `Task`-kall med inline-mandater (ett per delspørsmål), samler og triangulerer i forgrunn (prinsipp 4, §3). Ingen egen research-agent opprettes. Gjenbruk URL-fetch/multi-kilde-syntese-disiplinen som allerede finnes i `commands/react.md` der det er relevant.
|
||||
|
||||
---
|
||||
|
||||
## 7. Del 4 — Render-migrering (beslutning C)
|
||||
|
||||
### 7.1 Flytt render inn i pluginen
|
||||
|
||||
- Opprett mappen `render/` i pluginen og legg inn alle 4 skriptene (0.6) + `fonts/` + en `OFL.txt` (fontene Inter/JetBrains Mono/Source Serif 4/Newsreader er OFL-lisensierte og kan redistribueres — `shared/playground-design-system` i marketplace-en self-hoster allerede tre av dem, så praksisen er etablert).
|
||||
- **cwd-modellen bevares:** kommandoen kjører `node ${CLAUDE_PLUGIN_ROOT}/render/build-linkedin.mjs <input>` med `cwd` = serie-mappa. Output følger `cwd`, fonts følger `__dirname` (pluginens `render/fonts/`). (`${CLAUDE_PLUGIN_ROOT}` peker til plugin-install-mappa og brukes allerede i eksisterende kommandoer, f.eks. for `clipboard-helper.mjs`.)
|
||||
|
||||
### 7.2 Generaliser `build-linkedin.mjs`
|
||||
|
||||
Skriptet har i dag hardkodet Seres-spesifikk kalender, captions og ferskvare-flagg. Generaliser det til å lese en **edition-config** (f.eks. `linkedin/edition-config.json` i serie-mappa) for disse verdiene. Endringen gjøres i pluginen (skriptet bor nå der); maskinrommet leverer config-fila per serie.
|
||||
|
||||
### 7.3 maskinrommet blir konsument
|
||||
|
||||
- `maskinrommet/tools/`-kopiene fjernes; maskinrommet kaller pluginens render-skript i stedet.
|
||||
- **Dette er et eget arbeidsspor i maskinrommet-repoet** (`/Users/ktg/repos/maskinrommet/` — et ANNET repo, 0.1) og krever eksplisitt instruks fra operatøren når vi kommer dit. Planen beskriver det; selve utførelsen der gjøres separat. Pluginen fungerer uansett alene.
|
||||
|
||||
### 7.4 Graceful degradation (weasyprint)
|
||||
|
||||
- `build-html` + `build-linkedin` er zero-dep og virker alltid.
|
||||
- `build-pdf` + `build-carousel` krever `weasyprint` (eksternt Python-verktøy, kan ikke bundles) på PATH → detekter, og gi en tydelig installasjons-instruks hvis det mangler. Pipelinen stopper aldri på manglende weasyprint; den hopper over PDF-stegene med en advarsel.
|
||||
|
||||
### 7.5 Annoterings-renderer som førsteklasses kapabilitet (beslutning H)
|
||||
|
||||
`build-html.mjs` (0.6) ER annoteringsverktøyet (marker → Endre/Legg til/Fjern/Avklar/Risiko → kommentar → sidepanel → localStorage → eksport av annoterings-markdown). I dag er det kronikk-spesifikt: kun `##`/`###`-overskrifter, ingen tabell-støtte, ingen inline-kode, og frontmatter forutsetter kronikk-felter. Sesjon S0b beviste mangelen — for å annotere *denne planen* (full av tabeller) måtte rendereren utvides.
|
||||
|
||||
- **Generaliser** `render/build-html.mjs` til å rendre et hvilket som helst markdown-artefakt: tabeller, alle overskriftsnivåer (`#`–`####`), inline `` `kode` ``, og generisk frontmatter/tittel. Selve annoterings-motoren (CSS + klient-JS) er allerede artefakt-uavhengig og gjenbrukes ordrett.
|
||||
- **Bruksområde:** planer, briefer, ferdige poster, nyhetsbrev-editions, carousel-utkast — alt operatøren eller en Forgejo-nedlaster vil ha tilbakemelding på.
|
||||
- **Reference-implementasjon (S0b):** en engangs-generator som henter motoren ut av `build-html.mjs` ved kjøring og legger til tabell-/overskrift-/kode-parsing. Lå i `/private/tmp/claude-ltl-review/gen.mjs` i den sesjonen (kan være slettet — den er ikke kilden, kun et bevis på at det virker). **Plugin-versjonen skal embedde motoren** (CSS + klient-JS) direkte i `render/build-html.mjs` — ingen runtime-avhengighet til maskinrommet eller til en temp-fil.
|
||||
- **Integrasjon:** Step 7 i nyhetsbrev-pipelinen (§5.1) kaller denne. Genererte review-filer legges i `docs/review/` (gitignored — annoteringene lever uansett i nettleserens localStorage, ikke i fila).
|
||||
- **Zero-dep** — virker alltid, ingen weasyprint.
|
||||
|
||||
---
|
||||
|
||||
## 8. Kvalitetsregler for langform
|
||||
|
||||
Disse stammer fra Seres-erfaringen (0.2) og skal kodes inn i `references/longform-quality-rules.md` + i `/linkedin:newsletter` og agentene:
|
||||
|
||||
- **Leder-takeaway:** hver tekst skal lande ÉN klar takeaway + én konkret handling. Beskjær referanser hardt; hands-on-troverdighet slår sitat-dynge.
|
||||
- **Premiss→konklusjon-bue:** etabler ett klart premiss tidlig (ingress + første avsnitt); la avslutningen GRIPE premisset konkret og vri det framover (retning + ett håndfast grep), ikke bare oppsummere.
|
||||
- **AI-slop-fraser (forbudt):** «her må jeg være ærlig» / «for å være ærlig»; «ikke bare X, men Y»; unødig tre-listing; «i en stadig mer kompleks verden»; påklistrede oppsummeringssetninger.
|
||||
- **Generell, ikke etat-/person-spesifikk:** ingen personlige etat-anekdoter; presenter muligheter, ikke provokasjoner. Maks én strukturell forankrings-referanse per tekst (ikke gjentatt kritikk av en navngitt person).
|
||||
- **Formaterings-dose (minimal):** fet = maks ett poeng per bolk; korte lister (2–4) kun der teksten allerede ramser opp — aldri gjør bærende resonnement til kulepunkter; tabeller sparsomt. «Ingen artikkel skal ligne en PowerPoint-utskrift.»
|
||||
- **Gap lukkes med tightening/bytte, ikke utvidelse** — bytt svakere mot skarpere, hold lengden flat.
|
||||
- **Kalibrering per sweep er et brukervalg, ikke default:** før hver faktasjekk-/persona-sweep avklares fold-inn-aggressivitet (konservativ vs. aggressiv), sjargong-håndtering, og persona-vekting ved konflikt.
|
||||
|
||||
---
|
||||
|
||||
## 9. Fasing og sesjons-dekomponering
|
||||
|
||||
### 9.1 Faser
|
||||
|
||||
Kritisk sti er langform-kapabiliteten (operatøren har 6 editions i pipen og vil produsere neste serie raskere). Konsolidering som ikke blokkerer langform kommer etterpå.
|
||||
|
||||
| Fase | Innhold | Avhengighet | Risiko |
|
||||
|------|---------|-------------|--------|
|
||||
| **1 — Fundament** | Render-migrering (§7) + persona-bibliotek (§6.1) + `fact-checker` + `persona-reviewer` (§6.2/§6.3) + edition-state-skjema (§5.2). Finansier agentene ved å avvikle `content-tracker` + `personalization-scorer` (§4.2). | — | Middels |
|
||||
| **2 — Kapabiliteten** | `commands/newsletter.md` med alle faser (§5) + reconcile av newsletter-stien ut av `multiplatform.md` (§4.1). | Fase 1 | Middels |
|
||||
| **3 — Dogfood** | Produser en ekte edition gjennom pipelinen. Åpne review-HTML i nettleser og gå gjennom kjerne-flytene. Fang og fiks friksjon FØR release. | Fase 2 | Lav |
|
||||
| **4 — Renovering** | Resterende kommando-/agent-konsolidering (§4): templates→quick, publish→calendar, collab+speaking→outreach, authority→strategy, analytics/engagement-merge, router-gating. | Uavhengig | Middels |
|
||||
|
||||
Fase 4 kan kjøres parallelt med eller etter Fase 2–3. Hver konsolidering er en egen liten, testbar endring. v2.0.0 markerer fullført Fase 4.
|
||||
|
||||
### 9.2 Sesjons-dekomponering
|
||||
|
||||
Arbeidet spenner mange sesjoner. Hver sesjon = ÉN sammenhengende, testbar leveranse, holdt **innenfor 35% kontekst** (godt før compact). Én oppgave per sesjon — aldri start neste før HANDOVER (§9.3) er oppdatert. «S0» og «S0b» under er allerede gjort (planlegging); S1 er første bygge-sesjon.
|
||||
|
||||
| Sesjon | Fase | Leveranse | Verifisering (se §10) |
|
||||
|--------|------|-----------|--------------|
|
||||
| **S1** | 1 | Opprett `render/`; kopier de 4 skriptene + `fonts/` + OFL fra `/Users/ktg/repos/maskinrommet/tools/` INN i pluginen. (Ikke rør maskinrommet.) | Antakelse-test 1–3 |
|
||||
| **S1a** | 1 | Generaliser annoterings-rendereren `render/build-html.mjs` (beslutning H): tabeller, `#`–`####`, inline-kode, generisk artefakt. Embed motoren. | Rendrer denne planen + en post + en brief rent; tabeller intakt |
|
||||
| **S2** | 1 | Generaliser `render/build-linkedin.mjs` → leser `edition-config.json` (§7.2). | Antakelse-test 5 |
|
||||
| **S3** | 1 | `config/personas.template.md` + frø-personaene (§6.1). | Fil finnes, 3 personaer, primær merket |
|
||||
| **S4** | 1 | `agents/fact-checker.md` (§6.2) + test på prøvepåstander. | Returnerer verifiseringslogg + 🔴/🟡/🟢 |
|
||||
| **S5** | 1 | `agents/persona-reviewer.md` (§6.3, 2 moduser) + test. | Returnerer 6-akse-flagg / JA-NEI |
|
||||
| **S6** | 1 | Edition-state-skjema (§5.2) + avvikle `content-tracker` + `personalization-scorer` (§4.2). | `ls agents/` ned med 2; funksjon bevart |
|
||||
| **S7** | 2 | `commands/newsletter.md` skjelett — Step 0–2 (§5.1). | Antakelse-test 4 (research-fan-out kjører) |
|
||||
| **S8** | 2 | newsletter.md Step 3–4 (utkast + konsistens/kvalitet). | Utkast genereres voice-matchet |
|
||||
| **S9** | 2 | newsletter.md Step 5–6 (faktasjekk + persona-sweep). | Begge agenter kalles parallelt; sweep FØR lås |
|
||||
| **S10** | 2 | newsletter.md Step 7–10 (annotering, lås/leveranse, hook-gate, planlegging). | POST.html produseres; hook-gate etter lås |
|
||||
| **S11** | 2 | Reconcile newsletter-sti ut av `multiplatform.md` (§4.1) + skill-trigger (§5.3) + router-rad i `linkedin.md`. | Kun ÉN inngang til newsletter |
|
||||
| **S12** | 2 | `references/longform-quality-rules.md` (§8) + resumption-wiring (Step 0 leser edition-state). | Avbryt/gjenoppta-test |
|
||||
| **S13** | 3 | Dogfood: produser en ekte edition ende-til-ende; logg friksjon. | Edition i serie-mappa; review-HTML i nettleser |
|
||||
| **S14** | 3 | Fiks dogfood-friksjon. | Friksjonsliste lukket |
|
||||
| **S15** | 4 | `templates.md` → modus i `quick.md` (§4.1). | quick dekker begge; templates fjernet |
|
||||
| **S16** | 4 | `publish.md` → handling i `calendar.md` (§4.1). | calendar dekker publish |
|
||||
| **S17** | 4 | `collab.md` + `speaking.md` → ny `outreach.md` (§4.1). | outreach dekker begge |
|
||||
| **S18** | 4 | `authority.md` → `strategy.md` + trajectory-dedup + `profile.md` kanon (§4.1). | strategy dekker authority |
|
||||
| **S19** | 4 | Agent-merge: analytics (2→1) + engagement (2→1) (§4.2). | `ls agents/` ned med 2 |
|
||||
| **S20** | 4 | `import.md`-trim + router-gating (§4.1) + sluttdoc-pass → **v2.0.0**. | `ls commands/` verifisert ned; alle 3 doc-nivåer oppdatert |
|
||||
|
||||
Tabellen er veiledende. Hvis en sesjon nærmer seg 35% kontekst før leveransen er ferdig: splitt den, oppdater HANDOVER med delvis status, og la neste sesjon fullføre.
|
||||
|
||||
### 9.3 Bygge-protokoll (multi-sesjon)
|
||||
|
||||
**Single source of truth:** `docs/BUILD-HANDOVER.local.md` i pluginen (gitignored). Holder: hvor vi er nå, hva forrige sesjon gjorde/verifiserte, NESTE SESJON-oppgaven, ufravikelige regler, og en sesjons-logg. (Forveksles ikke med edition-HANDOVER i serie-mappa, §5.2.)
|
||||
|
||||
**Kontekst-budsjett:** Hver sesjon holdes innenfor 35% kontekst (før compact). Nærmer du deg: avslutt rent, ikke start nytt steg.
|
||||
|
||||
**Siste handling i HVER sesjon (ufravikelig):** Oppdater `BUILD-HANDOVER.local.md` — status, hva ble gjort og verifisert, og en presis NESTE SESJON-oppgave. Skriv aldri «gå til X» med mindre X eksisterer og er testet.
|
||||
|
||||
**Modell:** Opus 4.7 på alt. Ikke degrader til en mindre modell — vent heller på tilgjengelighet.
|
||||
|
||||
**Fast resume-kommando** (operatøren limer inn etter hver `/clear`, identisk hver gang):
|
||||
|
||||
```
|
||||
Fortsett byggingen av LTL fullspektrum-innholdsmotor. Les
|
||||
docs/BUILD-HANDOVER.local.md (single source of truth) og
|
||||
docs/plan-fullspektrum-innholdsmotor.md. Utfør oppgaven merket
|
||||
«NESTE SESJON» i HANDOVER, og BARE den. Hold deg innenfor 35%
|
||||
kontekst. Siste handling: oppdater BUILD-HANDOVER.local.md.
|
||||
Ikke push uten at jeg ber om det.
|
||||
```
|
||||
|
||||
**Disiplin:** Én oppgave per sesjon. Test før HANDOVER oppdateres. Ikke utvid scope utover NESTE SESJON-oppgaven. Cross-repo-arbeid (maskinrommet, §7.3) krever eksplisitt instruks før utførelse.
|
||||
|
||||
---
|
||||
|
||||
## 10. Verifisering — hvordan en sesjon vet at den er ferdig
|
||||
|
||||
Dette er kjernen i at planen holder uten drift: hver sesjon har en **Definition of Done (DoD)** som er binær og kjørbar, og som sesjonen MÅ evaluere på seg selv før den oppdaterer HANDOVER.
|
||||
|
||||
### 10.0 Prinsipp: hva «ferdig» betyr
|
||||
|
||||
«Ferdig» = hvert DoD-punkt for sesjonen er objektivt bekreftet. Tre typer punkter, etter hvordan de bekreftes:
|
||||
|
||||
- **Deterministisk** — bekreftes med en kommando (filen finnes, `wc -l` stemmer, `node --test` grønt, `grep` gir forventet treff). Claude kjører kommandoen og leser utfallet. Ingen skjønn.
|
||||
- **Kjent-svar-fixture** — for agenter/skript der output er strukturert: lag FØRST en liten fixture med fasit (jf. husregelen «ingen produksjonskode uten en feilende test først»), kjør, og sammenlign output mot fasit. Claude evaluerer mot fasiten, ikke mot egen magefølelse.
|
||||
- **Subjektiv kvalitet — SELV-SERTIFISERES ALDRI.** Om en tekst «lander», treffer voice, er original eller prosakvalitet er skjønn. Claude skal ALDRI sette grønn hake på dette selv. Det rutes til én av to:
|
||||
- en **gate-mekanisme** som produserer et eksplisitt verdikt (f.eks. `persona-reviewer` returnerer rent JA fra primær; `voice-trainer` bekrefter voice-match; `fact-checker` returnerer 0 åpne 🔴), eller
|
||||
- **operatøren**, via den annoterbare review-HTML-en (§7.5).
|
||||
- Et DoD-punkt av denne typen merkes `[GATE: <hvem>]` eller `[OPERATØR]` og regnes som uoppfylt til gaten/operatøren har gitt verdikt. En sesjon er ikke ferdig fordi Claude «mener» kvaliteten er god.
|
||||
|
||||
Hvis et DoD-punkt ikke kan bekreftes: sesjonen er IKKE ferdig. Skriv den faktiske statusen i HANDOVER (hva som gjenstår), ikke en grønn hake.
|
||||
|
||||
### 10.1 Nøkkelantakelser (test umiddelbart i sesjonen som rører dem)
|
||||
|
||||
1. **Render kjørbar fra pluginen med riktig cwd.** `cd <serie-mappe> && node <plugin>/render/build-linkedin.mjs utkast/01-*.md` → `linkedin/01/POST.html` + `linkedin/samle/POST.html` finnes i serie-mappa. *(Deterministisk.)*
|
||||
2. **Bundlede fonts resolver via `__dirname`.** `build-pdf` produserer PDF med Newsreader/Inter fra plugin-lokasjonen, ikke fallback. *(Deterministisk: inspiser PDF-metadata / visuell sjekk.)*
|
||||
3. **`${CLAUDE_PLUGIN_ROOT}` eksponert i kommando-Bash.** Et Step som ekko-er stien resolver til plugin-install-mappa. *(Deterministisk.)*
|
||||
4. **Task-fan-out i forgrunn beholder Task-verktøyet.** Kommandoen spawner 2+ parallelle research-`Task`-kall og får strukturerte svar (ikke degradert). *(Deterministisk: tell parallelle kall + svar.)*
|
||||
5. **Generalisert `build-linkedin` leser edition-config.** Endre `edition-config.json` → POST.html-output endres uten kode-endring. *(Deterministisk: diff to kjøringer.)*
|
||||
|
||||
### 10.2 DoD-arketyper
|
||||
|
||||
Hver sesjon arver én arketype + sine egne spesifikke verdier (§10.3).
|
||||
|
||||
- **A — Render-flytting (S1):** (a) `ls render/` viser 4 skript + `fonts/` + `OFL.txt`; (b) nøkkelantakelse 1–3 grønne; (c) zero-dep-skriptene kjører uten `npm install`.
|
||||
- **B — Skript-generalisering (S1a, S2):** (a) ny `node --test`-fil skrevet FØRST, feiler før endring; (b) etter endring: testen grønn; (c) **regresjon**: en kjent input gir uendret output for det gamle bruksmønsteret (diff = tom).
|
||||
- **C — Template/config-fil (S3, S6-skjema):** (a) filen finnes; (b) påkrevde felter til stede (`grep`); (c) parser uten feil (`node -e` som leser den).
|
||||
- **D — Ny agent (S4, S5):** (a) fil finnes med gyldig frontmatter (`model: opus`, `tools`, `description` — sjekk med `grep`/parse); (b) **kjent-svar-fixture skrevet FØRST** (f.eks. 3 påstander: én sann/én falsk/én uverifiserbar → forventet 🟢/🔴/🟡); (c) agent-kjøring mot fixturen matcher fasit-formen og -dommen; (d) `[GATE]` agenten skriver ALDRI om tekst selv (verifiser at output er flagg/retning, ikke ny copy).
|
||||
- **E — Kommando-steg (S7–S10, S12):** (a) steget kjører på en dummy-serie-fixture og produserer det spesifiserte artefaktet (fil finnes / forventet form); (b) resumption der relevant: avbryt → re-kjør → fortsetter fra riktig steg via edition-state (deterministisk); (c) subjektive steg (utkast, persona-sweep) er `[GATE]`/`[OPERATØR]`, ikke selv-sertifisert.
|
||||
- **F — Konsolidering (S11, S15–S20):** (a) **kapabilitets-sjekkliste**: hver enkeltfunksjon i forgjenger-fila er enumerert og verifisert til stede i målet (punkt for punkt); (b) forgjenger-fil fjernet; (c) `ls commands/ | wc -l` / `ls agents/ | wc -l` viser forventet, lavere tall; (d) **ingen daudlenker**: `grep -rn "<gammelt-navn>" commands/ agents/ skills/ hooks/ README.md` gir kun tilsiktede treff; (e) router (`linkedin.md`) oppdatert.
|
||||
- **G — Dogfood (S13–S14):** (a) en ekte edition produsert ende-til-ende (filer i serie-mappa); (b) rekkefølge-bevis: edition-HANDOVER viser persona-sweep FØR lås; (c) friksjon logget i HANDOVER; (d) for S14: hvert lukket friksjonspunkt re-testet med en konkret sjekk, ikke bare «fikset».
|
||||
|
||||
### 10.3 DoD per sesjon (konkret)
|
||||
|
||||
| Sesjon | Arketype | Spesifikt «suksess ser slik ut» |
|
||||
|--------|----------|-------------------------------|
|
||||
| S1 | A | 4 skript + fonts + OFL i `render/`; antakelse 1–3 grønne |
|
||||
| S1a | B | `node --test` for tabell/`#`–`####`/inline-kode grønn; rendrer denne planen med 9 tabeller intakt; gammel kronikk rendrer uendret (regresjon) |
|
||||
| S2 | B | Test for edition-config-lesing grønn; antakelse 5 (diff to configs); gammel Seres-input gir uendret POST.html om config matcher dagens hardkoding (regresjon) |
|
||||
| S3 | C | `personas.template.md` finnes; 3 personaer med alle felt (rolle/avkobler/overbeviser/ekspertise/sjargong); primær merket; parser OK |
|
||||
| S4 | D | `fact-checker.md` + fixture med 3 påstander → output 🟢/🔴/🟡 matcher fasit; flagger uverifiserbar som 🟡 (ikke gjetter) |
|
||||
| S5 | D | `persona-reviewer.md` + fixture: returnerer ≤5 flagg på 6 akser med retning; INGEN omskrevet copy; begge moduser gir riktig form |
|
||||
| S6 | C+F | edition-state-skjema parser; `content-tracker`+`personalization-scorer` fjernet; `ls agents/`=14→ (forventet); funksjon dekket av script (kjør scriptet) |
|
||||
| S7 | E | newsletter.md Step 0–2 kjører på dummy-serie; antakelse 4 (parallell fan-out) |
|
||||
| S8 | E | Step 3–4 produserer utkast-fil; `[OPERATØR]`/`[GATE: voice-trainer]` for voice-match — ikke selv-sertifisert |
|
||||
| S9 | E | Step 5–6: `fact-checker` + `persona-reviewer` kalles parallelt; sweep skjer FØR lås (rekkefølge-assert); `[GATE]` rent JA fra primær kreves for å gå videre |
|
||||
| S10 | E | Step 7–10: POST.html produsert av render; hook-gate kjører ETTER lås (rekkefølge-assert); edition registrert i kø |
|
||||
| S11 | F | Newsletter-sti fjernet fra `multiplatform.md` (kun én inngang — `grep`); skill-trigger lagt til; router-rad lagt til |
|
||||
| S12 | C+E | `longform-quality-rules.md` finnes m/ alle regler i §8; resumption: avbryt etter Step 6 → re-kjør → fortsetter fra Step 7 |
|
||||
| S13 | G | Ekte edition i serie-mappa; edition-HANDOVER viser sweep-før-lås; review-HTML åpnet; friksjon logget |
|
||||
| S14 | G | Hvert friksjonspunkt fra S13 re-testet med konkret sjekk; restliste tom eller eksplisitt deferert |
|
||||
| S15 | F | `quick.md` dekker alle 8 templates-typer (sjekkliste); `templates.md` fjernet; `ls commands/` ned 1; ingen daudlenker |
|
||||
| S16 | F | `calendar.md` dekker publish-handlingen (sjekkliste); `publish.md` fjernet; ned 1; ingen daudlenker |
|
||||
| S17 | F | `outreach.md` dekker collab+speaking (sjekkliste m/ hver funksjon); begge forgjengere fjernet; ned 1 netto; ingen daudlenker |
|
||||
| S18 | F | `strategy.md` dekker authority + trajectory; `audit`/`analyze` peker til `profile` som kanon; `authority.md` fjernet; ingen daudlenker |
|
||||
| S19 | F | analytics (2→1) + engagement (2→1); `ls agents/` ned 2; hver modus i den slåtte agenten dekker forgjengernes funksjoner (sjekkliste) |
|
||||
| S20 | F | `import.md` analyse delegert til `report`; router-gating på plass; **v2.0.0** i alle versjons-referanser (`grep`); alle 3 doc-nivåer oppdatert |
|
||||
|
||||
### 10.4 Selv-evaluerings-protokoll (kjøres ved hver sesjonsslutt)
|
||||
|
||||
1. Hent sesjonens DoD fra §10.3 (+ arketypen i §10.2).
|
||||
2. Gå gjennom hvert punkt og kjør sjekken. Skriv utfall: ✅ (bekreftet med kommando/fixture), 🔶 (`[GATE]`/`[OPERATØR]` — venter på verdikt), eller ❌ (feilet/ikke gjort).
|
||||
3. **Ærlighetsregel:** Aldri ✅ på et punkt du ikke faktisk har kjørt. Aldri ✅ på subjektiv kvalitet — den er 🔶 til gate/operatør har dømt.
|
||||
4. Skriv resultatet i HANDOVER-ens sesjons-logg, og sett NESTE SESJON. Er noe ❌/🔶 som blokkerer: NESTE SESJON er å lukke det, ikke å gå videre.
|
||||
5. Bare når alle DoD-punkter er ✅ (eller bevisst 🔶-deferert med operatørens viten) regnes sesjonen som ferdig.
|
||||
|
||||
### 10.5 Doc-plikt (obligatorisk ved push)
|
||||
|
||||
Enhver feature-endring oppdaterer i SAMME commit: plugin-`README.md`, plugin-`CLAUDE.md`, og rot-`README.md` (`/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/README.md`).
|
||||
|
||||
---
|
||||
|
||||
## 11. Risikoer og åpne spørsmål
|
||||
|
||||
| # | Risiko / spørsmål | Håndtering |
|
||||
|---|-------------------|------------|
|
||||
| R1 | **Cross-repo render-migrering** berører maskinrommet (§7.3, annet repo). | Krev eksplisitt instruks før endring der. Pluginen virker alene uansett. |
|
||||
| R2 | **weasyprint kan ikke bundles.** | Graceful degradation (§7.4). Zero-dep-skriptene dekker kjerne-leveransen (POST.html). |
|
||||
| R3 | **Font-vekt** øker plugin-størrelsen. | Verifiser total størrelse i S1; OFL-lisens vedlegges. Akseptabelt for kapabiliteten. |
|
||||
| R4 | **Konsolidering kan bryte eksisterende arbeidsflyt** (sammenslåtte kommandoer). | Hver merge er egen testbar endring med kapabilitets-sjekkliste; router oppdateres. |
|
||||
| R5 | **Opus-kostnad** ved mange parallelle fact-checker/persona-kall. | Forventet og akseptert (operatørens preferanse: Opus på alt; tillit-kritisk arbeid). Eskalér til operatør hvis volumet blir et problem. |
|
||||
| Q1 | `edition-config.json` som JSON eller frontmatter-md? | Avklares i S2 (JSON anbefales for deterministisk parsing). |
|
||||
| Q2 | Skal `video-scripter` absorberes i `content-repurposer`? | Marginal; avgjøres i S19 etter nærlesning. |
|
||||
| Q3 | Konsolidering av de eksisterende kommandoene utover §4 (større refaktor)? | Utenfor denne planens scope. Noteres som mulig fremtidig spor. |
|
||||
|
||||
---
|
||||
|
||||
## 12. Filmanifest
|
||||
|
||||
Alle stier relativt til pluginen `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/linkedin-studio/` med mindre annet er angitt.
|
||||
|
||||
**Nye filer:**
|
||||
- `commands/newsletter.md` — orkestrator-kommandoen (§5)
|
||||
- `commands/outreach.md` — fra sammenslåing av collab + speaking (§4.1)
|
||||
- `agents/fact-checker.md` — ny agent, Opus (§6.2)
|
||||
- `agents/persona-reviewer.md` — ny agent, Opus, 2 moduser (§6.3)
|
||||
- `config/personas.template.md` — persona-bibliotek (§6.1)
|
||||
- `render/build-linkedin.mjs`, `render/build-carousel.mjs`, `render/build-html.mjs`, `render/build-pdf.mjs` — flyttet fra maskinrommet (§7); `build-linkedin` generalisert (§7.2); `build-html` generalisert til artefakt-annoterings-renderer (§7.5)
|
||||
- `render/fonts/` + `render/OFL.txt`
|
||||
- `references/longform-quality-rules.md` — kodede kvalitetsregler (§8)
|
||||
|
||||
**Endrede filer:**
|
||||
- `commands/linkedin.md` (router) — ny rad for newsletter + nivå-gating av aspirasjonelle kommandoer (§4.1)
|
||||
- `commands/quick.md`, `calendar.md`, `strategy.md`, `import.md`, `multiplatform.md`, `profile.md`, `audit.md`, `analyze.md` — konsolidering (§4.1)
|
||||
- `agents/analytics-interpreter.md` (+ avvikle `performance-reporter`), `agents/engagement-coach.md` (+ avvikle `comment-strategist`) — sammenslåing (§4.2)
|
||||
- `skills/linkedin-content-creation/SKILL.md` — langform-trigger (§5.3)
|
||||
- `hooks/hooks.template.json` + kjør `python3 hooks/scripts/compile-hooks.py` — ev. SessionStart-resumption for editions
|
||||
- `.gitignore` — `docs/review/` lagt til (genererte annoterings-artefakter) ✓ gjort i S0b
|
||||
- `README.md`, `CLAUDE.md` (plugin) + rot-`README.md` — doc-plikt ved hver push
|
||||
|
||||
**Fjernede filer (konsolidering, §4):**
|
||||
- `agents/content-tracker.md`, `agents/personalization-scorer.md` (→ deterministisk script)
|
||||
- `agents/performance-reporter.md`, `agents/comment-strategist.md` (slått inn i analytics/engagement)
|
||||
- `commands/templates.md`, `commands/publish.md`, `commands/authority.md` (slått inn i quick/calendar/strategy)
|
||||
- `commands/collab.md`, `commands/speaking.md` (→ `commands/outreach.md`)
|
||||
|
||||
**Cross-repo (eget spor i `/Users/ktg/repos/maskinrommet/`, krever eksplisitt instruks — §7.3):**
|
||||
- `maskinrommet/tools/` — kopiene fjernes; maskinrommet kaller pluginens render
|
||||
- `maskinrommet/serier/<slug>/linkedin/edition-config.json` — ny per-serie config
|
||||
82
plugins/linkedin-studio/docs/voyage-build-brief.md
Normal file
82
plugins/linkedin-studio/docs/voyage-build-brief.md
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# Bygge-brief (Voyage-input) — LTL fullspektrum-innholdsmotor → v2.0.0
|
||||
|
||||
> **Formål:** Input til `/trekplan` (Voyage-pluginen). Dette er byggingen av LTL-pluginen, drevet som et Voyage-prosjekt: `/trekplan` produserer en kjørbar plan med per-steg Manifests, `/trekexecute --fg` + `/trekcontinue` driver sesjonene, `/trekreview` er release-gate.
|
||||
> **Detaljert referanse:** [`plan-fullspektrum-innholdsmotor.md`](./plan-fullspektrum-innholdsmotor.md) (samme mappe) — den hardnede planen med §0 orientering, §4 renovering, §5 langform, §6 byggeklosser, §7 render, §10 DoD. **Les den; den er fasit for hva som skal bygges.**
|
||||
> **Opprinnelig retningsbrief:** [`brief-fullspektrum-innholdsmotor.md`](./brief-fullspektrum-innholdsmotor.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Oppgave
|
||||
|
||||
Løft `linkedin-studio`-pluginen («LTL», v1.2.0) til **v2.0.0**: en fullspektrum-motor for ALT LinkedIn-innhold — fra kortform-post til nyhetsbrev-edition — samtidig som den totale kommando-/agent-overflaten **reduseres** gjennom konsolidering.
|
||||
|
||||
Tre arbeidskropper:
|
||||
1. **Renovering** — konsolider reell redundans (27→~23 kommandoer, 16→~14 agenter). Plan §4.
|
||||
2. **Langform-kapabilitet** — én ny kommando `/linkedin:newsletter` med faset, multi-sesjons pipeline (research → utkast → faktasjekk → persona-review FØR lås → leveranse → hook-gate). Plan §5–§6.
|
||||
3. **Render + annotering inn i pluginen** — flytt 4 render-skript + fonts; generaliser `build-linkedin`; generaliser `build-html` til artefakt-annoterings-renderer. Plan §7.
|
||||
|
||||
## 2. Orientering (kritisk — repoer og stier)
|
||||
|
||||
- **LTL-pluginen (bygg her):** `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/linkedin-studio/`. Plugin-i-monorepo: steg-stier er relative til denne mappa (sett Execution Strategy `cwd:` deretter).
|
||||
- **Marketplace-rot:** `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/` (Forgejo, aldri GitHub).
|
||||
- **maskinrommet (annet repo):** `/Users/ktg/repos/maskinrommet/` — render-skriptene kopieres HERFRA inn i pluginen. Skriving TIL maskinrommet krever eksplisitt instruks (cross-repo, eget spor).
|
||||
- **Voyage:** `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/voyage/` — harness for denne byggingen.
|
||||
- **svv-memory (prosess-erfaring):** `/Users/ktg/.claude/projects/-Users-ktg-repos-svv/memory/`.
|
||||
- **Seres-serien** (kilden til langform-prosessen): `/Users/ktg/repos/maskinrommet/serier/silvija-seres-motsvar/`.
|
||||
|
||||
Full ordliste (Seres, persona-sweep, hook-gate, faktasjekk «skyldig til motbevist», POST.html, Altinn-feilen, «største prosessfeil») står i **plan §0** — les den før planlegging.
|
||||
|
||||
## 3. Låste beslutninger (constraints — IKKE åpne for re-litigering)
|
||||
|
||||
- **A:** Én ny orkestrator-kommando `/linkedin:newsletter`, ikke en suite, ikke utvidelse av `pipeline`.
|
||||
- **B:** Bare langform nå — eksisterende kortform-kommandoer røres ikke.
|
||||
- **C:** Ship alle 4 render-skript + fonts i pluginen (`render/`); generaliser `build-linkedin`; maskinrommet blir konsument.
|
||||
- **D:** Hybrid persona-bibliotek i `config/`, override per prosjekt; primær merkes.
|
||||
- **E:** Faktasjekk er et eget steg.
|
||||
- **F:** Konsolider redundans i samme runde (netto færre kommandoer/agenter).
|
||||
- **G:** Edition-produksjons-state bor i serie-mappa (maskinrommet), ikke i plugin-state.
|
||||
- **H:** `build-html` generaliseres til artefakt-annoterings-renderer (tabeller, alle overskrifter, inline-kode).
|
||||
|
||||
## 4. Forskning er allerede gjort (ikke gjenta fra null)
|
||||
|
||||
`/trekplan`-utforskningen kan være **bekreftende, ikke fra-scratch**. Følgende er ferdig kartlagt og frosset i planen:
|
||||
- **Inventar-audit** av alle 27 kommandoer + 16 agenter (overlapp/redundans/langform-relevans) → plan §4, §6.
|
||||
- **Render-kontraktene** (4 skript, cwd-modell, weasyprint-avhengighet, build-linkedin hardkoding) → plan §0.6, §7.
|
||||
- **Prosess-erfaringen** (16 faser, persona-sweep-før-lås, faktasjekk-sweep) → plan §0.4, svv-memory.
|
||||
- **Arkitektur-mønstre** i LTL (kommando-mal, agent-frontmatter, hook-kompilering, state-updater) → plan §3.
|
||||
|
||||
`/trekplan` bør verifisere disse mot faktiske filer der det er billig, ikke re-derivere.
|
||||
|
||||
## 5. Scope og fasing
|
||||
|
||||
Fire faser (plan §9.1). Kritisk sti = langform (fase 1–3); renovering (fase 4) kan følge etter.
|
||||
- **Fase 1 — Fundament:** render-migrering + annoterings-generalisering + persona-bibliotek + `fact-checker` + `persona-reviewer` + edition-state-skjema; finansier de 2 nye agentene ved å avvikle `content-tracker` + `personalization-scorer`.
|
||||
- **Fase 2 — Kapabilitet:** `commands/newsletter.md` (10 steg) + reconcile newsletter-sti ut av `multiplatform`.
|
||||
- **Fase 3 — Dogfood:** produser en ekte edition ende-til-ende; fiks friksjon.
|
||||
- **Fase 4 — Renovering:** templates→quick, publish→calendar, collab+speaking→outreach, authority→strategy, analytics/engagement-merge, router-gating → v2.0.0.
|
||||
|
||||
Sesjons-dekomponeringen S1–S20 i plan §9.2 er forslag til Execution Strategy. Hver sesjon ≤ 35% kontekst.
|
||||
|
||||
**Utenfor scope:** variabel-intensitet-deling av agenter inn i kortform (utsatt, beslutning B); konsolidering utover §4; cross-repo-endringer i maskinrommet (eget spor, krever instruks).
|
||||
|
||||
## 6. Suksesskriterier (per-steg DoD → Manifests)
|
||||
|
||||
Plan §10 definerer DoD per sesjon. Oversett disse til Voyage per-steg **Manifests** (`expected_paths`, `min_file_count`, `must_contain`, `commit_message_pattern`, `forbidden_paths`):
|
||||
- **Deterministisk** (Voyage `Verify:` + Manifest + Phase 7.5-audit): filer finnes, `ls | wc -l` stemmer, `node --test` grønt, `grep` gir forventet, render kjører fra serie-mappa, edition-config-bytte endrer output.
|
||||
- **Kjent-svar-fixture** (skriv FØR implementasjon, jf. husregel «ingen produksjonskode uten feilende test først»): `fact-checker` mot 3 påstander (sann/falsk/uverifiserbar → 🟢/🔴/🟡); `persona-reviewer` mot test-tekst (≤5 flagg, 6 akser, INGEN omskrevet copy).
|
||||
- **Subjektiv kvalitet — ALDRI selv-sertifisert.** Voice-match, om teksten «lander», prosakvalitet rutes til (a) gate-agent med eksplisitt verdikt (`persona-reviewer` rent JA fra primær; `voice-trainer`-bekreftelse) eller (b) operatør via annoterbar review-HTML. Et slikt steg er ikke «ferdig» på Claudes eget skjønn. (Voyage Phase 7.5-audit er sikkerhetsnettet mot hallusinert «ferdig».)
|
||||
|
||||
## 7. Ufravikelige regler
|
||||
|
||||
- **Modell:** Opus 4.7 på alt (Voyage `profile: premium`). Ikke degrader.
|
||||
- **Kjøremodus:** `/trekexecute --fg` (sekvensielt, abonnement). IKKE parallell `claude -p` (API-billing). `/trekcontinue` per fersk sesjon.
|
||||
- **Doc-plikt:** hver feature-endring som pushes oppdaterer plugin-README + plugin-CLAUDE + rot-README i samme commit.
|
||||
- **Cross-repo:** ingen skriving til `/Users/ktg/repos/maskinrommet/` uten eksplisitt instruks.
|
||||
- **Ingen push** uten eksplisitt instruks.
|
||||
- **Hooks (bash 3.2 / Node):** alle nye hooks i Node `.mjs`; rediger `hooks/hooks.template.json` + `hooks/prompts/*.md` → kjør `python3 hooks/scripts/compile-hooks.py`.
|
||||
- **Redaksjonell kvalitet på .md-prompter** (kommandoer/agenter) verifiseres av operatør via annoterings-HTML, ikke mekanisk.
|
||||
|
||||
## 8. Åpne spørsmål (avklares i plan, ikke blokkerende)
|
||||
|
||||
- `edition-config.json` JSON vs. frontmatter (anbefalt JSON).
|
||||
- `video-scripter` absorberes i `content-repurposer`? (besluttes sent).
|
||||
82
plugins/linkedin-studio/docs/voyage-build/brief.md
Normal file
82
plugins/linkedin-studio/docs/voyage-build/brief.md
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# Bygge-brief (Voyage-input) — LTL fullspektrum-innholdsmotor → v2.0.0
|
||||
|
||||
> **Formål:** Input til `/trekplan` (Voyage-pluginen). Dette er byggingen av LTL-pluginen, drevet som et Voyage-prosjekt: `/trekplan` produserer en kjørbar plan med per-steg Manifests, `/trekexecute --fg` + `/trekcontinue` driver sesjonene, `/trekreview` er release-gate.
|
||||
> **Detaljert referanse:** [`plan-fullspektrum-innholdsmotor.md`](./plan-fullspektrum-innholdsmotor.md) (samme mappe) — den hardnede planen med §0 orientering, §4 renovering, §5 langform, §6 byggeklosser, §7 render, §10 DoD. **Les den; den er fasit for hva som skal bygges.**
|
||||
> **Opprinnelig retningsbrief:** [`brief-fullspektrum-innholdsmotor.md`](./brief-fullspektrum-innholdsmotor.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Oppgave
|
||||
|
||||
Løft `linkedin-thought-leadership`-pluginen («LTL», v1.2.0) til **v2.0.0**: en fullspektrum-motor for ALT LinkedIn-innhold — fra kortform-post til nyhetsbrev-edition — samtidig som den totale kommando-/agent-overflaten **reduseres** gjennom konsolidering.
|
||||
|
||||
Tre arbeidskropper:
|
||||
1. **Renovering** — konsolider reell redundans (27→~23 kommandoer, 16→~14 agenter). Plan §4.
|
||||
2. **Langform-kapabilitet** — én ny kommando `/linkedin:newsletter` med faset, multi-sesjons pipeline (research → utkast → faktasjekk → persona-review FØR lås → leveranse → hook-gate). Plan §5–§6.
|
||||
3. **Render + annotering inn i pluginen** — flytt 4 render-skript + fonts; generaliser `build-linkedin`; generaliser `build-html` til artefakt-annoterings-renderer. Plan §7.
|
||||
|
||||
## 2. Orientering (kritisk — repoer og stier)
|
||||
|
||||
- **LTL-pluginen (bygg her):** `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/linkedin-thought-leadership/`. Plugin-i-monorepo: steg-stier er relative til denne mappa (sett Execution Strategy `cwd:` deretter).
|
||||
- **Marketplace-rot:** `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/` (Forgejo, aldri GitHub).
|
||||
- **maskinrommet (annet repo):** `/Users/ktg/repos/maskinrommet/` — render-skriptene kopieres HERFRA inn i pluginen. Skriving TIL maskinrommet krever eksplisitt instruks (cross-repo, eget spor).
|
||||
- **Voyage:** `/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/plugins/voyage/` — harness for denne byggingen.
|
||||
- **svv-memory (prosess-erfaring):** `/Users/ktg/.claude/projects/-Users-ktg-repos-svv/memory/`.
|
||||
- **Seres-serien** (kilden til langform-prosessen): `/Users/ktg/repos/maskinrommet/serier/silvija-seres-motsvar/`.
|
||||
|
||||
Full ordliste (Seres, persona-sweep, hook-gate, faktasjekk «skyldig til motbevist», POST.html, Altinn-feilen, «største prosessfeil») står i **plan §0** — les den før planlegging.
|
||||
|
||||
## 3. Låste beslutninger (constraints — IKKE åpne for re-litigering)
|
||||
|
||||
- **A:** Én ny orkestrator-kommando `/linkedin:newsletter`, ikke en suite, ikke utvidelse av `pipeline`.
|
||||
- **B:** Bare langform nå — eksisterende kortform-kommandoer røres ikke.
|
||||
- **C:** Ship alle 4 render-skript + fonts i pluginen (`render/`); generaliser `build-linkedin`; maskinrommet blir konsument.
|
||||
- **D:** Hybrid persona-bibliotek i `config/`, override per prosjekt; primær merkes.
|
||||
- **E:** Faktasjekk er et eget steg.
|
||||
- **F:** Konsolider redundans i samme runde (netto færre kommandoer/agenter).
|
||||
- **G:** Edition-produksjons-state bor i serie-mappa (maskinrommet), ikke i plugin-state.
|
||||
- **H:** `build-html` generaliseres til artefakt-annoterings-renderer (tabeller, alle overskrifter, inline-kode).
|
||||
|
||||
## 4. Forskning er allerede gjort (ikke gjenta fra null)
|
||||
|
||||
`/trekplan`-utforskningen kan være **bekreftende, ikke fra-scratch**. Følgende er ferdig kartlagt og frosset i planen:
|
||||
- **Inventar-audit** av alle 27 kommandoer + 16 agenter (overlapp/redundans/langform-relevans) → plan §4, §6.
|
||||
- **Render-kontraktene** (4 skript, cwd-modell, weasyprint-avhengighet, build-linkedin hardkoding) → plan §0.6, §7.
|
||||
- **Prosess-erfaringen** (16 faser, persona-sweep-før-lås, faktasjekk-sweep) → plan §0.4, svv-memory.
|
||||
- **Arkitektur-mønstre** i LTL (kommando-mal, agent-frontmatter, hook-kompilering, state-updater) → plan §3.
|
||||
|
||||
`/trekplan` bør verifisere disse mot faktiske filer der det er billig, ikke re-derivere.
|
||||
|
||||
## 5. Scope og fasing
|
||||
|
||||
Fire faser (plan §9.1). Kritisk sti = langform (fase 1–3); renovering (fase 4) kan følge etter.
|
||||
- **Fase 1 — Fundament:** render-migrering + annoterings-generalisering + persona-bibliotek + `fact-checker` + `persona-reviewer` + edition-state-skjema; finansier de 2 nye agentene ved å avvikle `content-tracker` + `personalization-scorer`.
|
||||
- **Fase 2 — Kapabilitet:** `commands/newsletter.md` (10 steg) + reconcile newsletter-sti ut av `multiplatform`.
|
||||
- **Fase 3 — Dogfood:** produser en ekte edition ende-til-ende; fiks friksjon.
|
||||
- **Fase 4 — Renovering:** templates→quick, publish→calendar, collab+speaking→outreach, authority→strategy, analytics/engagement-merge, router-gating → v2.0.0.
|
||||
|
||||
Sesjons-dekomponeringen S1–S20 i plan §9.2 er forslag til Execution Strategy. Hver sesjon ≤ 35% kontekst.
|
||||
|
||||
**Utenfor scope:** variabel-intensitet-deling av agenter inn i kortform (utsatt, beslutning B); konsolidering utover §4; cross-repo-endringer i maskinrommet (eget spor, krever instruks).
|
||||
|
||||
## 6. Suksesskriterier (per-steg DoD → Manifests)
|
||||
|
||||
Plan §10 definerer DoD per sesjon. Oversett disse til Voyage per-steg **Manifests** (`expected_paths`, `min_file_count`, `must_contain`, `commit_message_pattern`, `forbidden_paths`):
|
||||
- **Deterministisk** (Voyage `Verify:` + Manifest + Phase 7.5-audit): filer finnes, `ls | wc -l` stemmer, `node --test` grønt, `grep` gir forventet, render kjører fra serie-mappa, edition-config-bytte endrer output.
|
||||
- **Kjent-svar-fixture** (skriv FØR implementasjon, jf. husregel «ingen produksjonskode uten feilende test først»): `fact-checker` mot 3 påstander (sann/falsk/uverifiserbar → 🟢/🔴/🟡); `persona-reviewer` mot test-tekst (≤5 flagg, 6 akser, INGEN omskrevet copy).
|
||||
- **Subjektiv kvalitet — ALDRI selv-sertifisert.** Voice-match, om teksten «lander», prosakvalitet rutes til (a) gate-agent med eksplisitt verdikt (`persona-reviewer` rent JA fra primær; `voice-trainer`-bekreftelse) eller (b) operatør via annoterbar review-HTML. Et slikt steg er ikke «ferdig» på Claudes eget skjønn. (Voyage Phase 7.5-audit er sikkerhetsnettet mot hallusinert «ferdig».)
|
||||
|
||||
## 7. Ufravikelige regler
|
||||
|
||||
- **Modell:** Opus 4.7 på alt (Voyage `profile: premium`). Ikke degrader.
|
||||
- **Kjøremodus:** `/trekexecute --fg` (sekvensielt, abonnement). IKKE parallell `claude -p` (API-billing). `/trekcontinue` per fersk sesjon.
|
||||
- **Doc-plikt:** hver feature-endring som pushes oppdaterer plugin-README + plugin-CLAUDE + rot-README i samme commit.
|
||||
- **Cross-repo:** ingen skriving til `/Users/ktg/repos/maskinrommet/` uten eksplisitt instruks.
|
||||
- **Ingen push** uten eksplisitt instruks.
|
||||
- **Hooks (bash 3.2 / Node):** alle nye hooks i Node `.mjs`; rediger `hooks/hooks.template.json` + `hooks/prompts/*.md` → kjør `python3 hooks/scripts/compile-hooks.py`.
|
||||
- **Redaksjonell kvalitet på .md-prompter** (kommandoer/agenter) verifiseres av operatør via annoterings-HTML, ikke mekanisk.
|
||||
|
||||
## 8. Åpne spørsmål (avklares i plan, ikke blokkerende)
|
||||
|
||||
- `edition-config.json` JSON vs. frontmatter (anbefalt JSON).
|
||||
- `video-scripter` absorberes i `content-repurposer`? (besluttes sent).
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
# Dogfood S13 — friction log (`/linkedin:newsletter` end-to-end)
|
||||
|
||||
**Step 14 (fasit S13) deliverable.** A real end-to-end dogfood of the long-form
|
||||
pipeline against a **throwaway fixture** (operator decision 2026-05-27: throwaway
|
||||
fixture in `docs/review/` scope, synthetic topic, no cross-repo write to
|
||||
maskinrommet). The fixture series lives at
|
||||
`docs/review/dogfood-serie/` (gitignored — throwaway, not committed). This log is
|
||||
the only committed artifact.
|
||||
|
||||
This is a **design-friction** log, not a content-quality review. Per plan Step 14
|
||||
"On failure: escalate — capture friction in the log, do not force a green check."
|
||||
The pipeline's deterministic backbone (render scripts, edition-state, queue) was
|
||||
**executed for real**; the gate layer (fact-check, persona sweep) is **BLOCKED**
|
||||
(see F1/F2) and its verdicts were **not fabricated**.
|
||||
|
||||
---
|
||||
|
||||
## Order-proof (the headline assertion this step must record)
|
||||
|
||||
The persona sweep is wired to run **FØR lås / before lock**, exactly as fasit §0.4
|
||||
mandates. Verified structurally (gate execution itself is blocked by F1/F2, so the
|
||||
proof is at the wiring level, not a live JA):
|
||||
|
||||
1. `config/edition-state.template.json` → `_doc.phases` orders the phases
|
||||
`consistency-quality → factcheck-sweep → persona-sweep-prelock → annotation →
|
||||
lock-delivery`. `persona-sweep-prelock` (Step 6) precedes `lock-delivery`
|
||||
(Step 8) in the canonical phase list.
|
||||
2. `commands/newsletter.md` Step 6 and Step 8 each carry an explicit
|
||||
**"Order assertion (enforced)"** block stating the pipeline may not reach lock
|
||||
until the primær persona returns a clean JA from the pre-lock sweep.
|
||||
3. The fixture `linkedin/edition-HANDOVER.md` physically records §5 persona-sweep
|
||||
(BEFORE lock) **above** §lås, and `edition-state.json` `currentPhase` never
|
||||
advances to `lock-delivery` because the persona JA precondition is unmet.
|
||||
|
||||
So the before-lock ordering holds in the wiring. What the dogfood could **not** do
|
||||
is execute the gate — because of F1/F2 below.
|
||||
|
||||
---
|
||||
|
||||
## Friction points (numbered; ordered by severity)
|
||||
|
||||
### F1 — [BLOCKER] Agents invoked by bare name; harness requires namespace
|
||||
**What:** `commands/newsletter.md` issues every `Task` fan-out by **bare agent
|
||||
name** — `subagent_type: fact-checker` (Step 5), `persona-reviewer` (Steps 6, 9),
|
||||
and `content-repurposer` (Step 3). The Claude Code harness registers plugin agents
|
||||
**namespaced** as `linkedin-thought-leadership:<name>`. A bare name does not
|
||||
resolve.
|
||||
**Evidence:** Live test this session — `Task(subagent_type: "content-repurposer")`
|
||||
returned `Agent type 'content-repurposer' not found`, with the available list
|
||||
showing only `linkedin-thought-leadership:content-repurposer`. Same failure for
|
||||
`fact-checker` and `persona-reviewer`.
|
||||
**Impact:** Steps 3, 5, 6, 9 — the entire gate/draft fan-out machinery — fail as
|
||||
written. This is the core of the long-form pipeline.
|
||||
**Implicates:** `commands/newsletter.md` (lines ~299, 398–399, 467–469, 638) →
|
||||
**Step 15 fix:** namespace all `subagent_type` references to
|
||||
`linkedin-thought-leadership:<name>` (or confirm the intended invocation form and
|
||||
align the command to it).
|
||||
|
||||
### F2 — [BLOCKER / ENV] `fact-checker` + `persona-reviewer` not registered this session
|
||||
**What:** Even namespaced, neither `fact-checker` nor `persona-reviewer` appears in
|
||||
the harness's available-agents list, while every other LTL agent (incl.
|
||||
`content-repurposer`) does. The two agents were added in S4/S5 (commits
|
||||
`be03d44`, `1faffac`) after the running session's plugin agent registry was built.
|
||||
**Evidence:** Available list this session contains
|
||||
`linkedin-thought-leadership:content-repurposer` etc. but **not** `…:fact-checker`
|
||||
or `…:persona-reviewer`. Their frontmatter is well-formed and structurally
|
||||
identical to `content-repurposer` (verified `name`/`description`/`model`/`color`/
|
||||
`tools`) — so this is **not** a frontmatter defect; it is a registry/reload gap.
|
||||
**Impact:** Compounds F1 — even after namespacing, Steps 5/6/9 cannot run until the
|
||||
session reloads the plugin agent set.
|
||||
**Implicates:** environment (Claude Code reload to register new agents) +
|
||||
**doc fix:** note in `CLAUDE.md`/`README.md` that adding a plugin agent requires a
|
||||
session reload before it is invokable. Not a code defect in the agent files.
|
||||
|
||||
### F3 — [MAJOR] No template for 3 of the 4 series-folder input artifacts
|
||||
**What:** The pipeline depends on four artifacts in the series folder:
|
||||
`edition-state.json`, `edition-config.json`, `edition-delingstekst.md`, and the
|
||||
`edition-HANDOVER.md`. Only `edition-state.json` ships a template
|
||||
(`config/edition-state.template.json`). The formats of `edition-config.json` and
|
||||
`edition-delingstekst.md` are discoverable **only** by reading the render scripts.
|
||||
**Evidence:** To run the dogfood I had to reverse-engineer both formats from
|
||||
`render/build-linkedin.mjs` (`loadEditionConfig` shape; `parseDelingstekst`
|
||||
section grammar `## Del N —` / `## Samle`, `**Første kommentar:**`, `#hashtag`
|
||||
line). A new operator has no shipped reference.
|
||||
**Impact:** Step 8 says "confirm the delivery inputs exist" with **no generation
|
||||
path** — a fresh edition cannot produce these from anything the plugin ships.
|
||||
**Implicates:** `config/` (add `edition-config.template.json` +
|
||||
`edition-delingstekst.template.md` + an `edition-HANDOVER.template.md`) and
|
||||
`commands/newsletter.md` Step 8 (point at the templates) → **Step 15 fix.**
|
||||
|
||||
### F4 — [MAJOR] Draft filename/location is inconsistent across steps
|
||||
**What:** Step 3 says write the draft to `<serie>/linkedin/<article>.draft.md`.
|
||||
Steps 7 and 8 expect `NN-utkast.md` (two-digit NN prefix) in the **series root**.
|
||||
`build-linkedin.mjs` **silently skips** any file without an `NN` prefix
|
||||
(`↷ hopper over … (ikke NN-prefiks)`).
|
||||
**Evidence:** `render/build-linkedin.mjs` line 357 regex `^(\d{2})`; the command
|
||||
text uses two different paths/names for the same draft.
|
||||
**Impact:** Following Step 3 literally produces a file (`*.draft.md`, inside
|
||||
`linkedin/`) that Step 8 then silently skips — a green exit with no POST.html.
|
||||
**Implicates:** `commands/newsletter.md` (reconcile Step 3 vs Steps 7/8 on draft
|
||||
filename + location) → **Step 15 fix.**
|
||||
|
||||
### F5 — [MAJOR] Series root hardcoded to maskinrommet
|
||||
**What:** Step 0 resolves the series folder under
|
||||
`/Users/ktg/repos/maskinrommet/serier/<slug>/` with no parameter to point
|
||||
elsewhere. Dogfooding (or any other repo / a throwaway fixture) requires a manual
|
||||
mental override of the documented path.
|
||||
**Evidence:** Step 0.1 and the architecture note both hardcode the maskinrommet
|
||||
path; the dogfood had to invent `docs/review/dogfood-serie/` off-spec.
|
||||
**Impact:** The command is coupled to one specific external repo. Operator must
|
||||
deviate from the written procedure for any other location.
|
||||
**Implicates:** `commands/newsletter.md` Step 0 (accept/derive a series-root arg;
|
||||
keep maskinrommet as default, not the only path) → **Step 15 fix (or deferred with
|
||||
operator note if maskinrommet-only is intentional).**
|
||||
|
||||
### F6 — [MINOR] `build-linkedin.mjs` carries Seres-specific hardcoding
|
||||
**What:** `CAROUSEL = new Set(["03","06"])` and the **unconditional** samle-post
|
||||
build are Seres-series assumptions baked into a script that S2 "generalized."
|
||||
**Evidence:** Dogfood produced `linkedin/samle/POST.html` for a single-edition
|
||||
fixture that has no series to summarize; the carousel set is dead for any series
|
||||
whose carousel editions aren't 03/06.
|
||||
**Impact:** Low for correctness (samle is harmless extra output), but it is
|
||||
generalization debt — the script still assumes the Seres shape.
|
||||
**Implicates:** `render/build-linkedin.mjs` (lines 63, 364–370) → likely **defer**
|
||||
to a later generalization pass; note for operator.
|
||||
|
||||
### F7 — [MINOR] `build-html.mjs` returns exit 0 on a missing input file
|
||||
**What:** A missing input arg prints `Fant ikke: <path>` to stderr and `continue`s;
|
||||
the loop completes with **exit 0** and no HTML produced.
|
||||
**Evidence:** `render/build-html.mjs` lines 1045–1048 (`continue`, no `process.exit`,
|
||||
no error accumulation).
|
||||
**Impact:** Step 7 already flags this (N3), but the script's silent exit-0 is a
|
||||
footgun: a typo'd filename looks like success. The command must check that the
|
||||
expected output file exists, not just the exit code.
|
||||
**Implicates:** `render/build-html.mjs` (exit non-zero if zero files written) +/or
|
||||
`commands/newsletter.md` Step 7 (verify output file, not just exit) → **Step 15
|
||||
fix candidate.**
|
||||
|
||||
### F8 — [MINOR] Fatal-vs-graceful asymmetry on missing delivery inputs (Step 8)
|
||||
**What:** Step 8 says "if either file is absent the script throws on read." In fact
|
||||
`edition-config.json` is **graceful** (`loadEditionConfig` falls back to empty
|
||||
defaults), while `edition-delingstekst.md` is **fatal** — `parseDelingstekst()`
|
||||
runs unconditionally at the top of `main()` and ENOENT-throws **before any
|
||||
POST.html is written**, including the article POST that does not depend on it.
|
||||
**Evidence:** `render/build-linkedin.mjs` lines 45–59 (graceful config) vs 180
|
||||
(`fs.readFileSync(DELINGSTEKST_FILE)` with no try) called at line 349.
|
||||
**Impact:** The command's description of the failure mode is inaccurate, and a
|
||||
missing delingstekst kills the whole build rather than degrading.
|
||||
**Implicates:** `render/build-linkedin.mjs` (guard `parseDelingstekst` like config)
|
||||
+ `commands/newsletter.md` Step 8 (correct the "either file throws" wording) →
|
||||
**Step 15 fix candidate.**
|
||||
|
||||
### F9 — [MINOR] `agents/README.md` registered as an agent
|
||||
**What:** The harness available list includes `linkedin-thought-leadership:README`
|
||||
— a non-agent README in `agents/` is being picked up as an invokable agent.
|
||||
**Evidence:** Available-agents list this session contains `…:README`.
|
||||
**Impact:** Cosmetic/registry noise; not harmful but pollutes the agent namespace.
|
||||
**Implicates:** `agents/README.md` (relocate, or ensure it lacks agent frontmatter)
|
||||
→ likely **defer** / doc-pass.
|
||||
|
||||
---
|
||||
|
||||
## What ran clean (no friction)
|
||||
|
||||
- **Step 7 annotation** — `build-html.mjs 01-utkast.md` (cwd = serie-mappe) → wrote
|
||||
`review/01-utkast.html` (30.4 KB), exit 0. ✅
|
||||
- **Step 8 render** — `build-linkedin.mjs 01-utkast.md` → `linkedin/01/POST.html`
|
||||
+ `linkedin/samle/POST.html`, exit 0; body survived (headings/lists/strong
|
||||
present in POST.html). ✅ (Render works; the *lock gate* is blocked, not the
|
||||
renderer.)
|
||||
- **cwd contract** — running both scripts from the series folder resolved
|
||||
`linkedin/` and `review/` correctly. The command's "cd to the series folder"
|
||||
instruction is right; the footgun is only if you forget (then output lands under
|
||||
the plugin).
|
||||
- **edition-state schema** — the template's phase list and article-status values
|
||||
were sufficient to represent the walk; resumption table in Step 0 is coherent.
|
||||
|
||||
## Friction summary for Step 15 (revert/fix targets)
|
||||
|
||||
| # | Severity | Implicated file | Step 15 disposition | Status |
|
||||
|---|----------|-----------------|---------------------|--------|
|
||||
| F1 | BLOCKER | `commands/newsletter.md` | namespace agent calls | ✅ |
|
||||
| F2 | BLOCKER/env | env + `CLAUDE.md`/`README.md` | reload + document | ✅ (doc) / 🔶 (reload = operator) |
|
||||
| F3 | MAJOR | `config/` + `commands/newsletter.md` | add 3 templates | ✅ |
|
||||
| F4 | MAJOR | `commands/newsletter.md` | reconcile draft path/name | ✅ |
|
||||
| F5 | MAJOR | `commands/newsletter.md` | de-hardcode series root | ✅ |
|
||||
| F6 | MINOR | `render/build-linkedin.mjs` | config-derive carousel; fix samle comment | ✅ |
|
||||
| F7 | MINOR | `render/build-html.mjs` (+ Step 7) | exit non-zero on no output | ✅ |
|
||||
| F8 | MINOR | `render/build-linkedin.mjs` (+ Step 8) | guard delingstekst + fix wording | ✅ |
|
||||
| F9 | MINOR | `agents/README.md` | relocate out of `agents/` | ✅ (relocated) / 🔶 (de-register on reload) |
|
||||
|
||||
**Headline:** the long-form pipeline's deterministic backbone is sound, but its
|
||||
**gate layer is currently un-runnable** (F1 + F2). Step 15 must close F1 (a concrete
|
||||
one-line-per-call edit) and F2 (reload + a doc note) before the pre-lock persona
|
||||
sweep can actually execute — the order is correctly wired, it just cannot fire yet.
|
||||
|
||||
---
|
||||
|
||||
## Step 15 (S14) — re-test outcomes
|
||||
|
||||
All nine friction points were closed (operator elected to fix F6–F9 rather than
|
||||
defer). Each was re-tested with a concrete check, not a self-asserted "fixed."
|
||||
|
||||
- **F1 — ✅ closed.** All four `Task` call sites in `commands/newsletter.md`
|
||||
(content-repurposer Step 3, fact-checker Step 5, persona-reviewer Steps 6 + 9)
|
||||
now use the namespaced `subagent_type: linkedin-thought-leadership:<name>`, plus a
|
||||
canonical "Agent invocation form (required)" note near the foreground principle.
|
||||
**Check:** `grep -nE 'subagent_type: (fact-checker|persona-reviewer|content-repurposer)' commands/newsletter.md`
|
||||
→ zero bare names; `grep -nE 'linkedin-thought-leadership:(fact-checker|persona-reviewer|content-repurposer)'`
|
||||
→ all 4 sites namespaced.
|
||||
- **F2 — ✅ doc / 🔶 reload.** Documented in `CLAUDE.md` (Agents section: invocation
|
||||
form + reload requirement) and `README.md` (Agent Architecture note). The
|
||||
environmental half — registering a newly-added agent — inherently requires a
|
||||
Claude Code **session reload**; that is an operator action, not a code change.
|
||||
Confirmed F2 persists across `/clear`: `fact-checker`/`persona-reviewer` were
|
||||
still absent from this fresh session's agent registry (only the 15 older agents +
|
||||
README appeared), proving it is a reload gap, not a per-session fluke.
|
||||
- **F3 — ✅ closed.** Added `config/edition-config.template.json`,
|
||||
`config/edition-delingstekst.template.md`, `config/edition-HANDOVER.template.md`
|
||||
(formats reverse-engineered from the render scripts, now shipped as reference).
|
||||
Wired into `newsletter.md` Step 0 (HANDOVER), Step 8 (config + delingstekst), and
|
||||
the Reference Files footer. **Check:** all three exist under `config/`;
|
||||
`edition-config.template.json` parses as valid JSON.
|
||||
- **F4 — ✅ closed.** Step 3 now writes the canonical `<serie>/NN-utkast.md` in the
|
||||
series root (the exact file Steps 7/8 render), with an explicit "do NOT write
|
||||
`linkedin/<article>.draft.md`" warning. **Check:** no `.draft.md`/`<article>` path
|
||||
refs remain except the intentional anti-pattern warning.
|
||||
- **F5 — ✅ closed.** Step 0 resolves a **series root** via an order (explicit path
|
||||
arg → `${LTL_SERIES_ROOT:-…/maskinrommet/serier}/<slug>/` → ask once); maskinrommet
|
||||
is the default, not the only path. The architecture preamble was aligned to match.
|
||||
- **F6 — ✅ closed.** `CAROUSEL = new Set(["03","06"])` removed; carousel editions are
|
||||
now config-derived (`config.carousel`, a list of NN strings) via `loadEditionConfig`
|
||||
+ `EMPTY_CONFIG` + the new config template. Misleading "samle bygges alltid" comment
|
||||
corrected (build has always been gated on `shareMap.samle`). **Check:** `grep -n
|
||||
CAROUSEL render/build-linkedin.mjs` → none; 20/20 render tests pass (one assertion
|
||||
updated to include the `carousel: []` default).
|
||||
- **F7 — ✅ closed.** `build-html.mjs main()` now counts files written, prints
|
||||
`Ingen HTML produsert …` and the CLI guard exits non-zero when zero files are
|
||||
produced (no more silent exit-0 on a typo'd filename). Step 7 wording updated to
|
||||
rely on the exit code AND verify the output file. **Re-tested live:** missing input
|
||||
→ exit 1; valid input → exit 0 + `review/NN-utkast.html` written.
|
||||
- **F8 — ✅ closed.** `parseDelingstekst()` wrapped in try/catch returning `{}` on
|
||||
ENOENT, matching `loadEditionConfig`'s fail-soft contract; Step 8 wording corrected
|
||||
("both inputs optional and graceful," not "either file throws"). **Re-tested live:**
|
||||
no delingstekst → `build-linkedin.mjs` exit 0 + `linkedin/01/POST.html` still built.
|
||||
- **F9 — ✅ relocated / 🔶 de-register on reload.** `git mv agents/README.md
|
||||
docs/agents-capability-matrix.md` — the only reliable fix, since the file registers
|
||||
by filename (it had no frontmatter yet still appeared as
|
||||
`linkedin-thought-leadership:README`). The stale registration clears on the next
|
||||
Claude Code reload (env, same as F2). **Check:** no README in `agents/`;
|
||||
`docs/agents-capability-matrix.md` present.
|
||||
|
||||
### Finding (out of Step 15 scope — recorded, not actioned)
|
||||
|
||||
`plan.md` Steps 16, 17, 18 hard-code `agents/README.md` as an explicit `grep` search
|
||||
path (plan.md:635, 727, 849). After F9's relocation that path no longer exists. The
|
||||
explicit arg is **redundant** with the recursive `agents/` search those greps already
|
||||
include, so dropping it (or repointing to `docs/agents-capability-matrix.md`) is
|
||||
safe — but `plan.md` is outside Step 15's Files list (Hard Rule 2), so this is logged
|
||||
for the operator/those steps rather than edited here. **Action for Steps 16–18:** drop
|
||||
the dangling `agents/README.md` arg from their Verify greps, or repoint to the new path.
|
||||
995
plugins/linkedin-studio/docs/voyage-build/plan.md
Normal file
995
plugins/linkedin-studio/docs/voyage-build/plan.md
Normal file
|
|
@ -0,0 +1,995 @@
|
|||
---
|
||||
task: "Lift linkedin-thought-leadership plugin v1.2.0 → v2.0.0 — full-spectrum content engine + newsletter, net-fewer commands/agents"
|
||||
slug: ltl-v2-fullspektrum
|
||||
project_dir: docs/voyage-build
|
||||
created: 2026-05-26
|
||||
plan_version: 1.7
|
||||
profile: premium
|
||||
phase_models:
|
||||
plan: opus
|
||||
execute: opus
|
||||
profile_source: state-mandate
|
||||
---
|
||||
|
||||
# LTL v2.0.0 — Full-Spectrum LinkedIn Content Engine
|
||||
|
||||
> **Plan quality: A** (90/100) — APPROVE_WITH_NOTES (revised after adversarial review: 3 blockers + 5 major + 4 minor resolved; see Revisions)
|
||||
>
|
||||
> Generated by trekplan v5.1.1 on 2026-05-26 — `plan_version: 1.7`
|
||||
>
|
||||
> **This is the Voyage-executable plan.** The authoritative human spec is
|
||||
> [`../plan-fullspektrum-innholdsmotor.md`](../plan-fullspektrum-innholdsmotor.md)
|
||||
> (the "fasit"). This file translates that spec's 20 sessions (S1–S20) into
|
||||
> Voyage `### Step N:` steps with per-step Manifests. Where the two differ,
|
||||
> the corrections in this plan (verified against actual files by the
|
||||
> exploration swarm) win — they are noted inline.
|
||||
|
||||
## Context
|
||||
|
||||
LTL must own the entire chain for ALL LinkedIn content — from short-form post
|
||||
to newsletter edition — at the quality the "Seres series" production proved
|
||||
possible, **while reducing** the total command/agent surface through
|
||||
consolidation. Three work-bodies (brief §1):
|
||||
|
||||
1. **Renovation** — consolidate real redundancy (27→~23 commands, 16→~14 agents).
|
||||
2. **Long-form capability** — one new `/linkedin:newsletter` command with a
|
||||
phased, multi-session pipeline (research → draft → fact-check → persona-review
|
||||
BEFORE lock → delivery → hook-gate).
|
||||
3. **Render + annotation into the plugin** — move 4 render scripts + fonts;
|
||||
generalize `build-linkedin`; generalize `build-html` into an artifact
|
||||
annotation renderer.
|
||||
|
||||
Decisions A–H (fasit §2) are LOCKED and not re-litigated here. The model is
|
||||
Opus 4.7 on everything; execution is `/trekexecute --fg` (subscription),
|
||||
`/trekcontinue` per fresh session.
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Plugin = engine"
|
||||
CMD["/linkedin:newsletter (new)"]
|
||||
FC["agents/fact-checker (new)"]
|
||||
PR["agents/persona-reviewer (new)"]
|
||||
PERS["config/personas.template.md (new)"]
|
||||
RENDER["render/ — build-html, build-linkedin, build-pdf, build-carousel + fonts"]
|
||||
QRULES["references/longform-quality-rules.md (new)"]
|
||||
CMD --> FC
|
||||
CMD --> PR
|
||||
CMD --> PERS
|
||||
CMD --> RENDER
|
||||
CMD --> QRULES
|
||||
end
|
||||
subgraph "Consolidation (net fewer)"
|
||||
Q["templates→quick"]
|
||||
C["publish→calendar"]
|
||||
O["collab+speaking→outreach"]
|
||||
A["authority→strategy"]
|
||||
AN["analytics merge / engagement merge"]
|
||||
RETIRE["retire content-tracker + personalization-scorer → scripts"]
|
||||
end
|
||||
subgraph "maskinrommet = workbench (other repo, read-only here)"
|
||||
SERIE["serier/<slug>/ — content + edition-state + edition-config.json"]
|
||||
end
|
||||
RENDER -. "cwd = serie-mappe" .-> SERIE
|
||||
```
|
||||
|
||||
## Codebase Analysis
|
||||
|
||||
- **Tech stack:** Claude Code plugin. Commands + agents are `.md` files
|
||||
(YAML frontmatter + Markdown system-prompt bodies). Hooks are Node.js `.mjs`
|
||||
compiled from `hooks/hooks.template.json` via `hooks/scripts/compile-hooks.py`.
|
||||
Tests use the built-in `node:test` + `node:assert/strict`. Zero npm deps in
|
||||
hooks/scripts. Node v25.8.2 (so `node --test <dir>` is broken — glob form only).
|
||||
- **Size:** 166 source files (medium). 27 commands, 16 agents (+ `agents/README.md`),
|
||||
9 hooks, 6 skills.
|
||||
- **Command template** (`commands/pipeline.md`, `commands/react.md`): frontmatter
|
||||
is `name: linkedin:<verb>`, `description: |` block ending in a `Triggers on:`
|
||||
line, `allowed-tools:` YAML bullet list (only tools actually used). Body: H1 +
|
||||
"You are a…" persona line + `## Step 0: Load Context` onward + closing
|
||||
`## Reference Files` bullet list using `${CLAUDE_PLUGIN_ROOT}/...` paths.
|
||||
- **Agent frontmatter** (`agents/differentiation-checker.md`,
|
||||
`agents/content-repurposer.md`): `name:` bare kebab-case, `description: |`
|
||||
block ending in `Triggers on:`, `model:` bare keyword, `color:` bare word,
|
||||
`tools: ["Read", ...]` JSON inline array. **Existing agents use `model: sonnet`,
|
||||
but the fasit (§6.2/§6.3) + KTG global rule mandate `model: opus` for the two
|
||||
new agents.**
|
||||
- **Reuse targets (verified):** `hooks/scripts/state-updater.mjs` (export
|
||||
functions + CLI guard `if (import.meta.url === \`file://${process.argv[1]}\`)`
|
||||
at line 227 — the canonical export/guard pattern), `queue-manager.mjs`
|
||||
(`PLUGIN_ROOT` env + `__dirname`), `personalization-score.mjs`
|
||||
(`calculateScore(pluginRoot)`), `clipboard-helper.mjs`, `compile-hooks.py`,
|
||||
`skills/linkedin-content-creation/SKILL.md`, `references/newsletter-strategy-guide.md`.
|
||||
- **${CLAUDE_PLUGIN_ROOT}** is injected by Claude Code into command-prompt Bash
|
||||
(proven at runtime: `pipeline.md:108,114,137,188`, `calendar.md:25`). Scripts
|
||||
do NOT read it internally — they self-resolve via `__dirname`/env. (Correction
|
||||
to fasit wording.)
|
||||
- **Render scripts** (`/Users/ktg/repos/maskinrommet/tools/`, read-only here):
|
||||
all 4 are zero-npm-dep (Node builtins only). build-html.mjs (963 lines),
|
||||
build-linkedin.mjs (364 lines, hardcoded Seres calendar/captions/freshness at
|
||||
lines 34–50: `CALENDAR`:34, `FRESHNESS`:44, `COVER_CREDIT`:49, `CAPTIONS`:50),
|
||||
build-pdf.mjs (345), build-carousel.mjs (268).
|
||||
|
||||
## Research Sources
|
||||
|
||||
No external web research was needed — the brief declares research complete
|
||||
(brief §4) and the four exploration agents (task-finder, convention-scanner,
|
||||
risk-assessor, test-strategist) confirmed the spec against actual files. Their
|
||||
material corrections are folded into the steps below and the Assumptions table.
|
||||
|
||||
## Corrections folded in from confirmatory exploration
|
||||
|
||||
These are verified deviations from the fasit. Each is a correction-in-scope
|
||||
(implements what the fasit already mandates, against the real files):
|
||||
|
||||
1. **Fonts:** only `build-pdf.mjs` + `build-carousel.mjs` consume fonts (via
|
||||
`file://` URLs from `__dirname/fonts`, not base64). `build-html.mjs` +
|
||||
`build-linkedin.mjs` use system-font stacks and need no fonts. Fonts dir =
|
||||
1.5 MB, 8 .ttf (Inter 400/600/700 + Newsreader 400/400i/600/600i/700).
|
||||
2. **No OFL license file exists** anywhere in maskinrommet. `render/OFL.txt`
|
||||
must be **authored/sourced** (Inter + Newsreader are OFL-1.1), not copied.
|
||||
3. **weasyprint graceful degradation is NOT implemented** — `build-pdf.mjs:339`
|
||||
and `build-carousel.mjs:262` hard-fail (`execFileSync` + `process.exit(1)`)
|
||||
on missing weasyprint. §7.4 degradation must be **written** during migration
|
||||
(Step 1), not assumed present. weasyprint IS installed on this machine (67.0)
|
||||
so the degradation path needs a forced-PATH-miss test.
|
||||
4. **Render scripts are not importable** — no `export`, no CLI guard, `main()`
|
||||
called unconditionally. The first production change for the generalized
|
||||
scripts (Steps 2, 3) is to add `export` + the CLI guard (copy
|
||||
`state-updater.mjs:227`). The failing import-test drives this naturally.
|
||||
5. **Agent "known-answer fixtures" cannot be `node:test`** (agents are .md
|
||||
prompts, no importable function, no deterministic LLM output). Split: (a) an
|
||||
automated `node:test` lints the fixture file's structure; (b) the actual
|
||||
accuracy comparison is an `[OPERATØR]`/`[GATE]` manual check — consistent
|
||||
with fasit §10.0 "subjective quality is never self-certified."
|
||||
6. **Dead-link blast radius is larger than the fasit's "Lav" labels** (N1, High):
|
||||
`publish` alone has 21 route-refs incl. 9 inside hook scripts
|
||||
(`session-start.mjs`, `posting-reminder.mjs`, `user-prompt-context.mjs`) that
|
||||
emit runtime guidance and break silently. The §10.2-F grep target set must add
|
||||
`agents/README.md` + `CLAUDE.md`. Treat every consolidation merge as Medium.
|
||||
7. **Skill catalogs duplicated across 6 skill dirs** (N2): the langform trigger
|
||||
+ catalog updates (Steps 11, 21) must sweep all 6 `skills/*/SKILL.md`, not
|
||||
just `linkedin-content-creation`.
|
||||
8. **`engagement-coach.md:24`** has a live "defer to the comment-strategist
|
||||
agent" cross-ref. Since comment-strategist merges INTO engagement-coach
|
||||
(Step 20), that line must be rewritten, not just deleted.
|
||||
9. **No existing parallel Task fan-out** to copy — the Step 8 fan-out
|
||||
(fasit assumption 4) is the single highest-uncertainty checkpoint; it is a
|
||||
pure runtime assumption with no static precedent.
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
> **Mapping:** one Voyage step = one fasit session. Each fasit session is
|
||||
> already sized to ≤35 % context as a single testable deliverable, so 1 step =
|
||||
> 1 `/trekcontinue` session is the correct granularity (not the usual 3–5
|
||||
> finer steps). The fasit session id (S1, S1a, …) is in each step title.
|
||||
> Internal TDD sub-steps live in **Changes** / **Test first**; the Manifest
|
||||
> encodes the session's binary Definition-of-Done (fasit §10.3).
|
||||
|
||||
### Step 1: S1 — Migrate render scripts + fonts into the plugin
|
||||
|
||||
- **Files:** `render/build-html.mjs`, `render/build-linkedin.mjs`, `render/build-pdf.mjs`, `render/build-carousel.mjs`, `render/fonts/` (8 .ttf), `render/OFL.txt` (all new)
|
||||
- **Changes:** Create `render/`. Copy the 4 scripts + `fonts/` (8 .ttf, 1.5 MB) FROM `/Users/ktg/repos/maskinrommet/tools/` (read-only source; do NOT modify maskinrommet). **Author `render/OFL.txt`** — the SIL Open Font License 1.1 text covering Inter + Newsreader (no license file exists at source; correction #2). **Add weasyprint graceful degradation** to `render/build-pdf.mjs` + `render/build-carousel.mjs` (correction #3): before `execFileSync("weasyprint", …)`, detect weasyprint on PATH; if absent, print a clear install instruction and skip the PDF step with a non-fatal warning instead of `process.exit(1)`. (codebase analysis)
|
||||
- **Reuses:** font-resolution pattern already in the scripts (`const FONT_DIR = path.join(__dirname, "fonts")`, build-pdf.mjs:154).
|
||||
- **Test first:**
|
||||
- File: `render/__tests__/weasyprint-degradation.test.mjs` (new)
|
||||
- Verifies: when `weasyprint` is not resolvable, the degradation helper returns a skip-signal (not a throw) and emits an install hint
|
||||
- Pattern: `hooks/scripts/__tests__/state-updater.test.mjs` (node:test + assert/strict, pure-function call)
|
||||
- **Verify:** `ls render/ && ls render/fonts/*.ttf | wc -l && node --test 'render/__tests__/*.test.mjs'` → expected: 4 `.mjs` + `fonts/` + `OFL.txt`; **8** .ttf (Inter-400/600/700, Newsreader-400/400i/600/600i/700); tests pass
|
||||
- **On failure:** revert — `git checkout -- render/ && rm -rf render/`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): migrate render scripts + fonts into plugin (S1)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- render/build-html.mjs
|
||||
- render/build-linkedin.mjs
|
||||
- render/build-pdf.mjs
|
||||
- render/build-carousel.mjs
|
||||
- render/OFL.txt
|
||||
- render/__tests__/weasyprint-degradation.test.mjs
|
||||
- render/fonts/Inter-400.ttf
|
||||
- render/fonts/Newsreader-400.ttf
|
||||
min_file_count: 8
|
||||
commit_message_pattern: "^feat\\(linkedin\\): migrate render scripts"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths:
|
||||
- /Users/ktg/repos/maskinrommet/tools/build-html.mjs
|
||||
- /Users/ktg/repos/maskinrommet/tools/build-linkedin.mjs
|
||||
must_contain:
|
||||
- path: render/build-pdf.mjs
|
||||
pattern: "weasyprint"
|
||||
- path: render/build-carousel.mjs
|
||||
pattern: "weasyprint"
|
||||
- path: render/OFL.txt
|
||||
pattern: "SIL Open Font License"
|
||||
```
|
||||
|
||||
### Step 2: S1a — Generalize the annotation renderer (build-html.mjs)
|
||||
|
||||
- **Files:** `render/build-html.mjs`, `render/__tests__/build-html.test.mjs` (new)
|
||||
- **Changes:** First make the script importable (correction #4): add `export` to `markdownToHtml`, `inline`, and the table/heading helpers, and wrap the CLI body in the guard `if (import.meta.url === \`file://${process.argv[1]}\`)` (copy `state-updater.mjs:227`). Then generalize the markdown→HTML engine (beslutning H): support tables (`| a | b |` → `<table>`), all heading levels `#`–`####` (today only `##`/`###`), inline `` `code` `` → `<code>` (today `inline()` only does `**bold**`/`*italic*`), and generic frontmatter/title. The annotation engine (CSS + client-JS: mark → Endre/Legg til/Fjern/Avklar/Risiko → sidebar → localStorage → export) is artifact-agnostic and embedded verbatim — no runtime dependency on maskinrommet or a temp file. (codebase analysis)
|
||||
- **Reuses:** `state-updater.mjs:227` CLI-guard pattern; the existing annotation engine inside the source `build-html.mjs`.
|
||||
- **Test first:**
|
||||
- File: `render/__tests__/build-html.test.mjs` (new)
|
||||
- Verifies: `markdownToHtml` converts a `| a | b |` block to `<table>`/`<tr>`/`<td>`; `#`→`<h1>` … `####`→`<h4>`; backtick span → `<code>`; empty input → no table; malformed row tolerated
|
||||
- Pattern: `hooks/scripts/__tests__/state-updater.test.mjs`
|
||||
- **Verify:** `node --test 'render/__tests__/*.test.mjs'` → expected: pass; then `cd /tmp && node <plugin>/render/build-html.mjs <a-table-heavy-md>` renders all tables (manual visual = `[OPERATØR]`)
|
||||
- **On failure:** revert — `git checkout -- render/build-html.mjs render/__tests__/build-html.test.mjs`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): generalize build-html annotation renderer — tables, headings, inline code (S1a)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- render/build-html.mjs
|
||||
- render/__tests__/build-html.test.mjs
|
||||
min_file_count: 2
|
||||
commit_message_pattern: "^feat\\(linkedin\\): generalize build-html"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: render/build-html.mjs
|
||||
pattern: "export"
|
||||
- path: render/build-html.mjs
|
||||
pattern: "import.meta.url"
|
||||
- path: render/build-html.mjs
|
||||
pattern: "<table>"
|
||||
- path: render/build-html.mjs
|
||||
pattern: "<h4"
|
||||
- path: render/build-html.mjs
|
||||
pattern: "<code>"
|
||||
- path: render/__tests__/build-html.test.mjs
|
||||
pattern: "<table>"
|
||||
```
|
||||
> **Manifest note (blocker fix):** the three `<table>`/`<h4`/`<code>`
|
||||
> patterns on the production `build-html.mjs` are the real generalization
|
||||
> predicate — a no-op that adds only `export` + the CLI guard but leaves the
|
||||
> markdown engine untouched will NOT emit `<table>`/`<h4`/`<code>` and fails
|
||||
> the Manifest. The `node --test` in Verify is the second gate.
|
||||
|
||||
### Step 3: S2 — Generalize build-linkedin.mjs to read edition-config.json
|
||||
|
||||
- **Files:** `render/build-linkedin.mjs`, `render/__tests__/build-linkedin.test.mjs` (new), `render/__tests__/fixtures/edition-config.json` (new)
|
||||
- **Changes:** First add `export` + CLI guard (correction #4). Replace the hardcoded `CALENDAR`/`FRESHNESS`/`CAPTIONS`/`COVER_CREDIT` constants (build-linkedin.mjs:34–50) with a read of `linkedin/edition-config.json` from `process.cwd()` (the serie-mappe). Resolve Q1 in favour of JSON (deterministic parsing). Provide a sensible default/empty-config path so a missing config degrades gracefully. (codebase analysis)
|
||||
- **Reuses:** `state-updater.mjs:227` guard; analytics fixture-dir convention (`scripts/analytics/tests/fixtures/`).
|
||||
- **Test first:**
|
||||
- File: `render/__tests__/build-linkedin.test.mjs` (new) + `fixtures/edition-config.json`
|
||||
- Verifies: changing values in the fixture config changes POST.html output (no code change) — fasit assumption 5; regression: a config matching the old hardcoded Seres values yields the previous output
|
||||
- Pattern: `scripts/analytics/tests/csv-parser.test.ts` (file-fixture loading)
|
||||
- **Verify:** `node --test 'render/__tests__/*.test.mjs'` → expected: pass (diff of two configs differs; regression config matches baseline)
|
||||
- **On failure:** revert — `git checkout -- render/build-linkedin.mjs render/__tests__/build-linkedin.test.mjs render/__tests__/fixtures/`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): generalize build-linkedin to read edition-config.json (S2)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- render/build-linkedin.mjs
|
||||
- render/__tests__/build-linkedin.test.mjs
|
||||
- render/__tests__/fixtures/edition-config.json
|
||||
min_file_count: 3
|
||||
commit_message_pattern: "^feat\\(linkedin\\): generalize build-linkedin"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: render/build-linkedin.mjs
|
||||
pattern: "edition-config"
|
||||
- path: render/build-linkedin.mjs
|
||||
pattern: "import.meta.url"
|
||||
```
|
||||
|
||||
### Step 4: S3 — Persona library (config/personas.template.md)
|
||||
|
||||
- **Files:** `config/personas.template.md` (new)
|
||||
- **Changes:** Create the reusable reader-persona library (beslutning D, fasit §6.1) with the 3 Seres seed personas: IT division director, AI-section lead, and line manager (primary). Per persona document: role, what disconnects them, what convinces them, expertise level, jargon tolerance. Mark the primary explicitly. Active overrides live in a gitignored `config/personas.local.md` (per `*.local.md`). (codebase analysis)
|
||||
- **Reuses:** template style of `config/state-file.template.md`, `config/user-profile.template.md`.
|
||||
- **Test first:** *(config file — structural check, not node:test)* DoD archetype C: file exists, required fields present (grep), parses.
|
||||
- **Verify:** `test -f config/personas.template.md && grep -c -E 'rolle|avkobler|overbeviser|ekspertise|sjargong' config/personas.template.md && grep -ci 'primær' config/personas.template.md` → expected: file present; field markers present for 3 personas; primary marked
|
||||
- **On failure:** revert — `git checkout -- config/personas.template.md`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): add reusable persona library template (S3)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- config/personas.template.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^feat\\(linkedin\\): add reusable persona library"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: config/personas.template.md
|
||||
pattern: "[Pp]rimær"
|
||||
```
|
||||
|
||||
### Step 5: S4 — fact-checker agent (agents/fact-checker.md)
|
||||
|
||||
- **Files:** `agents/fact-checker.md` (new), `agents/fixtures/fact-checker-cases.md` (new), `agents/__tests__/fact-checker-fixture.test.mjs` (new)
|
||||
- **Changes:** New Opus agent, `tools: ["Read", "WebSearch"]` (fasit §6.2). Mandate: given a block of factual claims, verify each against a primary/credible source under "guilty until proven" (aldri fyll hull med gjetninger; flag unverifiable explicitly); return a verification log + risk-sort 🔴/🟡/🟢. Copy the gate structure from `agents/differentiation-checker.md` (Mission → numbered search process with literal example queries → scored dimensions + verdict-threshold table → PASS/REWORK/BLOCK gate → fenced Output Format → Key Principles + Anti-Patterns) but change the mandate from originality to factual correctness. **Do not extend differentiation-checker** — orthogonal questions. Per correction #5: write a fasit fixture (3 claims: one true→🟢, one false→🔴, one unverifiable→🟡) + a node:test that lints the fixture's structure; the accuracy comparison is `[OPERATØR]`/`[GATE]`. (codebase analysis)
|
||||
- **Reuses:** `agents/differentiation-checker.md` gate-prompt form; `state-updater.test.mjs` test shape.
|
||||
- **Test first:**
|
||||
- File: `agents/__tests__/fact-checker-fixture.test.mjs` (new)
|
||||
- Verifies: the fixture file has exactly 3 cases, each with exactly one of 🟢/🔴/🟡 and a non-empty fasit field
|
||||
- Pattern: `hooks/scripts/__tests__/state-updater.test.mjs`
|
||||
- **Verify:** `node --test 'agents/__tests__/*.test.mjs' && grep -E '^model: opus' agents/fact-checker.md` → expected: fixture-lint passes; frontmatter has `model: opus`. Accuracy run against fixture = `[GATE: fact-checker output matches fasit form + verdicts]`
|
||||
- **On failure:** revert — `git checkout -- agents/fact-checker.md agents/fixtures/ agents/__tests__/fact-checker-fixture.test.mjs`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): add fact-checker agent + fixture (S4)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- agents/fact-checker.md
|
||||
- agents/fixtures/fact-checker-cases.md
|
||||
- agents/__tests__/fact-checker-fixture.test.mjs
|
||||
min_file_count: 3
|
||||
commit_message_pattern: "^feat\\(linkedin\\): add fact-checker agent"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths:
|
||||
- agents/differentiation-checker.md
|
||||
must_contain:
|
||||
- path: agents/fact-checker.md
|
||||
pattern: "model: opus"
|
||||
- path: agents/fact-checker.md
|
||||
pattern: "WebSearch"
|
||||
```
|
||||
|
||||
### Step 6: S5 — persona-reviewer agent (agents/persona-reviewer.md, 2 modes)
|
||||
|
||||
- **Files:** `agents/persona-reviewer.md` (new), `agents/fixtures/persona-reviewer-cases.md` (new), `agents/__tests__/persona-reviewer-fixture.test.mjs` (new)
|
||||
- **Changes:** New Opus agent, `tools: ["Read"]` (fasit §6.3). Mandate: read one persona definition (from Step 4's library) + the text → judge on 6 axes (hook holds? resonance? tone? credibility? leader-takeaway + concrete action? length/drive?). Return top-5 flags as **direction, not rewritten copy** (the jury NEVER writes text). Two modes in the same file (parameter in the call): resonance-mode (Step 8 of newsletter, BEFORE lock) and conversion-mode (Step 11, after lock — binary YES/NO "would YOU click?" on the hook only). Convergence-loop: re-run per persona judging LØST/DELVIS/IKKE until clean YES from primary. Per correction #5: fixture + structural lint test; accuracy = manual gate. (codebase analysis)
|
||||
- **Reuses:** `agents/differentiation-checker.md` form; `state-updater.test.mjs` test shape.
|
||||
- **Test first:**
|
||||
- File: `agents/__tests__/persona-reviewer-fixture.test.mjs` (new)
|
||||
- Verifies: fixture has a persona def + sample text + the 6 axis labels; both modes documented
|
||||
- Pattern: `hooks/scripts/__tests__/state-updater.test.mjs`
|
||||
- **Verify:** `node --test 'agents/__tests__/*.test.mjs' && grep -E '^model: opus' agents/persona-reviewer.md && grep -Eci 'resonans|konverter' agents/persona-reviewer.md` → expected: lint passes; opus; both modes present (BSD-grep-safe `-E`). Output-shape (≤5 flags, 6 axes, NO rewritten copy) = `[GATE]`
|
||||
- **On failure:** revert — `git checkout -- agents/persona-reviewer.md agents/fixtures/persona-reviewer-cases.md agents/__tests__/persona-reviewer-fixture.test.mjs`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): add persona-reviewer agent (2 modes) + fixture (S5)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- agents/persona-reviewer.md
|
||||
- agents/fixtures/persona-reviewer-cases.md
|
||||
- agents/__tests__/persona-reviewer-fixture.test.mjs
|
||||
min_file_count: 3
|
||||
commit_message_pattern: "^feat\\(linkedin\\): add persona-reviewer agent"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: agents/persona-reviewer.md
|
||||
pattern: "model: opus"
|
||||
```
|
||||
|
||||
### Step 7: S6 — Edition-state schema + retire content-tracker & personalization-scorer
|
||||
|
||||
- **Files:** `config/edition-state.template.json` (new), remove `agents/content-tracker.md`, remove `agents/personalization-scorer.md`, `agents/README.md` (update), `CLAUDE.md` (update agent table)
|
||||
- **Changes:** Define the edition-state schema (fasit §5.2) — current phase + per-article status — as a documented JSON template (beslutment G: production state lives in the serie-mappe, this is the schema the plugin defines). Retire the 2 deterministic agents (fasit §4.2): their function is already covered by `hooks/scripts/personalization-score.mjs` (placeholder detection) and `state-updater.mjs`/`calendar` (plan-vs-queue diff) — verify the script path covers it, then delete the agent files. Update `agents/README.md` (flow diagrams + Haiku table reference them — N1) and `CLAUDE.md` agent count. (codebase analysis)
|
||||
- **Reuses:** `hooks/scripts/personalization-score.mjs` `calculateScore()`; `state-updater.mjs`.
|
||||
- **Test first:** *(schema file — archetype C+F)* parse the JSON template; enumerate the retired agents' capabilities and confirm each is covered by an existing script (run `node -e` on personalization-score.mjs).
|
||||
- **Verify:** `node -e "JSON.parse(require('fs').readFileSync('config/edition-state.template.json','utf8'))" && ! test -f agents/content-tracker.md && ! test -f agents/personalization-scorer.md && ! grep -rn 'content-tracker\|personalization-scorer' agents/ README.md CLAUDE.md skills/ && ls agents/*.md | grep -v README | wc -l` → expected: JSON parses; both agents gone; **zero stray refs**; agent count = 16 (14 + 2 new fact-checker/persona-reviewer)
|
||||
- **On failure:** revert — `git checkout -- agents/ config/edition-state.template.json CLAUDE.md`
|
||||
- **Checkpoint:** `git commit -m "refactor(linkedin): edition-state schema + retire 2 deterministic agents to scripts (S6)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- config/edition-state.template.json
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^refactor\\(linkedin\\): edition-state schema"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: config/edition-state.template.json
|
||||
pattern: "phase"
|
||||
```
|
||||
> **Manifest-schema limit (acknowledged):** `must_contain` is positive-match
|
||||
> only, so the two agent *deletions* + capability-parity claim (Assumption 3)
|
||||
> cannot be encoded as a Manifest predicate. For this and every consolidation
|
||||
> step (16–21), the **`! test -f` + dead-link grep in Verify is the binding
|
||||
> completion predicate** — trekexecute must treat Verify as a gate here, not
|
||||
> just the Manifest. Archetype F (fasit §10.2) governs.
|
||||
|
||||
### Step 8: S7 — newsletter.md skeleton, Step 0–2 (load, calibrate, research fan-out)
|
||||
|
||||
- **Files:** `commands/newsletter.md` (new)
|
||||
- **Changes:** Create the orchestrator command following the LTL command template (`name: linkedin:newsletter`, `description: |` + Triggers, `allowed-tools:` incl. `Task`, `AskUserQuestion`, `Read`, `Bash`). Implement Step 0 (load edition-state/HANDOVER, voice-profile, persona library, serie-brief), Step 1 (brief + calibration, ≤3 questions, mark primary persona), Step 2 (parallel research **Task fan-out in foreground** — correction #9: highest-uncertainty checkpoint; no existing pattern to copy). Principle 4: fan-out from the command layer, never from a nested background agent. (codebase analysis)
|
||||
- **Reuses:** `commands/pipeline.md` step structure + `${CLAUDE_PLUGIN_ROOT}` Bash form; `commands/react.md` multi-source synthesis discipline.
|
||||
- **Test first:** *(command prose — archetype E)* run Step 0–2 against a dummy serie fixture; fasit assumption 4: confirm 2+ parallel research Task calls return structured (not degraded) results.
|
||||
- **Verify:** `grep -E '^name: linkedin:newsletter' commands/newsletter.md && grep -c 'Step [012]' commands/newsletter.md` → expected: frontmatter correct; Steps 0–2 present. Parallel-fan-out behavior = `[GATE: assumption-4 runtime test — count parallel calls + structured replies]`
|
||||
- **On failure:** escalate — if parallel Task fan-out degrades, stop and report (this is the load-bearing assumption); do not paper over with sequential calls without operator sign-off
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): newsletter command skeleton Step 0-2 (S7)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/newsletter.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^feat\\(linkedin\\): newsletter command skeleton"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/newsletter.md
|
||||
pattern: "name: linkedin:newsletter"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 0"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 2"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "[Pp]arallel"
|
||||
```
|
||||
> **Manifest note:** the `Step 0`/`Step 2`/`parallel` patterns force all three
|
||||
> phases + the fan-out wiring to be present (a single-string no-op fails). The
|
||||
> *runtime* behavior — that foreground `Task` fan-out keeps the Task tool and
|
||||
> returns non-degraded results (Assumption 1, the highest-uncertainty
|
||||
> checkpoint) — is the `[GATE]` in Verify and the `escalate` On-failure; it
|
||||
> cannot be encoded statically.
|
||||
|
||||
### Step 9: S8 — newsletter.md Step 3–4 (draft + consistency/quality)
|
||||
|
||||
- **Files:** `commands/newsletter.md` (edit)
|
||||
- **Changes:** Add Step 3 (draft in dramaturgical order, voice-matched, may span sessions with maintained HANDOVER — use `content-repurposer` extended + Task) and Step 4 (consistency + quality: threads, premise→conclusion arc, leader-takeaway, AI-slop removal, minimal formatting-dose). **Forward-reference fix (major):** `references/longform-quality-rules.md` is not authored until Step 13. So Step 4 **inlines the fasit §8 rules directly in `newsletter.md` here**; Step 13 then EXTRACTS them to `references/longform-quality-rules.md` and replaces the inline block with a pointer. No dangling reference at any point. (codebase analysis)
|
||||
- **Reuses:** `agents/content-repurposer.md`; voice-samples (always read before content — existing LTL rule); fasit §8 rule text (inlined now, extracted in Step 13).
|
||||
- **Test first:** *(archetype E)* Step 3–4 produce a draft file on the dummy serie; voice-match is `[OPERATØR]`/`[GATE: voice-trainer]` — NOT self-certified (fasit §10.0).
|
||||
- **Verify:** `grep -c 'Step 3' commands/newsletter.md && grep -c 'Step 4' commands/newsletter.md && grep -ci 'AI-slop\|premiss' commands/newsletter.md` → expected: Steps 3–4 present; §8 rules inlined. Draft quality = `[OPERATØR]`/`[GATE]`
|
||||
- **On failure:** revert — `git checkout -- commands/newsletter.md`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): newsletter Step 3-4 draft + consistency (S8)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/newsletter.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^feat\\(linkedin\\): newsletter Step 3-4"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/newsletter.md
|
||||
pattern: "content-repurposer"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 3"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 4"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "AI-slop"
|
||||
```
|
||||
|
||||
### Step 10: S9 — newsletter.md Step 5–6 (fact-check sweep + persona sweep BEFORE lock)
|
||||
|
||||
- **Files:** `commands/newsletter.md` (edit)
|
||||
- **Changes:** Add Step 5 (fact-check sweep: risk-sorted 🔴/🟡/🟢, "guilty until proven", verification log — fan-out N parallel `fact-checker` calls in foreground) and Step 6 (persona sweep BEFORE lock: reader-jury via `persona-reviewer` resonance-mode, primary trumps, convergence-loop to clean YES). This is the fix for the single biggest Seres process error (fasit §0.4 / principle 5). Order assertion: sweep precedes lock. (codebase analysis)
|
||||
- **Reuses:** `agents/fact-checker.md` (Step 5), `agents/persona-reviewer.md` (Step 6).
|
||||
- **Test first:** *(archetype E)* both agents invoked in parallel; order-assert: persona-sweep step appears before the lock step; `[GATE]` clean-YES-from-primary required to proceed.
|
||||
- **Verify:** `grep -n 'Step 5\|Step 6\|fact-checker\|persona-reviewer\|FØR lås\|before lock' commands/newsletter.md` → expected: Steps 5–6 present, both agents referenced, sweep ordered before lock. Verdicts = `[GATE]`
|
||||
- **On failure:** revert — `git checkout -- commands/newsletter.md`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): newsletter Step 5-6 fact-check + persona sweep before lock (S9)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/newsletter.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^feat\\(linkedin\\): newsletter Step 5-6"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/newsletter.md
|
||||
pattern: "fact-checker"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "persona-reviewer"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 5"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 6"
|
||||
```
|
||||
> **Order assertion** (persona-sweep Step 6 BEFORE lock — the single biggest
|
||||
> Seres process error, fasit §0.4): `must_contain` proves both phases exist;
|
||||
> the *ordering* (Step 6 precedes the Step 8 lock) is asserted by the grep in
|
||||
> Verify + is a `[GATE]`. A wiring that placed the sweep after lock would pass
|
||||
> `must_contain` but fail the Verify order-assert.
|
||||
|
||||
### Step 11: S10 — newsletter.md Step 7–10 (annotate, lock/delivery, hook-gate, schedule)
|
||||
|
||||
- **Files:** `commands/newsletter.md` (edit)
|
||||
- **Changes:** Add Step 7 (optional annotation: `render/build-html.mjs` → review HTML in `docs/review/`), Step 8 (LOCK → delivery: POST.html via `render/build-linkedin.mjs`, cwd = serie-mappe), Step 9 (hook/conversion gate: `persona-reviewer` conversion-mode on the distribution text, AFTER lock — order assertion), Step 10 (register edition in the queue via `queue-manager.mjs` for native scheduling). Correction (N3): when shelling out to render scripts, check exit codes — don't assume success. (codebase analysis)
|
||||
- **Reuses:** `render/build-html.mjs`, `render/build-linkedin.mjs`, `hooks/scripts/queue-manager.mjs`.
|
||||
- **Test first:** *(archetype E)* Step 8 produces POST.html on the dummy serie; order-assert: hook-gate (Step 9) runs AFTER lock (Step 8); edition registered in queue.
|
||||
- **Verify:** `grep -n 'Step 7\|Step 8\|Step 9\|Step 10\|POST.html\|build-linkedin\|queue-manager' commands/newsletter.md` → expected: Steps 7–10 present; render + queue wired; hook-gate after lock
|
||||
- **On failure:** revert — `git checkout -- commands/newsletter.md`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): newsletter Step 7-10 lock, delivery, hook-gate, schedule (S10)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/newsletter.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^feat\\(linkedin\\): newsletter Step 7-10"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/newsletter.md
|
||||
pattern: "build-linkedin"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "queue-manager"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 7"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 9"
|
||||
- path: commands/newsletter.md
|
||||
pattern: "Step 10"
|
||||
```
|
||||
> **Order assertion** (hook-gate Step 9 AFTER lock Step 8): `must_contain`
|
||||
> proves all four phases exist; ordering is the Verify grep + `[GATE]`.
|
||||
|
||||
### Step 12: S11 — Reconcile newsletter path out of multiplatform + skill trigger + router row
|
||||
|
||||
- **Files:** `commands/multiplatform.md` (edit), `commands/linkedin.md` (edit), `skills/linkedin-content-creation/SKILL.md` (edit) + the other 5 `skills/*/SKILL.md` catalogs (edit — correction #7)
|
||||
- **Changes:** Remove the newsletter/blog adaptation path from `multiplatform.md` so there is exactly ONE entry to long-form (fasit §4.1). Add the langform trigger to `skills/linkedin-content-creation/SKILL.md` (fasit §5.3) AND sweep the catalog tables in all 6 `skills/*/SKILL.md` (N2). Add a router row for `newsletter` in `commands/linkedin.md`. (codebase analysis)
|
||||
- **Reuses:** existing router-row format in `linkedin.md`; skill trigger format.
|
||||
- **Test first:** *(archetype F)* grep proves only one newsletter entry; router row present; no dead newsletter path in multiplatform.
|
||||
- **Verify:** `[ "$(grep -Eci 'Step.*newsletter|newsletter (pipeline|workflow|edition)' commands/multiplatform.md)" = "0" ] && grep -q 'newsletter' commands/linkedin.md` → expected: **0** multi-step newsletter section in multiplatform (a one-line pointer is allowed, any wording); router row present in linkedin.md. (Robust: does not depend on exact pointer phrasing — only that no *pipeline/section* survives. **Fix v2.0 doc-pass:** the original `grep -Eci ... && grep -c ...` was `&&`-broken — `grep -c` returns the count as stdout but exits non-zero when count=0, so the chain short-circuited even on the desired "0 matches" outcome. Reworked to an explicit string-equality test.)
|
||||
- **On failure:** revert — `git checkout -- commands/multiplatform.md commands/linkedin.md skills/`
|
||||
- **Checkpoint:** `git commit -m "refactor(linkedin): single newsletter entry + skill trigger + router row (S11)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/linkedin.md
|
||||
- commands/multiplatform.md
|
||||
min_file_count: 2
|
||||
commit_message_pattern: "^refactor\\(linkedin\\): single newsletter entry"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/linkedin.md
|
||||
pattern: "newsletter"
|
||||
```
|
||||
|
||||
### Step 13: S12 — longform-quality-rules.md + resumption wiring
|
||||
|
||||
- **Files:** `references/longform-quality-rules.md` (new), `commands/newsletter.md` (edit — Step 0 reads edition-state for resumption)
|
||||
- **Changes:** Codify the fasit §8 quality rules (leader-takeaway, premise→conclusion arc, forbidden AI-slop phrases, generic-not-agency-specific, minimal formatting-dose, gap-closing by tightening not expansion, per-sweep calibration). Wire resumption: Step 0 reads edition-state and continues from the right step (abort → re-run → resumes). (codebase analysis)
|
||||
- **Reuses:** edition-state schema (Step 7); fasit §8 content.
|
||||
- **Test first:** *(archetype C+E)* file exists with all §8 rules (grep); resumption: abort after Step 6 → re-run → resumes from Step 7 (deterministic via edition-state).
|
||||
- **Verify:** `test -f references/longform-quality-rules.md && grep -ci 'leder-takeaway\|premiss\|AI-slop\|formaterings-dose' references/longform-quality-rules.md` → expected: file present; rules present. Resumption = deterministic test
|
||||
- **On failure:** revert — `git checkout -- references/longform-quality-rules.md commands/newsletter.md`
|
||||
- **Checkpoint:** `git commit -m "feat(linkedin): longform quality rules + edition resumption wiring (S12)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- references/longform-quality-rules.md
|
||||
- commands/newsletter.md
|
||||
min_file_count: 2
|
||||
commit_message_pattern: "^feat\\(linkedin\\): longform quality rules"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: references/longform-quality-rules.md
|
||||
pattern: "AI-slop"
|
||||
```
|
||||
|
||||
### Step 14: S13 — Dogfood: produce a real edition end-to-end
|
||||
|
||||
> **`[OPERATØR]`-gated session — not pure headless.** This step runs the live
|
||||
> pipeline with a human in the loop (browser walkthrough, real edition).
|
||||
> A headless `claude -p` cannot self-certify a real edition's quality.
|
||||
|
||||
- **Files:** `docs/voyage-build/dogfood-S13-friction.md` (new — the in-plugin deliverable). Edition content is produced in a maskinrommet serie-mappe (operator-gated, stays in that repo).
|
||||
- **Changes:** Run `/linkedin:newsletter` end-to-end to produce one real edition (files in the serie-mappe). **Cross-repo write to maskinrommet requires explicit operator instruction** (R1) — confirm before writing there; otherwise dogfood against a throwaway serie fixture inside `docs/review/` scope. Open the review HTML in a browser and walk the core flows (dogfood-UI gate). **Write a structured friction log to `docs/voyage-build/dogfood-S13-friction.md`** recording: each friction point (numbered), an order-proof note (edition-HANDOVER shows persona-sweep BEFORE lock), and which pipeline file each friction implicates (drives Step 15's revert targets). (codebase analysis)
|
||||
- **Reuses:** the full Step 8–13 pipeline.
|
||||
- **Test first:** *(archetype G — operator/manual)* an edition produced end-to-end; order-proof: edition-HANDOVER shows persona-sweep BEFORE lock; review HTML opened.
|
||||
- **Verify:** `test -f docs/voyage-build/dogfood-S13-friction.md && grep -ci 'sweep.*lås\|before lock\|FØR lås' docs/voyage-build/dogfood-S13-friction.md` → expected: friction log exists; order-proof recorded. Edition quality + UI = `[OPERATØR]`
|
||||
- **On failure:** escalate — dogfood reveals design friction; capture it in the log, do not force a green check
|
||||
- **Checkpoint:** `git commit -m "test(linkedin): dogfood newsletter pipeline end-to-end (S13)"` *(edition content stays in maskinrommet; only the friction log is committed plugin-side)*
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- docs/voyage-build/dogfood-S13-friction.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^test\\(linkedin\\): dogfood newsletter"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths:
|
||||
- /Users/ktg/repos/maskinrommet/tools/build-html.mjs
|
||||
must_contain:
|
||||
- path: docs/voyage-build/dogfood-S13-friction.md
|
||||
pattern: "[Ff]riction|[Ff]riksjon"
|
||||
```
|
||||
> **Blocker fix:** the friction-log file is now a real, checkable deliverable
|
||||
> (file must exist + record the order-proof) — the step can no longer pass by
|
||||
> producing nothing. The edition's subjective quality stays `[OPERATØR]` per
|
||||
> fasit §10.0.
|
||||
|
||||
### Step 15: S14 — Fix dogfood friction
|
||||
|
||||
> **`[OPERATØR]`-gated session.** Revert targets come from the Step 14 friction
|
||||
> log's "implicates file X" notes — that log is the referent for every fix and
|
||||
> every `git checkout`.
|
||||
|
||||
- **Files:** the pipeline files named in `docs/voyage-build/dogfood-S13-friction.md`; `docs/voyage-build/dogfood-S13-friction.md` (update with re-test outcomes)
|
||||
- **Changes:** Close each friction point from Step 14 with a concrete fix; re-test each with a concrete check (not "fixed"). Update the friction log with per-item status (✅ re-tested / 🔶 deferred). Remaining items either closed or explicitly deferred with operator's knowledge. (codebase analysis)
|
||||
- **Reuses:** the S13 friction log (names the files to touch + revert).
|
||||
- **Test first:** *(archetype G)* each closed friction point re-tested with a concrete check; restliste empty or explicitly deferred.
|
||||
- **Verify:** `grep -c '✅\|🔶' docs/voyage-build/dogfood-S13-friction.md` → expected: every friction item has a ✅ (re-tested) or 🔶 (deferred with operator note) — no silent closures
|
||||
- **On failure:** revert the specific fix using the file path recorded against that friction item in the log (`git checkout -- <that file>`); if the log does not name the file, escalate
|
||||
- **Checkpoint:** `git commit -m "fix(linkedin): close dogfood friction (S14)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- docs/voyage-build/dogfood-S13-friction.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^fix\\(linkedin\\): close dogfood friction"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: docs/voyage-build/dogfood-S13-friction.md
|
||||
pattern: "✅|🔶"
|
||||
```
|
||||
|
||||
### Step 16: S15 — templates.md → mode in quick.md
|
||||
|
||||
- **Files:** `commands/quick.md` (edit), remove `commands/templates.md`, `commands/linkedin.md` (router edit)
|
||||
- **Changes:** Enumerate every one of the 8 template types in `templates.md` and confirm each is covered by a mode in `quick.md` (capability checklist — archetype F). Remove `templates.md`. Grep the expanded target set (`commands/ agents/ skills/ hooks/ README.md CLAUDE.md agents/README.md`) for `templates` route-refs and fix all (N1). (codebase analysis)
|
||||
- **Reuses:** existing `quick.md` 3-line formula + templates bank.
|
||||
- **Test first:** *(archetype F)* capability checklist: all 8 types in quick; `templates.md` gone; `ls commands/` down 1; no dead links.
|
||||
- **Verify:** `! test -f commands/templates.md && grep -rn '/linkedin:templates\|commands/templates' commands/ agents/ skills/ hooks/ README.md CLAUDE.md agents/README.md` → expected: file gone; zero stray route-refs (only intentional)
|
||||
- **On failure:** revert — `git checkout -- commands/quick.md commands/templates.md commands/linkedin.md`
|
||||
- **Checkpoint:** `git commit -m "refactor(linkedin): merge templates into quick (S15)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/quick.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^refactor\\(linkedin\\): merge templates into quick"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/quick.md
|
||||
pattern: "template"
|
||||
```
|
||||
|
||||
### Step 17: S16 — publish.md → action in calendar.md
|
||||
|
||||
- **Files:** `commands/calendar.md` (edit), remove `commands/publish.md`, plus the 9 hook-script refs (N1: `session-start.mjs`, `posting-reminder.mjs`, `user-prompt-context.mjs`, `hooks/prompts/state-update-reminder.md`)
|
||||
- **Changes:** Move the publish action into `calendar.md` (both read `queue.json`; calendar already routes to publish). Remove `publish.md`. **Critical (N1):** `publish` has 21 route-refs, 9 inside hook scripts that emit runtime guidance — update every one or the plugin ships text pointing at a dead command. Re-compile hooks if any `hooks/` source changed. (codebase analysis)
|
||||
- **Reuses:** `queue-manager.mjs`; existing calendar→publish routing.
|
||||
- **Test first:** *(archetype F)* capability checklist: calendar covers publish; `publish.md` gone; all 21 refs (incl. 9 hook refs) reconciled.
|
||||
- **Verify:** `! test -f commands/publish.md && grep -rn '/linkedin:publish\|commands/publish' commands/ agents/ skills/ hooks/ README.md CLAUDE.md` → expected: file gone; zero stray refs; `python3 hooks/scripts/compile-hooks.py --check` clean if hooks touched
|
||||
- **On failure:** revert — `git checkout -- commands/calendar.md commands/publish.md hooks/`
|
||||
- **Checkpoint:** `git commit -m "refactor(linkedin): merge publish into calendar — reconcile hook refs (S16)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/calendar.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^refactor\\(linkedin\\): merge publish into calendar"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/calendar.md
|
||||
pattern: "[Pp]ublish"
|
||||
```
|
||||
|
||||
### Step 18: S17 — collab.md + speaking.md → new outreach.md
|
||||
|
||||
- **Files:** `commands/outreach.md` (new), remove `commands/collab.md`, remove `commands/speaking.md`, `commands/linkedin.md` (router edit)
|
||||
- **Changes:** Create `outreach.md` covering both collab and speaking (structural twins: same outreach/pitch paradigm + pipeline table). Capability checklist: every function of both predecessors present in outreach. Remove both. Reconcile all route-refs (collab 8, speaking 8, incl. `README.md:511` ToS table). (codebase analysis)
|
||||
- **Reuses:** the shared outreach/pitch structure from both files.
|
||||
- **Test first:** *(archetype F)* checklist covers collab + speaking; both predecessors gone; net down 1; no dead links.
|
||||
- **Verify:** `test -f commands/outreach.md && ! test -f commands/collab.md && ! test -f commands/speaking.md && grep -rn '/linkedin:collab\|/linkedin:speaking' commands/ agents/ skills/ hooks/ README.md CLAUDE.md` → expected: outreach present; both gone; zero stray refs
|
||||
- **On failure:** revert — `git checkout -- commands/`
|
||||
- **Checkpoint:** `git commit -m "refactor(linkedin): merge collab + speaking into outreach (S17)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/outreach.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^refactor\\(linkedin\\): merge collab \\+ speaking into outreach"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/outreach.md
|
||||
pattern: "[Ss]peaking"
|
||||
```
|
||||
|
||||
### Step 19: S18 — authority.md → strategy.md + trajectory dedup + profile canon
|
||||
|
||||
- **Files:** `commands/strategy.md` (edit), remove `commands/authority.md`, `commands/audit.md` (edit — point to profile/strategy), `commands/analyze.md` (edit — point to profile)
|
||||
- **Changes:** Absorb `authority.md` into a section of `strategy.md` (authority has no unique core). De-duplicate trajectory logic to live only in `strategy.md`; `audit.md` references it. Make `profile.md` the canonical source for profile-alignment; `audit.md`/`analyze.md` point there. Remove `authority.md`. (codebase analysis)
|
||||
- **Reuses:** existing strategy phase content; profile-alignment check in `profile.md`.
|
||||
- **Test first:** *(archetype F)* strategy covers authority + trajectory; audit/analyze point to profile canon; `authority.md` gone; no dead links.
|
||||
- **Verify:** `! test -f commands/authority.md && grep -rn '/linkedin:authority\|commands/authority' commands/ agents/ skills/ hooks/ README.md CLAUDE.md` → expected: gone; zero stray refs
|
||||
- **On failure:** revert — `git checkout -- commands/`
|
||||
- **Checkpoint:** `git commit -m "refactor(linkedin): absorb authority into strategy + profile canon (S18)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- commands/strategy.md
|
||||
min_file_count: 1
|
||||
commit_message_pattern: "^refactor\\(linkedin\\): absorb authority into strategy"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: commands/strategy.md
|
||||
pattern: "[Aa]uthority"
|
||||
```
|
||||
|
||||
### Step 20: S19 — Agent merges: analytics (2→1) + engagement (2→1)
|
||||
|
||||
- **Files:** `agents/analytics-interpreter.md` (edit → analytics, 2 modes), remove `agents/performance-reporter.md`, `agents/engagement-coach.md` (edit → engagement), remove `agents/comment-strategist.md`, `agents/README.md` (edit), `CLAUDE.md` (edit)
|
||||
- **Changes:** Merge `performance-reporter` into `analytics-interpreter` (one analytics agent, interpret/report modes — identical data sources). Merge `comment-strategist` into `engagement-coach` (5x5x5 + first-hour + CEA). **Rewrite `engagement-coach.md:24`** ("defer to the comment-strategist agent" — correction #8) since the target now lives in-file. Decide Q2 (video-scripter → content-repurposer) here. Reconcile all refs incl. `agents/README.md` flow diagrams + `skills/linkedin-analytics/SKILL.md:40`. (codebase analysis)
|
||||
- **Reuses:** existing agent bodies (merge, don't rewrite from scratch).
|
||||
- **Test first:** *(archetype F)* each mode in the merged agent covers predecessors' functions (checklist); `ls agents/` down 2; no dead links; the self-ref at line 24 rewritten.
|
||||
- **Verify:** `! test -f agents/performance-reporter.md && ! test -f agents/comment-strategist.md && ! grep -n 'comment-strategist agent' agents/engagement-coach.md && grep -rn 'performance-reporter\|comment-strategist' agents/ skills/ README.md CLAUDE.md agents/README.md` → expected: both gone; self-ref rewritten; zero stray refs
|
||||
- **On failure:** revert — `git checkout -- agents/ CLAUDE.md`
|
||||
- **Checkpoint:** `git commit -m "refactor(linkedin): merge analytics + engagement agents 2→1 each (S19)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- agents/analytics-interpreter.md
|
||||
- agents/engagement-coach.md
|
||||
min_file_count: 2
|
||||
commit_message_pattern: "^refactor\\(linkedin\\): merge analytics \\+ engagement"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: agents/engagement-coach.md
|
||||
pattern: "CEA"
|
||||
```
|
||||
|
||||
### Step 21: S20 — import.md trim + router gating + final doc pass → v2.0.0
|
||||
|
||||
- **Files:** `commands/import.md` (edit), `commands/linkedin.md` (router gating edit), `README.md`, `CLAUDE.md`, `.claude-plugin/plugin.json`, `CHANGELOG.md`, marketplace `README.md` + `CLAUDE.md` (version refs)
|
||||
- **Changes:** Delegate `import.md` Step 6 analysis to `report.md` (two report generators run the same `trends` CLI). Add router gating in `linkedin.md` (show monetize/outreach/collab as "unlocks at ~1K followers"). **Version bump to v2.0.0** — update all 7 in-tree refs (`plugin.json:3`, `CLAUDE.md:1`, `README.md:9/23/42/522`, `CHANGELOG.md:8`) + 2 marketplace refs (`README.md:209`, `CLAUDE.md:12`), and fix the `#whats-new-v120` anchor → `#whats-new-v200`. Final doc pass: all 3 doc levels (plugin README + plugin CLAUDE + root README) reflect v2.0.0 scope (fasit §10.5). (codebase analysis)
|
||||
- **Reuses:** `report.md` trends CLI; existing version-sync discipline.
|
||||
- **Test first:** *(archetype F)* command count verified down; all version refs = 2.0.0 (grep); all 3 doc levels updated; router gating present.
|
||||
- **Changes (count, corrected):** net command count = **24** = 27 today − 5 removed (`templates`, `publish`, `authority`, `collab`, `speaking`) + 2 added (`newsletter`, `outreach`). (The fasit's "~23" was approximate; the exact arithmetic is 24. This is the binding number.)
|
||||
- **Verify:** `test "$(ls commands/*.md | wc -l | tr -d ' ')" = "24" && grep -q '"version": "2\\.0\\.0"' .claude-plugin/plugin.json && grep -q '^# LinkedIn Thought Leadership Plugin (v2\\.0\\.0)' CLAUDE.md && grep -q 'version-2\\.0\\.0-blue' README.md && grep -q '^## \\[2\\.0\\.0\\]' CHANGELOG.md` → expected: command count is **exactly 24**; every target file carries an **active** v2.0.0 marker (forward-positive assertion). **Fix v2.0 doc-pass:** original `! grep '1\\.2\\.0'` was overly strict — it would have blocked any release that correctly preserves historical changelog entries. Forward-positive form asserts the actual release intent (active version is 2.0.0) without requiring deletion of changelog history.
|
||||
- **On failure:** revert — `git checkout -- commands/ README.md CLAUDE.md .claude-plugin/ CHANGELOG.md`
|
||||
- **Checkpoint:** `git commit -m "chore(linkedin): v2.0.0 — import trim, router gating, full doc pass (S20)"`
|
||||
- **Manifest:**
|
||||
```yaml
|
||||
manifest:
|
||||
expected_paths:
|
||||
- .claude-plugin/plugin.json
|
||||
- CHANGELOG.md
|
||||
- README.md
|
||||
- CLAUDE.md
|
||||
min_file_count: 4
|
||||
commit_message_pattern: "^chore\\(linkedin\\): v2\\.0\\.0"
|
||||
bash_syntax_check: []
|
||||
forbidden_paths: []
|
||||
must_contain:
|
||||
- path: .claude-plugin/plugin.json
|
||||
pattern: "2.0.0"
|
||||
- path: CHANGELOG.md
|
||||
pattern: "2.0.0"
|
||||
- path: CLAUDE.md
|
||||
pattern: "2.0.0"
|
||||
- path: README.md
|
||||
pattern: "2.0.0"
|
||||
```
|
||||
> **Blocker fix:** the count predicate is now a single exact value (24), tested
|
||||
> with a string-equality assertion in Verify. The earlier self-contradictory
|
||||
> "22 / 23 / verify net" is removed. Note: agent net count stays 16 (4 removed,
|
||||
> 2 added — verified in the end-to-end Verification block).
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
| Approach | Pros | Cons | Why rejected |
|
||||
|----------|------|------|--------------|
|
||||
| Newsletter as a suite of 5 phase-commands | Each phase independently invocable | Surface explosion; against beslutning A; fragments resumption | Locked decision A: one orchestrator command |
|
||||
| Extend `commands/pipeline.md` for long-form | Reuse existing pipeline | pipeline is locked to feed-post artifact contract (wrong output shape) | Locked decision A |
|
||||
| Keep render scripts in maskinrommet, call cross-repo | No migration work | Forgejo downloaders get no render pipeline; cross-repo coupling | Locked decision C: ship render in plugin |
|
||||
| Finer Voyage steps (3–5 per session) | Matches Voyage default granularity | Explodes to 60+ steps; fasit already validated session sizing at ≤35 % context | 1 step = 1 session is the right grain here |
|
||||
| Integrate fact-check into research/review | Fewer agents | "Altinn-feilen" proved research notes miss facts; needs a dedicated sweep | Locked decision E: fact-check is its own step |
|
||||
|
||||
## Test Strategy
|
||||
|
||||
- **Framework:** `node:test` + `node:assert/strict`, zero deps. Run with the
|
||||
**glob form** `node --test 'render/__tests__/*.test.mjs'` (Node 25 breaks
|
||||
`node --test <dir>`).
|
||||
- **Existing patterns:** `hooks/scripts/__tests__/*.test.mjs` (inline
|
||||
template-literal fixtures, pure-function calls, `assert.match`/`assert.equal`);
|
||||
`scripts/analytics/tests/*.test.ts` (file fixtures in `tests/fixtures/`).
|
||||
- **Clean test-first (render scripts):** the first production change is to add
|
||||
`export` + the CLI guard, so the failing import-test drives the refactor
|
||||
(Steps 2, 3). Mirrors `state-updater.mjs:227`.
|
||||
- **Awkward test-first (agents):** agents are .md prompts — not unit-testable.
|
||||
Automate a **fixture-schema lint** as `node:test`; route the actual accuracy
|
||||
comparison to an `[OPERATØR]`/`[GATE]` manual check (corrections #5, fasit §10.0).
|
||||
|
||||
### Tests to write
|
||||
|
||||
| Type | File | Verifies | Model test |
|
||||
|------|------|----------|------------|
|
||||
| Unit | `render/__tests__/weasyprint-degradation.test.mjs` | missing weasyprint → skip-signal, not throw | `state-updater.test.mjs` |
|
||||
| Unit | `render/__tests__/build-html.test.mjs` | tables, `#`–`####`, inline code | `state-updater.test.mjs` |
|
||||
| Unit | `render/__tests__/build-linkedin.test.mjs` | reads edition-config; config diff → output diff | `csv-parser.test.ts` (file fixture) |
|
||||
| Lint | `agents/__tests__/fact-checker-fixture.test.mjs` | fixture has 3 cases, one each 🟢/🔴/🟡 | `state-updater.test.mjs` |
|
||||
| Lint | `agents/__tests__/persona-reviewer-fixture.test.mjs` | persona def + 6 axes + both modes | `state-updater.test.mjs` |
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| Priority | Risk | Location | Impact | Mitigation |
|
||||
|----------|------|----------|--------|------------|
|
||||
| High | Dead-link blast radius (N1) — `publish` has 21 refs incl. 9 in hook scripts emitting runtime guidance | `session-start.mjs:253,258,333,336,383`, `posting-reminder.mjs:94,95`, `user-prompt-context.mjs:46` | Ships text pointing at a removed command | Step 17 grep target set incl. `agents/README.md` + `CLAUDE.md`; recompile hooks; treat every merge as Medium |
|
||||
| High | Parallel Task fan-out (fasit assumption 4) has NO existing precedent to copy | `commands/newsletter.md` Step 2 | If it degrades, the whole research phase falls back to guessing | Step 8 = highest-uncertainty checkpoint; escalate on degrade, don't paper over |
|
||||
| Medium | weasyprint hard-fails; degradation not implemented (N/3) | `build-pdf.mjs:339`, `build-carousel.mjs:262` | PDF steps abort on machines without weasyprint | Write degradation in Step 1; force-PATH-miss test |
|
||||
| Medium | No OFL license file at source (correction #2) | `render/OFL.txt` | License-compliance gap redistributing OFL fonts | Author OFL-1.1 text in Step 1 |
|
||||
| Medium | Skill catalogs duplicated across 6 dirs (N2) | `skills/*/SKILL.md` | Stale catalogs ship | Steps 12, 21 sweep all 6 |
|
||||
| Low | Font weight 1.5 MB (R3) | `render/fonts/` | Plugin size | Acceptable; OFL attached |
|
||||
| Low | Cross-repo render migration (R1) | maskinrommet | Out of plugin scope | Explicit operator instruction before any maskinrommet write |
|
||||
| Low | Opus cost on many parallel agent calls (R5) | Steps 10, 14 | Cost | Expected/accepted; escalate if volume spikes |
|
||||
|
||||
## Assumptions
|
||||
|
||||
| # | Assumption | Why unverifiable | Impact if wrong |
|
||||
|---|-----------|-----------------|-----------------|
|
||||
| 1 | Foreground Task fan-out from a command keeps the Task tool (vs background agents losing it) | Pure runtime behavior; no static precedent in repo | Step 8/10 research + sweeps degrade to sequential/guessing |
|
||||
| 2 | `${CLAUDE_PLUGIN_ROOT}` resolves in command Bash | Proven at runtime in existing commands, but env-injected | Render/script invocations fail to resolve |
|
||||
| 3 | Retired agents' function is fully covered by existing scripts | personalization-score.mjs + state-updater exist, but coverage parity is judgment | A capability silently lost in Step 7 |
|
||||
| 4 | maskinrommet write for dogfood (Step 14) gets explicit operator OK | Cross-repo; operator-gated | Dogfood blocked or done against a fixture only |
|
||||
|
||||
## Verification
|
||||
|
||||
End-to-end / cross-step checks (per-step Manifests run automatically during execution):
|
||||
|
||||
- [ ] `ls render/ && ls render/fonts/*.ttf | wc -l && node --test 'render/__tests__/*.test.mjs'` → 4 scripts + 8 fonts + OFL.txt; all render tests pass
|
||||
- [ ] **Antakelse 2 (fonts resolve via `__dirname`, not fallback):** `cd /tmp && node <plugin>/render/build-pdf.mjs <sample.md>` produces a PDF embedding Newsreader/Inter (inspect PDF metadata / visual) — `[OPERATØR]` visual confirm
|
||||
- [ ] `node --test 'agents/__tests__/*.test.mjs'` → fixture-lint tests pass
|
||||
- [ ] `test "$(ls commands/*.md | wc -l | tr -d ' ')" = "24"` → command count is exactly 24 (27 − 5 removed + 2 added)
|
||||
- [ ] `ls agents/*.md | grep -v README | wc -l` → **14** (content-tracker, personalization-scorer, performance-reporter, comment-strategist removed = −4; fact-checker, persona-reviewer added = +2; net 16 − 4 + 2 = 14). **Fix v2.0 doc-pass:** the original "16" was the pre-S20 figure carried over by mistake. **Correction:** S14 moved `agents/README.md` into the per-agent files, so `agents/README.md` no longer exists; the `grep -v README` filter is a no-op but harmless.
|
||||
- [ ] `grep -rn '1\.2\.0' .claude-plugin/plugin.json CLAUDE.md README.md CHANGELOG.md` → zero matches (all bumped to 2.0.0)
|
||||
- [ ] `grep -rEn '/linkedin:(templates|publish|authority|collab|speaking)\b|commands/(templates|publish|authority|collab|speaking)\.md|`:(templates|publish|authority|collab|speaking)`' commands/ agents/ skills/ hooks/ README.md CLAUDE.md` → zero stray route-refs to removed commands. **Fix v2.0 doc-pass:** original target set included `agents/README.md` (file removed in S14); extended to also match the shorthand backtick form (`` `:templates` ``, etc.) used in the pillar/skill tables — Step 21 doc-pass uncovered three live shorthand refs in `README.md:294` that the original grep would have missed.
|
||||
- [ ] `python3 hooks/scripts/compile-hooks.py --check` → clean (no drift after hook-ref edits)
|
||||
- [ ] `[OPERATØR]` one real edition produced end-to-end with persona-sweep before lock, reviewed in browser
|
||||
- [ ] All 3 doc levels (plugin README + plugin CLAUDE + root README) reflect v2.0.0
|
||||
|
||||
## Estimated Scope
|
||||
|
||||
- **Files to create:** ~13 (newsletter.md, outreach.md, fact-checker.md, persona-reviewer.md, personas.template.md, edition-state.template.json, longform-quality-rules.md, OFL.txt, 4 render scripts under render/ + fonts/, plus test/fixture files)
|
||||
- **Files to modify:** ~15 (multiplatform, linkedin router, quick, calendar, strategy, audit, analyze, import, profile, analytics-interpreter, engagement-coach, 6 skills, agents/README, CLAUDE.md, README.md, plugin.json, CHANGELOG, hook scripts, marketplace docs)
|
||||
- **Files to remove:** **9** = 5 commands (`templates`, `publish`, `authority`, `collab`, `speaking`) + 4 agents (`content-tracker`, `personalization-scorer`, `performance-reporter`, `comment-strategist`). **Fix v2.0 doc-pass:** original "7" was a stale count from an earlier draft that listed only one of the two outreach precursors. The 5 + 4 arithmetic is binding.
|
||||
- **Complexity:** high (21 sessions, multi-session resumption, cross-repo touchpoint, runtime-assumption checkpoint)
|
||||
|
||||
## Execution Strategy
|
||||
|
||||
> **Execution is strictly ONE step per session, run sequentially via
|
||||
> `/trekexecute --step N --project docs/voyage-build`** (subscription; never
|
||||
> `--fg`, which runs all 21 steps in a single session; never parallel
|
||||
> `claude -p`, API billing). `/trekcontinue` advances exactly one session
|
||||
> (= one step) at a time. Each session is a self-contained ≤35 %-context
|
||||
> deliverable that MUST complete within its own context window; `/clear`
|
||||
> between sessions. The 21 sessions below map **1:1** to the 21 steps. Waves
|
||||
> are dependency groupings, **not** parallelism licenses. **Continuity handoff
|
||||
> is via `STATE.md`** — `NEXT-SESSION-PROMPT.local.md` is deprecated per the
|
||||
> global continuity system (STATE.md + MEMORY.md + CLAUDE.md). trekexecute may
|
||||
> still auto-write that file; treat it as ignorable noise, never as the handoff.
|
||||
|
||||
### Session 1: S1 — Migrate render scripts + fonts into the plugin
|
||||
- **Step:** 1 · **Wave:** 1 · **Depends on:** none
|
||||
|
||||
### Session 2: S1a — Generalize the annotation renderer (build-html.mjs)
|
||||
- **Step:** 2 · **Wave:** 1 · **Depends on:** Session 1 (render present)
|
||||
|
||||
### Session 3: S2 — Generalize build-linkedin.mjs to read edition-config.json
|
||||
- **Step:** 3 · **Wave:** 1 · **Depends on:** Session 1 (render present)
|
||||
|
||||
### Session 4: S3 — Persona library (config/personas.template.md)
|
||||
- **Step:** 4 · **Wave:** 1 · **Depends on:** none (internal)
|
||||
|
||||
### Session 5: S4 — fact-checker agent (agents/fact-checker.md)
|
||||
- **Step:** 5 · **Wave:** 1 · **Depends on:** none (internal)
|
||||
|
||||
### Session 6: S5 — persona-reviewer agent (agents/persona-reviewer.md, 2 modes)
|
||||
- **Step:** 6 · **Wave:** 1 · **Depends on:** Session 4 (personas)
|
||||
|
||||
### Session 7: S6 — Edition-state schema + retire content-tracker & personalization-scorer
|
||||
- **Step:** 7 · **Wave:** 1 · **Depends on:** none (internal)
|
||||
|
||||
### Session 8: S7 — newsletter.md skeleton, Step 0–2 (load, calibrate, research fan-out)
|
||||
- **Step:** 8 · **Wave:** 2 · **Depends on:** Wave 1 complete (agents, personas, render, edition-state)
|
||||
|
||||
### Session 9: S8 — newsletter.md Step 3–4 (draft + consistency/quality)
|
||||
- **Step:** 9 · **Wave:** 2 · **Depends on:** Session 8 (newsletter.md, strict order)
|
||||
|
||||
### Session 10: S9 — newsletter.md Step 5–6 (fact-check sweep + persona sweep BEFORE lock)
|
||||
- **Step:** 10 · **Wave:** 2 · **Depends on:** Session 9
|
||||
|
||||
### Session 11: S10 — newsletter.md Step 7–10 (annotate, lock/delivery, hook-gate, schedule)
|
||||
- **Step:** 11 · **Wave:** 2 · **Depends on:** Session 10
|
||||
|
||||
### Session 12: S11 — Reconcile newsletter path out of multiplatform + skill trigger + router row
|
||||
- **Step:** 12 · **Wave:** 2 · **Depends on:** Session 11
|
||||
|
||||
### Session 13: S12 — longform-quality-rules.md + resumption wiring
|
||||
- **Step:** 13 · **Wave:** 2 · **Depends on:** Session 11 (rules inlined in newsletter.md at Session 9; extracted here)
|
||||
|
||||
### Session 14: S13 — Dogfood: produce a real edition end-to-end `[OPERATØR]`
|
||||
- **Step:** 14 · **Wave:** 3 · **Depends on:** Wave 2 complete (full pipeline) + Wave 1 render
|
||||
|
||||
### Session 15: S14 — Fix dogfood friction `[OPERATØR]`
|
||||
- **Step:** 15 · **Wave:** 3 · **Depends on:** Session 14 (friction log)
|
||||
|
||||
### Session 16: S15 — templates.md → mode in quick.md
|
||||
- **Step:** 16 · **Wave:** 4 · **Depends on:** Wave 1 (independent of Wave 2–3)
|
||||
|
||||
### Session 17: S16 — publish.md → action in calendar.md
|
||||
- **Step:** 17 · **Wave:** 4 · **Depends on:** Wave 1
|
||||
|
||||
### Session 18: S17 — collab.md + speaking.md → new outreach.md
|
||||
- **Step:** 18 · **Wave:** 4 · **Depends on:** Wave 1
|
||||
|
||||
### Session 19: S18 — authority.md → strategy.md + trajectory dedup + profile canon
|
||||
- **Step:** 19 · **Wave:** 4 · **Depends on:** Wave 1
|
||||
|
||||
### Session 20: S19 — Agent merges: analytics (2→1) + engagement (2→1)
|
||||
- **Step:** 20 · **Wave:** 4 · **Depends on:** Wave 1
|
||||
|
||||
### Session 21: S20 — import.md trim + router gating + final doc pass → v2.0.0
|
||||
- **Step:** 21 · **Wave:** 4 · **Depends on:** ALL prior sessions (closes v2.0.0 — always last overall)
|
||||
|
||||
### Wave scope fences (reference)
|
||||
|
||||
Scope fences are defined per wave; each session inherits its wave's fence.
|
||||
|
||||
- **Wave 1 (Sessions 1–7):** Touch `render/`, `config/personas.template.md`, `config/edition-state.template.json`, `agents/fact-checker.md`, `agents/persona-reviewer.md`, `agents/fixtures/`, `agents/__tests__/`, remove content-tracker + personalization-scorer, `agents/README.md`, `CLAUDE.md` (agent table). Never touch `commands/newsletter.md` (Wave 2), any consolidation target (Wave 4).
|
||||
- **Wave 2 (Sessions 8–13):** Touch `commands/newsletter.md`, `commands/multiplatform.md`, `commands/linkedin.md`, `skills/*/SKILL.md`, `references/longform-quality-rules.md`. Never touch render scripts (frozen after Wave 1), consolidation targets (Wave 4).
|
||||
- **Wave 3 (Sessions 14–15):** Touch a serie-mappe (maskinrommet — operator-gated) or a `docs/review/` fixture; friction log; whichever pipeline files S14 fixes name. Never touch maskinrommet without explicit operator instruction (R1).
|
||||
- **Wave 4 (Sessions 16–21):** Touch consolidation targets (quick, calendar, outreach, strategy, audit, analyze, import, profile, linkedin router), `agents/analytics-interpreter.md`, `agents/engagement-coach.md`, removed files, all doc levels, version refs, hook scripts (publish refs). Never touch `commands/newsletter.md` internals (frozen after Wave 2).
|
||||
|
||||
### Execution Order
|
||||
|
||||
Run sessions **1 → 21 in numeric order**, one per `/trekcontinue` (or
|
||||
`/trekexecute --step N`). Wave boundaries are dependency gates: do not begin a
|
||||
Wave-2 session before Wave 1 is complete; Session 21 is always last (closes
|
||||
v2.0.0). Wave 4 (Sessions 16–21) is independent of Waves 2–3 and may run any
|
||||
time after Wave 1, but the canonical order is sequential 1→21.
|
||||
|
||||
### Grouping rules applied
|
||||
|
||||
- One step per session — each is a full ≤35 %-context deliverable that completes within its own context window.
|
||||
- Steps sharing files are adjacent and strictly ordered (newsletter Sessions 8–11 all touch `newsletter.md`).
|
||||
- Render (Sessions 1–3) frozen before the newsletter command consumes it.
|
||||
- Consolidation (Wave 4) isolated from langform files to avoid cross-contamination.
|
||||
|
||||
## Plan Quality Score
|
||||
|
||||
| Dimension | Weight | Score | Notes |
|
||||
|-----------|--------|-------|-------|
|
||||
| Structural integrity | 0.15 | 95 | 21 steps, dependency-ordered, waves match fasit phases |
|
||||
| Step quality | 0.20 | 92 | each step has Files/Changes/Reuses/Test-first/Verify/On-failure/Checkpoint/Manifest; some Verify cmds approximate (agent gates) |
|
||||
| Coverage completeness | 0.20 | 95 | every fasit session S1–S20 (+S1a) mapped; all decisions A–H realized |
|
||||
| Specification quality | 0.15 | 90 | concrete paths + reuse refs; a few `[OPERATØR]`/`[GATE]` steps are intentionally non-mechanical |
|
||||
| Risk & pre-mortem | 0.15 | 92 | R1–R5 + N1–N3 + 4 assumptions; highest-uncertainty checkpoint flagged |
|
||||
| Headless readiness | 0.10 | 90 | On-failure + Checkpoint per step; multi-session resumption via project dir |
|
||||
| Manifest quality | 0.05 | 85 | every step has a real predicate after revision; consolidation deletions bind to Verify (schema is positive-match only — acknowledged) |
|
||||
| **Weighted total** | **1.00** | **90** | **Grade: A** |
|
||||
|
||||
**Adversarial review:**
|
||||
- **Plan critic:** REPLAN → revised. 3 blockers + 6 major + 5 minor found; all blockers + 5/6 major + 4/5 minor addressed (see Revisions). The one major not "fixed" (M2: Manifest can't encode deletions) is an acknowledged schema limitation — bound to the Verify gate instead.
|
||||
- **Scope guardian:** ALIGNED. 0 scope-creep; all S1–S20 (+S1a) mapped 1:1; decisions A–H realized, none re-litigated; maskinrommet read-only/operator-gated; decision B (no short-form extension) honored. 2 gaps + 1 dependency issue — all addressed in Revisions.
|
||||
|
||||
## Revisions
|
||||
|
||||
| # | Finding | Severity | Resolution |
|
||||
|---|---------|----------|------------|
|
||||
| 1 | Step 21 command-count predicate self-contradictory (22/23/"verify net"); correct net is 24 | blocker | Verify rewritten to exact string-equality `= "24"`; Changes states the binding number; "~23" noted as approximate. Manifest adds CLAUDE.md + README.md `2.0.0` checks |
|
||||
| 2 | Step 2 Manifest (string `export`/`import.meta.url`) doesn't prove the table/heading/inline-code generalization (a no-op passes) | blocker | Manifest `must_contain` now greps the production renderer for `<table>`, `<h4`, `<code>` — output markers a no-op cannot emit |
|
||||
| 3 | Steps 14, 15 empty Manifests + Step 9 single-string Manifest = rubber stamps | blocker | Step 14 now requires `docs/voyage-build/dogfood-S13-friction.md` (with order-proof); Step 15 requires the log updated with ✅/🔶 per item; Step 9 Manifest adds `Step 3`/`Step 4`/`AI-slop` |
|
||||
| 4 | Newsletter Steps 8–11 hide 2–4 phases behind single-string Manifests; order assertions only in grep | major | Each Manifest now requires all phase `Step N` headings present; order (sweep-before-lock, hook-after-lock) bound to Verify grep + `[GATE]` with explicit notes |
|
||||
| 5 | Step 7 deletions + capability-parity not verified by Manifest | major | Acknowledged schema limit (positive-match only); Verify `! test -f` + dead-link grep made the binding predicate; archetype-F note added |
|
||||
| 6 | Step 9 forward-references `longform-quality-rules.md` created in Step 13 | major | Step 9 now inlines the §8 rules in newsletter.md; Step 13 extracts them to the reference file + leaves a pointer. No dangling reference at any point |
|
||||
| 7 | Step 15 On-failure ("revert the specific fix") had no referent | major | On-failure now reverts using the file path recorded against each friction item in the S13 log; escalate if unnamed. Steps 14–15 marked `[OPERATØR]`-gated (not pure headless) |
|
||||
| 8 | Step 8 fan-out Manifest only checked the command name | major | Manifest adds `Step 0`/`Step 2`/`parallel`; runtime fan-out behavior remains the `[GATE]` + escalate On-failure (cannot be static) |
|
||||
| 9 | Step 12 grep depended on exact pointer wording `see /linkedin:newsletter` | major | Verify rewritten to assert no multi-step newsletter *section* survives (`Step.*newsletter` count = 0); a one-line pointer of any wording is allowed |
|
||||
| 10 | build-linkedin constants cited "32–50"; actual 34–50 | minor | Citations corrected to 34–50 with per-constant line refs (CALENDAR:34, FRESHNESS:44, COVER_CREDIT:49, CAPTIONS:50) |
|
||||
| 11 | Step 1 `min_file_count: 6` undercounts; fonts absent from Manifest | minor | Added `render/fonts/Inter-400.ttf` + `render/fonts/Newsreader-400.ttf` to expected_paths; `min_file_count: 8`; Verify asserts 8 .ttf; build-carousel weasyprint added to must_contain |
|
||||
| 12 | Step 6 Verify `grep -ci 'modus\|mode'` — `\|` not portable on BSD grep (darwin) | minor | Rewritten to `grep -Eci 'resonans\|konverter'` (BSD-safe `-E`) |
|
||||
| 13 | Scope gap: antakelse 2 (PDF fonts resolve via `__dirname`, not fallback) not asserted | minor | Added to end-to-end Verification as an `[OPERATØR]` PDF-metadata check |
|
||||
Loading…
Add table
Add a link
Reference in a new issue