linkedin-studio/docs/m0/log.md

162 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# M0 — Implementation Log
Running record of decisions, deviations, and out-of-scope follow-ups discovered
during M0 execution. Plan: `docs/m0/plan.md` (18 steps). History → git; this file
captures only what the commit messages cannot.
## Session 4 — Steps 1418 (2026-06-18)
### Step 14 GATE outcome — the D3 convention works; edit count is ~1:1, not reduced
Prototyped `references/data-path-convention.md` on the voice-readers family:
**38 refs across 19 files repointed** — exactly the plan's prediction. The measured
answer to brief D3's open question (*can a convention reduce edits, or do commands
need literal paths for Claude to act on?*): command/agent prose that tells Claude to
**read** a file needs a resolvable path **on the line**. A "the data dir's
`voice-samples/` (see convention doc)" reference adds a lookup hop and is not directly
actionable. The inline `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/…`
token is both self-resolving **and** points at the doc. So the convention does **not**
cut edit count below ~1 per ref — it makes every edit a **uniform mechanical token
swap** (vs bespoke per-line decisions), with the doc as single source of truth.
**GATE = proceed** (convention confirmed working); Step 15 applies the same uniform
token to the remaining families. This is D3 realized as one token — NOT a re-decision
to literal-edit (alt. a). Applied via an ordered swap (prefixed `${CLAUDE_PLUGIN_ROOT}/
assets/voice-samples/` form before bare `assets/voice-samples/`, so the bare pass can't
corrupt the prefixed one).
### Designed inter-step red lint (Step 14 → Step 16)
After Step 14 the structure lint is **Failed: 1**`references/*.md: 26 (expected 25)`,
the new convention doc as the 26th ref file. `EXPECT_REFS` bumps to 26 in **Step 16**
(plan Session-4 scope forbids touching tests). This session lands 14→18 in one go, so
the lint is restored to green at Step 16 — no red is left at session end. The only
surviving bare `assets/voice-samples/` is inside the convention doc itself (it documents
the in-plugin placeholder-scaffold location for the fallback rule) — Step 16's
no-bare-path assertion must exempt `references/data-path-convention.md`.
### Step 15 — 130 refs / 34 files repointed; shipped read-only preserved
Rule-based repoint (negative-lookahead per subdir): analytics 50, drafts 24,
audience-insights 14, profile/D1 12, examples 11, plans 6, my-post-templates 4,
frameworks 3, case-studies 3, network 2, repurposing-tracker 1. Counts match the
plan's family estimates once the **shipped exclusions** are accounted for
(frameworks 6→3 drops `framework-template.md`; case-studies 5→3 drops
`case-study-template.md`; analytics drops `README.md`; the Step-11 `ANALYTICS_ROOT`
pins were already gone). **Style-A `${CLAUDE_PLUGIN_ROOT}` preserved** for shipped
read-only: `analytics/README.md`, all `*-template.md` seeds, `assets/checklists/`,
`assets/quick-post-resources.md`, the shipped `assets/templates/*` (only the
`my-post-templates.md` *instance* repointed), `config/*.template.*`, and every
`scripts/analytics` / `hooks/scripts` CODE path. **profile/D1:** `config/user-profile.local.md`
`${…}/profile/user-profile.md` (path + filename change, drops `.local`, per
MOVE_FILES). **ab-tests/** routes under `${…}/analytics/ab-tests/` (brief §7.1);
**plans/** at `${…}/plans/` (top-level). Verify: the only in-plugin data-dir paths
left are the three shipped exclusions; full lint Failed:1 = EXPECT_REFS only.
### Step 15 scope notes — two additions, one deferral
- **network/ + repurposing-tracker.md repointed (additions beyond the plan's named
families).** Both are **code-invisible data classes** the agents *write*
(`network-builder.md`, `content-repurposer.md` save trackers into the plugin tree).
They fit Step 15's stated goal ("route the code-invisible data classes so they don't
silently orphan when the default flips"); the plan named ab-tests/plans as examples,
not an exhaustive list. As **write-targets** they are self-sufficient — the agent
creates the file external on first write, no migration entry needed.
- **`config/personas.local.md` deferred — OUT of M0 scope.** Unlike the write-targets,
this is a **read fallback** in a resolution chain (edition-state → series file →
plugin `personas.local.md` → template). Repointing the read external without a
migration dest would break the read, and adding it to `MOVE_FILES` is a `.mjs` change
Step 15's scope fence forbids. Personas are newsletter/series production data (already
external via `$LTL_SERIES_ROOT`); the plugin-level `personas.local.md` is a deliberate
in-plugin fallback library. Left as-is; Step 16's no-bare-path lint targets the
*migrated* data classes only, so it does not flag personas. Track as a follow-up if a
full personas externalization is wanted later.
### Step 16 — lint extended 74→81 checks; the no-pin guard caught 5 real survivors
Added to `test-runner.sh`: `EXPECT_REFS` 25→26 + "delta = exactly +1 (data-path-convention.md)"
assert (m3/m11); **Section 13** (no command/agent/skill/hook prose references a migrated
user-data path in-plugin — bare or `${CLAUDE_PLUGIN_ROOT}`-pinned — R1; + the no-`ANALYTICS_ROOT`-
pin sibling M3; + the SC2 `git status --porcelain --ignored` dry-run), each with a
non-vacuity self-test mirroring Section 8; **Section 14** assertion-count anti-erosion
floor (pre-M0 baseline 74, count may only grow — SC6 / assumption 3).
**The no-pin guard earned its keep on first run:** it flagged **5 surviving in-plugin
`ANALYTICS_ROOT="${CLAUDE_PLUGIN_ROOT}/assets/analytics"` pins** that Step 11 missed —
`agents/analytics-interpreter.md` (×2) + `skills/linkedin-analytics/SKILL.md` (×3). Step 11
scoped the pin-drop to the *commands* (`import.md`/`report.md`); the agent + skill invoke the
CLI directly and kept the pin, which would force the analytics CLI to read in-plugin even
after the default flipped external (the exact M3 failure). Dropped all 5 (surgical: removed
only the `ANALYTICS_ROOT=… ` prefix, kept the `node --import tsx` form — no CLI-style change),
matching how Step 11 left the commands. Lint now **81/0**.
### Step 18 — release 0.4.0 → 0.5.0 (minor, non-breaking)
Bumped `.claude-plugin/plugin.json` + the three lint-grepped literals (README
`version-0.5.0-blue` badge, CLAUDE.md `(v0.5.0)` header, CHANGELOG `## [0.5.0]`).
Also synced the README `reference_docs-25→26` badge (not lint-checked, but stale after
the convention doc) and refreshed the now-false "user data still lives inside the plugin"
prose in README + CLAUDE.md to "M0 done — data external." Version-consistency lint green;
the only remaining `0.4.0` refs are CHANGELOG history + the `docs/m0/` planning docs that
describe the `0.4.0 → 0.5.0` transition itself (correct, not stale declarations).
**Plan deviation — root marketplace docs NOT touched (scope).** Plan Step 18 listed
`/Users/ktg/.claude/plugins/marketplaces/ktg-plugin-marketplace/{README,CLAUDE}.md` as
"root docs." Skipped: that path is the **installed** marketplace artifact, **outside this
polyrepo**, and STATE.md scope is "KUN denne pluginnen" (+ global "don't write in other
repos"). The binding gate (the lint) checks only this plugin's four declarations, all
green. **Follow-up (separate catalog repo task):** reflect linkedin-studio v0.5.0 in the
marketplace catalog README if/when desired — operator's call, not this plugin's M0.
## Session 3 — Steps 1113 (2026-06-18)
### Environment reality vs. plan assumptions
The plan was authored assuming the operator's **real `.local.md` runtime data**
sat in the plugin tree (227-line voice profile, analytics exports, draft queue).
On this machine that data is **absent** — it is a clean clone:
- `assets/voice-samples/`: only the PII-free placeholder `authentic-voice-samples.md`
(+ `.template.md`). No `.local.md` source.
- `assets/drafts/`: only `.gitkeep`. `assets/analytics/`: only `README.md` + empty `ab-tests/`.
- The 4 tracked D2 scaffold instances DO exist (`high-engagement-posts.md`,
`demographics.md`, `engagement-patterns.md`, `my-post-templates.md`).
**Consequence for Step 12 (live migration):** every `MOVE_FILES` / `MOVE_DIRS`
entry resolved to an absent source → clean no-op. Only the 4 `COPY_FILES`
scaffolds were relocated. Result: `migrated — moved 0, copied 4, skipped 0`;
`.migrated` marker written; idempotent re-run confirmed `already-migrated`.
**Step 12 verify adapted:** the plan's literal check
(`test -f .../voice-samples/authentic-voice-samples.md`) cannot pass without a
`.local.md` source to move, so it was replaced with the achievable post-condition:
`.migrated` marker present + 4 scaffolds external + `migrateData` wired into
`session-start.mjs` + idempotency. The voice MOVE correctly no-op'd. (Operator
pre-approved this adaptation before the run.)
### OUT OF M0 SCOPE — git-history scrub of the leaked post (FOLLOW-UP)
`assets/examples/high-engagement-posts.md` held the operator's **real** LinkedIn
post at HEAD (the "Ralph Wiggum / vibe-coding" post — real names, real engagement
metrics, real personal narrative). Step 13 scrubbed the **working-tree** content
to a generic placeholder (0 `## Post N` sections → personalization score 0, no
PII). **This does not remove the post from git history.** A history rewrite
(`git filter-repo` / BFG) on `assets/examples/high-engagement-posts.md` is a
separate, explicit operation — **deferred, not done here** (brief §13: out of M0
scope). Track until the repo is published.
### Note — external instance retains pre-scrub content (by design)
The Step-12 migration copied `high-engagement-posts.md` to
`~/.claude/linkedin-studio/examples/high-engagement-posts.md` **before** Step 13
scrubbed the in-plugin file (B3 ordering). The external copy therefore still holds
the migrated content. That is the operator's private data dir (outside any repo) —
the operator may curate it; not an M0 concern.
### D2 scaffolds completed
All 6 scaffolds now have a read-only `*-template.*` seed: `case-study-template.md`
and `framework-template.md` already shipped; Step 13 added the 4 missing ones
(`high-engagement-posts-template.md`, `demographics-template.md`,
`engagement-patterns-template.md`, `my-post-templates-template.md`). The 3
already-generic instances seeded their templates verbatim; the high-engagement
template is a freshly-authored generic seed (the old instance was the leak).