Splits the old S3d charter: S3d = ops centre ONLY (operations.md becomes a
read tributary; strategy-advisor honours the dated frozen-past-self anchor
that deprecates older inferences — advisory/reader-side). The hygiene +
triple-post reconciliation half (dead content-history.md retirement +
post-tracking↔published↔analytics) moves to a new SB-S3e (the last S3 slice).
Light-Voyage hardening: scope-guardian ALIGNED (0 creep/gap); brief-reviewer
REVISE (5 FIX folded); plan-critic 3 major + 3 minor folded. Baselines
verified live: gate 95→97, brain 113→114, ASSERT floor 80→82. The RED-bearing
brain-test literal is pinned to the dated-anchor convention (_As of YYYY-MM-DD:_,
verified absent) — NOT 'deprecates older inferences', which already ships in
the seed comment and would make the RED gate vacuous.
Go-before-code gate pending (forks: Option B enrich seed · advisory deprecation
· one reader · doc home).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Hub-side design: the published record now carries the specifics/trends ids
it was built from (additive, omit-empty → byte-backward-compatible), and a
new pure assembler (scripts/brain/src/assemble.ts + `brain assemble`) joins
post↔analytics by normalized title-prefix + date with honest confidence
tiers (high/low/none). Answers the arc's north-star query: which raw
material actually performs? (specific → post → measured analytics).
All four tributaries untouched (analytics READ-only via inlined raw-JSON,
no package import); profile.md grammar untouched (the fact→post link stays
OUT — C-1). The repeatable --specific/--trend ingest flags collect via a
new collectRepeated helper, leaving parseFlags untouched.
TDD: 19 new brain tests (ingest 4 + publish 3 + assemble 8 + cli 4), all
SC1–SC12. brain 113/113, gate 95/0/0, BRAIN_TESTS_FLOOR 94→113,
ASSERT_BASELINE_FLOOR unchanged at 80. Light-Voyage hardened
(brief-review 5 FIX · plan-critic 1 BLOCK+4 MAJOR+4 MINOR · scope-guardian ALIGNED).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Hub-side design: thread specifics/trends ids onto the brain's published
record + a pure analytics resolver (title-prefix + date, confidence tiers).
Tributaries untouched; blast radius bounded to scripts/brain/. Answers the
north-star query "which raw material performs?" (specific -> post -> analytics).
Two hard constraints shaped the slice: profile.md's rigid 6-token grammar
keeps the fact->post link OUT (C-1, breaking SCHEMA_VERSION bump); analytics
has no body/URN so the analytics<->post join is a resolver, not a stored id (C-2).
Light-Voyage: brief-review APPROVE-WITH-FIXES (5 FIX), plan-critic
REVISE -> 1 BLOCK + 4 MAJOR + 4 MINOR folded, scope-guardian ALIGNED.
Awaiting operator go-before-code gate before any TDD.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
The engine can now RETIRE a fact via an operator-gated, explicitly-signalled
temporal-update supersede — completing the consolidation motor's one S2 TODO
("no supersede in S2 — that's S3"). A `supersedes` signal on a candidate retires
the stale fact (re-minted to an archival id, status: superseded, REPLACED IN
PLACE, retained as audit) and installs the new winner under the canonical key-id,
so mintEntityId(key) always points at the live fact.
- consolidate.ts: Candidate.supersedes? + ProfileDiff.supersedes (SupersedeOp
carries the full winner fact, so applyDiff stays a pure projector). proposeDiff
value-guarded routing fork (routes only when the target holds a DIFFERENT value —
else a re-sent signal would self-supersede every run) + intra-batch
first-supersede-wins guard. applyDiff replace-in-place + value-matched state-check
(oldId present, active, value===oldValue) → idempotent re-apply + stale-diff safe;
superseded facts never bumped/promoted (supersede wins). Decay excludes superseded.
archivalId seeded with the pre-archival id (collision-free).
- cli.ts: renderDiffMd `## Supersessions (old → new)` (rendered last, only when
present → zero-supersession diffs stay byte-identical); validateCandidates optional
single-line `supersedes`; `--gather` profileFacts filtered to active (superseded
archival facts never re-presented as live context).
- Tests: +12 brain (consolidate 10 + consolidate-cli 2). TDD: RED (6 fail) → GREEN
(94/94). BRAIN_TESTS_FLOOR 82->94; ASSERT_BASELINE_FLOOR unchanged at 80 (no new
test-runner.sh section). Gate 95/0/0.
- Docs: consolidation-loop.md rule table + honest-limit reconciled (the operator
gate is the only classification net); engine docstring updated.
All 13 success criteria deterministically tested (unlike S3a, no behavioural-only
SC). READ-only gate unchanged — brain consolidate --apply --confirm stays the sole
profile.md writer. Scope held: scripts/brain/ only; temporal-update only
(condition-dependent/distractor deferred).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Second sub-slice of SB-S3 (after S3a's reader landed). Adds an operator-gated,
explicitly-signalled supersede op to the consolidation engine: a temporal-update
candidate retires a stale fact (status: superseded, re-minted to an archival id,
replaced in place, retained as audit) and installs the new winner under the
canonical key-id. Bounded to scripts/brain/ — pure, deterministic, fully
unit-testable.
Operator-confirmed scope: temporal-update→supersede ONLY · winner→dynamic
(re-earn) · minimal `supersedes?: string` · retain superseded as audit.
Light-Voyage-hardened:
- brief-reviewer APPROVE-WITH-FIXES — 8 FIX folded (the load-bearing one: id
lifecycle → winner-takes-key-id, retired fact re-minted, so mintEntityId(key)
always points at the live fact).
- plan-critic REVISE → 2 BLOCK + 6 FIX folded: applyDiff must REPLACE-IN-PLACE
(not copy → would dup the canonical key-id); proposeDiff must value-guard the
supersede fork (a re-sent stale signal would self-supersede every run); plus
--gather active-only filter + decay excludes superseded (else retired facts
leak back as live context / spam stale flags).
- scope-guardian ALIGNED (0 creep / 0 gap).
Also reconciles brief-sb-s3a.md's stale DRAFT status (S3a landed 4fa411f).
No code yet — awaiting go-before-code.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
The second brain now feeds generation: strategy-advisor reads brain/profile.md
as evidence-to-test (anti-sycophancy default, graceful absence on fresh installs).
First end-to-end proof of capture → consolidate → read-back-into-generation.
- agents/strategy-advisor.md: brain/profile.md added to Step 0 Load Context + a
consumption subsection (Static/Dynamic layers, evidence_count/last_seen weighting,
anti-sycophancy counter-pressure, silent degrade when the file is absent).
- scripts/test-runner.sh: new Section 16d (Brain Profile Reader) — 2 UNCONDITIONAL
checks (non-vacuity self-test with legacy-path/lowercase decoy + exact-literal
wiring grep on strategy-advisor.md), ASSERT_BASELINE_FLOOR 78→80, header
enumeration extended. Gate 93→95/0/0; brain floor 82 untouched.
- docs/second-brain/consolidation-loop.md: reconciled the stale "no reader yet"
honest-limit to the true partial state (one reader; broader consumption deferred).
TDD: RED (only the wiring grep failed, exit 1) → GREEN (95/0/0). READ-only — the
brain consolidate --apply --confirm gate stays the sole writer. SC4 (graceful
absence) verified by inspection; SC5 (read-back) deferred to a reloaded session
(honest — agent prompts load at session start). S3a scope held: one reader, no
parser, no id-threading.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Bump BRAIN_TESTS_FLOOR to 82 (SB-S2 adds consolidate(12)+consolidate-cli(7)).
No new test-runner section → ASSERT_BASELINE_FLOOR unchanged at 78 (the hook
SC6 test runs separately via `node --test hooks/scripts/__tests__/*.test.mjs`,
not the structure gate). Add docs/second-brain/consolidation-loop.md (CLI usage,
engine rules, the candidate-file session↔engine contract, operator gate, honest
limits incl. no-reader-until-S3). Gate 93/0/0; hook suite 136/0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
session-start.mjs (zero-dep, Edit): unconditional brain/+ingest/ scaffold-ensure
(mkdir, runs on the fresh-install path too); a consolidation-due nudge in the
reminders block (counts published records + reads consolidation-state.json last_run
via getDataRoot — same root the CLI --apply writes; no profile.md parse, \n idiom,
null-safe never-nags); a brain-init nudge appended AFTER the if/else so the
fresh-install branch's context reassignment can't clobber it. + readdirSync import.
SC6 hook test (5 cases) via heredoc. Hook suite 131→136/0; no recompile (body-only edit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Operator-invoked, operator-gated loop:
- --gather reads published bodies directly (parsePublishedRecord, filtered by
published_date > last_run) + current profile, for the session to extract candidates.
- --propose validates candidates (shape + single-line key/value, else non-zero/no-write),
proposeDiff, writes brain/pending-diff.{json,md}; never touches profile.md.
- --apply --diff <json> --confirm is the ONLY path that writes profile.md (refuses
without --confirm), then records brain/consolidation-state.json last_run.
init/ingest/published preserved. 7 subprocess CLI tests (SC5). brain 75→82, tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
voice-trainer's Gather step now reads ingest/published/ (provenance=published) as
the primary gold source, keeps voice-samples as a human tributary (not reshaped),
forbids learning from provenance=ai-draft, and fences the auto-append trap. New gate
Section 16c (Brain Published-Only Invariant) enforces the wiring with exact-literal
greps (grep -F 'ingest/published' + 'provenance=ai-draft') + a non-vacuity self-test
(rejects an 'AI-generated'-only probe), against voice-trainer.md + the new contract
doc docs/second-brain/ingest-manual-import.md. BRAIN_TESTS_FLOOR 34→63; assertion
floor 75→78 (+3 unconditional checks; not pinned to deps-present TOTAL to preserve the
warn-skip margin). Gate 90→93/0/0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Add flag-routing infra (parseFlags) + two subcommands to the brain CLI, keeping
the existing `init` branch intact:
- `ingest --file <path> [--source] [--date]` / `ingest --scan-inbox` → capture
published posts into ingest/published/ (Wrote / Duplicate / Collision report).
- `published list [--json]` → inspect the gold corpus (id · provenance · date · first line).
6 subprocess tests incl. an explicit `init` regression guard. brain 57→63 tests, tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Idempotent, collision-safe, create-on-demand IO under the brain dataRoot:
- writePublished: identical body → no-op; differing body at same id → disambiguated
<id>-N.md (never clobber, never silently drop a gold record, B2 defence-in-depth).
- ingestText: provenance always published; published_date defaults to captured_at.
- scanInbox: top-level *.md only (skips .DS_Store/dotfiles/non-md), non-destructive,
empty/absent inbox = clean no-op, re-scan re-skips via dedup.
- listPublished: parses each file in try/catch (malformed → skipped count, never
crashes), surfaces provenance so ai-draft leakage is eyeball-visible.
9 temp-dir IO tests. brain 48→57 tests, tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Slice SB-S1 (Ingest + gold signal): manual import → ingest/published/ with
provenance=published; wire voice-trainer to learn from published-only (the
model-collapse guard). Brief + plan hardened through light-Voyage:
brief-reviewer (PROCEED_WITH_RISKS), plan-critic (REVISE 63→folded),
scope-guardian (GAP→folded). Key decisions: verbatim-body content hash (no
normalizeContent, avoids silent data loss), one pinned record separator with a
round-trip edge battery, gate-enforced published-only lint, S1/S2 boundary held
(no profile.md mutation in S1). Operator-approved scope; version bump to 0.5.1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
SB-S0 (Foundation) of the second-brain arc: a new TS package scripts/brain/
(structural copy of specifics-bank) establishing the spine later slices hang on.
TDD throughout (failing test first); fold scope = P1+P2 only per operator decision.
- types.ts: one provenance vocab (human|published|ai-draft) + the six-field
ProfileFact record + two-layer ProfileDoc.
- id.ts: slugify + mintEntityId (sha256[:12], kind-namespaced, slug-keyed so the
id is stable across value edits) + normalizeProvenance (throws on unknown). [SC4]
- profile.ts: no-YAML line-grammar parse/serialize (parse∘serialize = identity over
the whole doc; values may contain ]/|/quotes) [SC2] + foldUserProfile: lossless,
idempotent, source-absent-aware fold of config/user-profile.template.md. Pinned
extraction — P1 labeled scalars (group-headers skipped, [placeholder]→empty) + P2
expertise group; stops at "### Research Tooling" so deferred explainer prose can't
leak in as fields. Checkbox-prefs (Goals/Tone/MCPs/Assets) deferred (§8). [SC3]
- dataRoot.ts: inline per-package resolver (repo idiom; no new seam → SC6).
- scaffold.ts/cli.ts: idempotent `brain init` — brain/{index,profile,operations}.md
+ journal/ + ingest/{inbox,published} under the data-dir; compare-then-skip, never
clobbers a user edit. No session-start wiring (SB-S2 owns it). [SC1]
Gate: new BRAIN floor in test-runner.sh (34 tests; trends/specifics/contract floors
unchanged) + anti-erosion floor 74→75. Full gate 90/0/0, tsc --noEmit clean. [SC5/SC6]
No new command/agent/reference → no version bump, no structure-count change.
Refs docs/second-brain/{brief,plan-sb-s0,architecture}.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Light-Voyage process artifacts for the first second-brain slice (SB-S0):
- brief.md — task brief, revised after adversarial brief-review (B1 fold-source
premise, B2 pathguard claim [later verified: STATE was right, reviewer wrong],
M1 floors-as->=, M2 scaffold trigger, M3 grammar contract, M4 runtime-only).
- plan-sb-s0.md — SB-S0 implementation plan, revised after adversarial
plan-review (plan-critic + scope-guardian, both code-verified):
B1 test convention -> real TS idiom (tests/*.test.ts via tsx, .js specifiers);
M2 profile format -> defined line-grammar (repo has no YAML parser, no dep);
M3 data-root -> inline per-package resolver (repo idiom, not a new seam);
M4 fold extraction rule pinned against the actual user-profile template;
+ minors (gate-count rationale, npm-install-before-gate, provenance throws,
stable-slug id, push-confirm).
scope-guardian: zero creep, zero MAJOR gap. plan-critic: B1+3 MAJOR, all folded.
Design phase, no code yet — awaiting operator go to build SB-S0 (TDD).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
Persist the operator-approved second-brain architecture design and the three
parallel research threads that fed it, so nothing is lost before build:
- architecture.md — approved design: thin Markdown `brain/` hub (two-layer
profile.md + episodic journal/ + operations.md + index.md MOC) over the
existing typed tributaries (voice/specifics/trends/analytics), a
provenance-tagged ingest/ seam, a sleep-time consolidation loop with
evidence-threshold promotion + temporal-validity + anti-sycophancy/
anti-collapse invariants. Build sequence SB-S0..S4.
- research/connector-egress.md — LinkedIn data egress reality (EU/EEA DMA
portability API = auto for content; analytics manual CSV; no scraping).
- research/secondbrain-sota.md — 2026 second-brain / AI-memory SOTA synthesis.
- research/silo-inventory.md — faithful inventory of the 12 existing per-user
silos + the 5 hardest unification problems.
Boundary confirmed: engine -> plugin (domain-general), user data -> data dir,
cockpit -> Maskinrommet. Design phase, no code yet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
SessionStart now warns (>=7d, warn-only) when the persistent trend store's
newest capture is stale, firing ONLY when the store already holds captures
(a never-scanned user is never nagged). Neutral wording — "scan for trends"
hits trend-spotter's own trigger; no hardcoded beat (de-niche invariant).
- store.ts: newestCaptureDate() — pure max-capturedAt staleness signal (SSOT)
- cli.ts: status [--json] subcommand (count + newest + daysStale)
- session-start.mjs: trendsNewestCapture() reads trends.json as raw JSON
(no tsx spawn at session start) + the reminder line, beside import-staleness
- tests: +3 store tests (newestCaptureDate) + hook subprocess test (3 cases:
>=7d fires, <7d silent, absent/empty silent + no crash)
- test-runner.sh: trends floor 21->24
Verified: trends 24/24 · all hook tests 131/131 · gate 89/0/0 · real render
confirms "Trend signals are N days old. Scan for trends…".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
The de-niche sweep (B-S1/B-S2) stripped the hardcoded KTG beat
(Microsoft|Azure|Copilot|public sector|offentlig sektor) from three surfaces,
but §17 only locked agents/trend-spotter.md against its return. Generalize the
guard to the full de-niched set so a beat token cannot creep back into the
other two:
- agents/trend-spotter.md (B-S1)
- agents/content-planner.md (B-S2a)
- references/content-framework.md (B-S2a)
Single shared NICHE_TOKENS criterion + non-vacuity/false-positive self-test
(unchanged shape), then a per-file grep loop over the explicit allowlist.
Scoped per file BY DESIGN, not tree-wide: references/content-angles.md keeps
"Public Sector" as 1 of 6 example industry tables (a KEEP surface — generic
illustration, not a beat) and is deliberately NOT guarded; "AI" is the plugin's
own subject, not a niche token. Added an "AI-driven content planning ..."
negative probe to lock that in (self-test now 5 caught / 6 ignored).
Non-vacuity proven by injection: a beat token appended to each new file makes
the guard fail naming the file+line; reverts clean. Gate 87→89/0/0 (+2 file
checks; assertion-count floor §18 still satisfied).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
The last de-niche slice: recast the 10 sites where the vendor/sector beat
(Microsoft|Azure|Copilot|public sector) sat as the PRIVILEGED/default example,
varying each to a concrete cross-domain example instead of sterilizing
(plugin-is-domain-general — domain comes from user config, never hardcoded).
Recast (10): url-processing-templates (news worked-example Copilot->Figma),
opportunity-generation (3 headline examples + About block -> varied/ops persona),
profile (3 "good example" headlines/impact -> healthcare/e-commerce/support),
first-comment-strategy (drop "Microsoft" from research-paper example),
poll-strategy-guide (Copilot option -> generic AI assistants),
engagement-frameworks (1 of 3 direct-address audiences -> RevOps/SaaS),
setup (audience e.g. -> two varied examples), post (invocation e.g. -> SaaS pricing),
network-builder (tagline example -> ops/manufacturing),
video-scripter (2 filename slugs -> neutral topics).
Kept as false positives (would sterilize): content-angles.md (Public Sector is
1 of 6 balanced industry tables + Industry-Agnostic section), outreach.md
(Microsoft Build/Ignite/Azure UG = 3 of ~20 varied real conferences),
linkedin-growth-playbook (biographical fact in a real case study), the
Gemini/Tavily/Perplexity MCP tool-name examples, and the algorithm-signals
"Gemini provenance" SSOT citation. AI-as-topic kept (not a niche token; the
de-AI/AI-slop mechanic is the plugin's legit subject).
Gate scripts/test-runner.sh 87/0/0 (no lint touches these files yet; §17-guard
extension to content-planner is the deferred next step). 10 files, 26/26.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBMKqPSVbvSZHtQ4heM1UY
B-S2a, the constraining-first slice of the de-niche sweep: kill the niche at
its source. B-S1 made trend-spotter pillar-driven, but the agent still READ
references/ai-content-framework.md (and so did differentiation-checker,
voice-trainer, and the content-creation skill) — an AI/Microsoft-specific file
whose very name baked in the niche. So the niche leaked back regardless of how
clean the agents were. This recasts that file domain-general and de-niches the
content-planner seasonal calendar (the other hardcoded beat: MS Build/Ignite as
THE anchors). The principle: vary concreteness, don't sterilize
(plugin-is-domain-general).
- Recast + rename references/ai-content-framework.md -> references/content-framework.md:
title "AI Content Framework" -> "Content Framework"; the 4 pillars kept as a
domain-general pattern (News/Implementation/Strategy/Tools) with examples now
spanning multiple fields instead of AI-only; AI-specific placeholders
([AI announcement], [AI system], GPT-X/Claude X) generalized to neutral
brackets; anti-patterns "AI will change everything" -> "[Field] will change
everything". The "News Monitoring / Sources by Priority" section (AI sources:
The Batch, ArXiv, r/MachineLearning, OpenAI/Anthropic blogs) — now duplicated
by the trend engine's config source-list — is thinned to point at
config/trends-sources.template.md + the data-dir override, keeping the
daily/weekly RHYTHM (general) and dropping the baked source list.
- Rename ripple, 6 referrers repointed: trend-spotter, differentiation-checker,
voice-trainer (reference lines, + dropped "AI" from descriptions), glossary
(Used-in + de-niched the "Example for AI content" pillar illustration),
linkedin-content-creation SKILL ("AI-specific angles" -> "Domain content
pillars + angles"), and test-runner §17 (NEGATIVE17 probe path + comment).
docs/hardening/log.md left intact — historical record, not a live pointer.
- content-planner.md seasonal calendar de-niched: header "Nordic/Tech Focus" ->
"rhythm, adapt to your field & region" + intro prompt; Microsoft Build,
Ignite (x2), Apple/Microsoft launches, NDC, EU AI Act, "Azure AI" example
pillar, "AI predictions", Nordic/17.mai locale anchors -> domain/region-
neutral prompts. Global anchors kept (New Year, IWD, Halloween, Black Friday,
year-end).
Deferred to after the full sweep (per STATE): extending the §17 de-niche guard
to content-planner (and content-framework) — the guard's token set + agent
scope is best designed once the sweep (B-S2b) reflects the final clean surface.
ref count unchanged (27; rename is 1->1). Gate 87/0/0 (§17 self-test green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBMKqPSVbvSZHtQ4heM1UY
The trend-spotter agent stated its own contract — "the niche lives in the
source list and the user's pillars, never in this agent" — yet contradicted it
by hardcoding the Microsoft/public-sector beat in four surfaces. Resolve the
contradiction so the file is genuinely domain-general (plugin-is-domain-general):
the domain comes from the user's profile/pillars at runtime, never baked in.
- Description: "trending topics in AI, Microsoft, and public sector" ->
"across the user's content pillars and domain"; trigger phrases "what's
happening in AI" -> "in my field"/"in my space".
- Mission: drop "intersection of AI, Microsoft technology, and public sector
digitalization" -> "within the creator's own domain, defined entirely by
their content pillars and expertise areas, never by a beat baked into this
agent".
- Content Trigger Classification: "Microsoft platform changes" -> "platform
changes in the user's stack"; "public sector milestones" -> "sector
milestones in the user's domain".
- 4-Question Relevance Filter: "Public sector leaders or enterprise AI
implementers" -> "the user's target audience (per their profile)".
- Anti-pattern example: "AI is changing everything" -> "[topic] is changing
everything".
- CI: new test-runner Section 17 (trend-spotter de-niche guard) forbids the
KTG-beat proper nouns (Microsoft|Azure|Copilot|public sector|offentlig
sektor, case-insensitive) from returning to agents/trend-spotter.md, with a
non-vacuity self-test mirroring Sections 8/13. Scoped to this one agent
(B-S1); the wider sweep (B-S2) owns the other surfaces + the
ai-content-framework.md reference filename. Assertion-count renumbered to
Section 18. Gate 85 -> 87/0/0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBMKqPSVbvSZHtQ4heM1UY
Bind the layers slices 1 (store) and 2a (config) built: trend-spotter goes from
an amnesiac, niche-hardcoded scanner to a persistent, generic engine.
- Tools: drop the `tools:` allowlist (inherit all session tools incl. any
research MCP) + `disallowedTools: Write, Edit, NotebookEdit`. An explicit
allowlist would block every MCP unless its mcp__server__tool name were
hardcoded — which breaks "prefer whatever MCP the user connected, hardcode
nothing". WebSearch+WebFetch stay as the always-available floor; Bash runs
the deterministic store CLI. (CC mechanic verified vs code.claude.com/docs.)
- Store-wiring (de-amnesia): query prior history before polling; persist every
kept trend through scripts/trends `add` (dedup/union preserved — a raw Write
would bypass it). Mirrors how specifics-bank is wired from the command layer.
- MCP-first routing: read the profile's "### Research Tooling" declaration,
prefer a declared MCP, fall back to the floor, fail soft. No hardcoded names.
- Scoring -> SSOT: replace the inline matrix/composite/bands with a pointer to
references/trend-scoring-modes.md (kortform default, long-form on request).
- Sources from config: replace the hardcoded vendor/outlet list + query bank
with a read of trends/sources.md (user override) -> trends-sources.template.md
(shipped default).
- CI: new test-runner Section 16 (trends-store binding guard, floor 21,
KTG-only skip) mirroring the specifics-bank guard; assertion-count renumbered
to Section 17. Gate 84 -> 85/0/0. model-consistency green (model unchanged).
Reload required: the tools-grant change takes effect only after a session reload.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBMKqPSVbvSZHtQ4heM1UY
Declaration/config groundwork that slice 2b's trend-spotter upgrade reads.
Standalone (no agent wiring yet — that's 2b), mirroring slice 1's pattern;
[skip-docs] for the same reason slice 1 was — user-facing docs land when 2b
wires the engine live.
- references/trend-scoring-modes.md: methodology SSOT for two rubrics —
kortform (feed post, timing 20%) + long-form (chronicle, depth 25% / timing 10%,
per tema-research-motor-spec §4.2). Both sum to 100%. trend-spotter renders from
this in 2b instead of inlining a matrix (S12-consistent).
- config/trends-sources.template.md: shipped generic source-list defaults →
user override at ${LINKEDIN_STUDIO_DATA}/trends/sources.md (data-dir, survives
reinstall; same template->data-dir pattern as user-profile).
- user-profile.template.md: new "Research Tooling" section — declared research MCPs
(Tavily/Gemini/Perplexity/Other) + WebSearch/WebFetch floor. 2b routes MCP-first.
- setup.md Step 3f + onboarding.md Phase 2: ask "which research MCPs?" -> profile.
Store only what the user declares; no hard-coded MCP names.
- test-runner.sh: EXPECT_REFS 26->27; generalized the M0 +1 delta-guard into a
named-post-M0-additions guard (POSTM0_REFS) so a legit later ref doc passes while
the anti-masking intent holds. Gate green 84/0/0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBMKqPSVbvSZHtQ4heM1UY
[skip-docs] internal plumbing — standalone store, no command/agent/pipeline
surface change until slice 2 wiring (mirrors specifics-bank slice 2). CLAUDE.md
"Telling"/counts untouched; lint stays 84/0/0.
Research-engine §5 (foundation layer) slice 1: the deterministic STORE half of
the persistent trend store — a topic-tagged, provenance-bearing inventory of
trend signals captured over time, so the research engine accumulates HISTORY
instead of starting amnesiac each session. Trend-side twin of the lived-specifics
bank (same store/dedup/query discipline; dedupe key is normalized title+URL, not
free-text content). Generic by architecture: nothing niche-specific lives here —
topics and source are free-form, decided upstream via config/profile.
scripts/trends/ (sibling to specifics-bank, same tsx convention):
- src/types.ts — TrendRecord/TrendStore schema (schemaVersion 1), minimal
generic core: title, url, source, capturedAt, topics[], optional summary
- src/store.ts — pure store: normalizeField, title+url-hash id (= dedupe key),
load/save, addTrend (dedupe + topic union on re-capture; first-sighting
source/capturedAt kept), queryByTopic (overlap-ranked then recency), history
(time-scoped, since/limit)
- src/cli.ts — add / query / list; default store under
${LINKEDIN_STUDIO_DATA:-~/.claude/linkedin-studio}/trends/ so trend history
survives plugin upgrades/reinstalls (M0 data-path seam)
- tests/store.test.ts — 21/21 green; tsc clean
- README + .gitignore for node_modules/build
Capture/scoring agent + MCP-first routing land in slice 2; the CI binding guard
is deferred to wiring, mirroring the specifics-bank timeline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the kilde-så-draft binding (slices 1–2) into the live /linkedin:newsletter
pipeline so a draft is grounded in the operator's real material BEFORE any prose.
- New Step 1.5 "Lived-specifics extraction" BETWEEN brief (1) and research (2):
query the bank → 3–5 sharp elicitation questions (vagueness refused, never
fabricated — invariant 3) → `add` to the specifics-bank → populate the
articles.NN.livedSpecifics slot-map → render the NN-kilder.md ledger. First
phase to write machine state (persists brief + personas + binding).
- Deterministic binding-gate (`validate-binding`, exit 1 = BLOCK) wired as a new
step 5 of the Step 2.5 skeleton-gate (renumbered 5→9): no prose is written on
an unresolved load-bearing claim; livedSpecifics → "bound" on pass.
- Step 2 re-scoped BY the binding: research fills the ekstern/unresolved slots +
verifies the operator's own numbers (regel 6/7) → flips bank unverified→verified
→ re-renders NN-kilder.md, instead of re-sourcing what is already grounded.
- Pipeline 17 → 18 phases: new `lived-specifics` phase id in edition-state
_doc.phases; resumption table + pipeline overview + CLAUDE.md + README updated.
- Lint: new Section 15 "Specifics-Bank Binding" wires the specifics-bank suite
into CI (suite green + case-count floor 28, KTG-only/adopter-skip); the
assertion-count anti-erosion floor moves to Section 16.
Verification: structure gate 84/0/0 (was 83), specifics-bank 28/28, contract-gate
ratify 1:1, edition-state template valid JSON (18 phases), gitleaks clean on all
5 files. Design record: docs/fix2/slice2-binding.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Slice 2 of fix#2 (kilde-så-draft): the per-edition bridge between the global
specifics-bank (slice 1) and a draft. In scripts/specifics-bank:
- binding.ts — livedSpecifics slot-map model + validateBinding, the «vaghet
avvises» gate (G4). BLOCK on unresolved / dangling specific-id / unjustified
abstrakt; WARN on an unverified-number-backed slot (regel 6/7); PASS otherwise.
- kilder.ts — renderKilder, the read-only NN-kilder.md sources ledger, rendered
deterministically from the slot-map + bank (coverage via validateBinding, so
the artifact and the skeleton-gate cannot disagree).
- cli.ts — validate-binding (exit 1 = BLOCK) + render-kilder subcommands.
edition-state schema gains articles.NN.livedSpecifics ({ slots, status }) + _doc.
Design record + the Step 2 research re-scope spec: docs/fix2/slice2-binding.md.
Wiring into newsletter.md (Step 1.5 elicitation + Step 2.5 gate + pipeline 17->18)
is slice 3 — not in this commit.
Tests 28/28 (specifics-bank; +9 binding +4 kilder), tsc clean, plugin gate 83/0/0
unaffected, gitleaks clean on all 8 files.
[skip-docs] internal plumbing — no command/agent/pipeline surface change until
slice 3 wires it; documenting it in the user-facing root CLAUDE.md/README now would
overclaim unreachable functionality. Docs ARE updated where they belong: the design
record docs/fix2/slice2-binding.md, scripts/specifics-bank/README.md, and the
edition-state _doc. Root-doc update lands with the slice-3 wiring feat.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix#2 (kilde-så-draft) slice 1: the deterministic STORE half of the
lived-specifics bank — the operator's real, un-generatable raw material
(measured number / named-but-real case / what broke / contrarian opinion /
mind-change), so drafts start grounded instead of hollow (retning §3;
dream-spec "Lived-Specifics Extraction", the single biggest missing upstream
piece for the authenticity thesis).
scripts/specifics-bank/ (sibling to contract-gate, same tsx convention):
- src/types.ts — Specific/Bank schema (schemaVersion 1), un-generatable taxonomy
- src/bank.ts — pure store: normalizeContent, content-hash id (= dedupe key),
load/save, addSpecific (dedupe + topicTag union on re-capture), queryByTopic
(active-only, ranked by tag overlap then recency)
- src/cli.ts — query / add / list; default bank under
${LINKEDIN_STUDIO_DATA:-~/.claude/linkedin-studio}/specifics-bank/ so it
survives plugin upgrades/reinstalls (M0 data-path seam)
- tests/bank.test.ts — 15/15 green; tsc clean
- README + .gitignore for node_modules/build
Content is stored verbatim; never AI-generated. The elicitation interview
(refuses vague answers; abstrakt/ekstern escape hatches) lands in slice 3
(/linkedin:newsletter Step 1.5). Forward-compatible with the profile-evolution
/ "second brain" architecture.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Slice 3 of fix#1 (§C2 bindende + akkumulerende): wire the deterministic
§B/§C1 contract-gate into the long-form pipeline as a pre-handoff step, so
mechanical violations die deterministically BEFORE the costly AI gates and
never reach the operator at a judgment gate ("rettelser fester seg").
Newsletter pipeline:
- New Step 4.5 (between Step 4 consistency/quality and Step 5 fact-check):
(1) ratify the gate (rules.ts <-> §E-manifest 1:1; DRIFT escalates to
operator, not a draft fix) and (2) gate the draft (exit 0 PASS / exit 1
BLOCK -> fix by tightening + re-run; WARN = direction, never blocks).
Adopter-safe: no-op when contract-gate deps / the maskinrommet contract
are absent.
- Pipeline phase count 16 -> 17 across every LIVE surface (overview table +
build-status + narrative + banner + schema pointer in newsletter.md;
command-table rows in CLAUDE.md and README). Historical version-narrative
entries left untouched.
- edition-state.template.json: new "contract-gate" phase string (Step 4.5),
between consistency-quality and factcheck-sweep. JSON valid; phases 16->17.
CI integration (test-runner.sh, Section 14 — new):
- 14a: contract-gate suite stays green (npm test exit 0) with a 33-test
case-count floor (anti-erosion).
- 14b: ratify invariant — rules.ts bound 1:1 to the §E-manifest.
- Both KTG-internal and adopter-safe (warn/skip, never fail, when deps or the
contract are absent). set +e inside the test command-substitution keeps a
red suite from aborting the runner; ratify exit 1 captured via if-subshell.
- SC6 anti-erosion section renumbered 14 -> 15; header comment updated.
Verification:
- Structure lint 81 -> 83/0/0 (contract-gate suite + ratify both green).
- Documented Step 4.5 command smoke-tested on a real draft
(serier/silvija-seres-motsvar/utkast/04-utkast.md): 0 BLOCK / 2 WARN ->
"Gate passert", exit 0.
- edition-state.template.json valid JSON; phases array = 17.
foldIns capture into the pipeline phases deferred to a later slice (scope A).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Slice 2 av fix#1 («rettelser fester seg»): binder rules.ts til den menneskelige
sannhetskilden (skrivekontrakt §E), så en korreksjon gjort én gang håndheves for
alltid i stedet for å gjenoppdages hver utgave (diagnose-gap 2).
- ratify.ts: importerer RULES direkte (ingen skjør tekst-parsing), krever en
bijeksjon mot §E gate-bindings-manifestet (id ↔ ref ↔ severity) + dangling-
§-anker-sjekk. CLI: `--ratify [kontrakt]` / `npm run ratify`. Sti: arg > env
MASKINROMMET_CONTRACT > default (relativt til modulen).
- edition-state.template: foldIns[] fangst-kø (per-artikkel proveniens,
pending|promoted|rejected, mekanisk→rules.ts / dømmekraft→§C2).
- 12 nye tester (33 totalt), tsc rent. Live-kontrakt-integrasjonstest ratify-er
det ekte §E-manifestet rent (10 regler bundet 1:1).
Companion: skrivekontrakt §E (JA-promoter-prosedyre + manifest) i maskinrommet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds scripts/contract-gate/ — a deterministic, AI-free gate that enforces the
mechanical half of the Maskinrommet writing contract (§B red flags + §C1
regelsjekk) BEFORE operator handoff, so the same mechanical correction never
reaches KTG twice. First slice of fix#1 (binding + accumulating); target
re-scoped to close all 4 diagnosis gaps = sustainable newsletter workflow.
- Structured ruleset as DATA (src/rules.ts) so accumulation (slice 2) appends.
- 3 BLOCK (modell-strawman, series-thesis "er ikke modellen", _underscore_)
+ 7 WARN (judgment-needed). Block only on provable, ~zero-FP matches.
- Code/frontmatter masking; line-accurate reporting; CLI receipt + exit code.
- 21/21 tests (node:test), tsc clean.
- Validated on 6 real editions: 0 BLOCK on published Del 2-5; caught a real
Rule-10 violation in Del 6 (WIP) automatically. Versal-tic uses an
emphasis-word denylist after real-data FPs (NAV/DFØ/NPM).
Finding: the plugin already mirrors §A + §C2; §C1 was the unhomed gap this fills.
Plugin structure lint unchanged (81/0/0); no new agents/commands/refs/skills.
assets/examples/high-engagement-posts.md restored as the generic placeholder
example. STATE.md is now gitignored (local-only): there is no private remote,
and STATE must not reach the public mirror.
The reviews-line used the (await parallel(...)).filter(...) pattern but had
only 3 closing parens before .filter, leaving the wrapper paren unbalanced.
Added the missing close paren so the script parses.
Post-hardening. Two jobs queued for next session (operator: work starts next session):
JOB 1 = run the dream-spec expert panel (docs/expert-review/dream-spec-panel.md, launch-ready
Workflow script; supersedes plan.md critique-of-current direction). Operator twist: a world-class
LinkedIn-growth panel defines the IDEAL plugin (north-star), then reviews the current plugin against
it (informational, not a plan). Mission clarified: followers of an account using this plugin recruit
NEW LinkedIn users to the account because the account is so good. JOB 2 = push everything EXCEPT
STATE.md to public open/ (STATE woven into 8/18 parked commits -> strip via filter-repo, backup first,
re-gitignore STATE local-only; open/ already public = low risk; check Ralph-Wiggum history item).
STATE.md updated with full mechanics for both. Nothing started this session per operator.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
S31c descriptive-% log entry (full A/B/C/straggler breakdown + VERIFY). STATE: the Nx-multiplier
+ descriptive-% platform-norm class is now fully SSOT-reconciled; next phase = post-hardening
expert panel (operator-triggered), then command testing + GUI. Push parked (ahead 17; open/ is
public, STATE must not be published).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
Log entries for the two multiplier-scrub batches (S31a reference-docs 35ed480,
S31b commands/agents c850352) with full TEST/HARDEN/KEPT/VERIFY/SURFACED, and
STATE.md overwritten for session-end.
Captures the new measured-data-class rule (S31b): "Nx"/"N%" that reports or
hypothesizes from the user's OWN measured analytics — or a pure placeholder
[X]x — is not a platform claim and stays (the ab-test:418 "your data shows ...
test whether this holds" pattern); distinguish from generic-claim TARGET. S31c
must apply it to the descriptive-% sub-class (some bands are unsourced platform
norms to scrub, some are sourced benchmarks / user-measured % to keep).
Also records the jerndisiplin reminder that grep output does not count as a
Read for the Edit safety check (three S31b edits were correctly blocked until
re-read with the Read tool).
Push held: origin = open/ (public), STATE.md must not go there; ahead 14 parked.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
Hardening, multiplier class (S31, batch b). SSOT-reconciliation of unsourced
generic "Nx" reach/growth multipliers asserted as platform fact across commands
and agents. Same discipline as S31a: read-and-show (Read tool, not Bash-tail) ->
edit -> re-grep final; 8-step gate.
5 edits / 5 files (+5/-5):
- report:249 (PDF/Carousels 3.4x reach multiplier) -> top organic format, no
reliable multiplier.
- outreach:652 (collab content 2-3x reach, ref collaborations-guide) -> above-
baseline reach, aligned with the S31a collab scrub.
- engagement-coach:120 (2-3 touchpoints = 3.6x more likely) -> far more likely
(figure unverified).
- strategy-advisor:241 (saves drive 3x faster audience growth) -> faster growth
(figure unverified).
- content-repurposer:591 (Expected reach multiplier: [2-5x original]) -> Expected
reach: above the original (no reliable multiplier).
KEPT (operator-ruled measured-data class — report/hypothesize from the USER's own
analytics, not a platform norm; the number is an illustrative-computed example or
a pure placeholder, and removing it adds no honesty): report:303 ("[format] ...
2x more shares" example), report:335 ("your one carousel got 3x" rendered
example), ab-test:418 ("Your data shows ... 2x ... test whether this holds"),
outreach:1234 ("Average reach multiplier (collab vs solo): [X]x" output field).
KEPT INTACT: engagement-coach:148 SSOT comment ~= 2x a like. OUT (untouched):
strategy-advisor:239 4-5x/week frequency.
VERIFY: re-grep generic-claim targets in the 5 files -> 0; measured-data class +
KEEP/OUT intact; gate scripts/test-runner.sh = 81/0/0 exit 0, counts 29/19/26
+ v0.5.0 unchanged.
Push held: origin = open/ (public namespace), STATE.md must not go there.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
Appends the S30 row to the hardening journal (FIXED, 20 edits/7 files, commit 1bab1d6) and
catalogs the surfaced S31 multiplier-scrub class (~50 unsourced "Nx" instances across ~15 files,
with the SSOT-backed-vs-unsourced and coefficient-vs-frequency distinctions the next pass must make).
New entry only — prior entries untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
Appends the S29e row to the hardening journal, closing the per-step gap (S29a-d were logged;
S29e was not). New entry only — existing S29a-d entries untouched (history content URØRT).
Documents: pure rename + 17 pointer files (29 occurrences), the history-URØRT categorisation,
and VERIFY (no old file remains; residual confined to STATE+CHANGELOG+plan+log; gate 81/0/0).
Marks S29 complete (a-e); next is S30 magnitude-scrub.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
Final sub-pass of the S29 plugin-wide terminology scrub. The canonical reference file is
renamed and every functional pointer updated atomically in one commit. The file's in-file
title/headers were already FORM A-scrubbed in S29c (H1 reads "Content Angles"), so S29e is a
pure rename + pointer update — no FORM A remained in the file.
Rename: references/thought-leadership-angles.md -> references/content-angles.md (git mv).
Pointers updated (17 files, 29 occurrences) — token "thought-leadership-angles" -> "content-angles":
- references/ (2): ai-content-framework, glossary
- agents/ (7): content-repurposer, strategy-advisor, network-builder, content-planner,
trend-spotter, video-scripter, differentiation-checker
- commands/ (6): pipeline, video, post, competitive, react, batch
- skills/ (1): linkedin-content-creation/SKILL
- docs/ (1, forward-looking): integration-test-guide
Left URØRT per the standing S29 decision (history = honest record of a past state, not a
runtime load): CHANGELOG.md, docs/hardening/log.md, docs/hardening/plan.md. STATE.md untouched
here (rewritten at session end).
Verify: no thought-leadership-angles* file remains; references/content-angles.md present; zero
residual "thought-leadership-angles" in commands/agents/references/skills/integration-test-guide;
structure gate scripts/test-runner.sh 81/0/0 exit 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
Append the S29d verification record to docs/hardening/log.md (anchored
"### S29d terminology-scrub" entry, matching S29a-S29c format): the 8-edit replacement map
across 6 files (skills/linkedin-content-creation x3, linkedin-strategy, config template, two
shipped assets, the clipboard test fixture), the 4 judgment-calls, the FORM B = 8 kept-by-design
list (buzzword denylists + role usages), the broad anchor-link check (none break), and the
verify block (FORM A in scope = NONE; FORM B unchanged 8; filename pointers unchanged 21;
clipboard test 13/0; gate 81/0/0; counts 29/19/26/6 unchanged). Documents fix commit d15ffa4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
Fourth sub-pass of the S29 plugin-wide terminology scrub: the banned brand phrase
"thought leadership" (FORM A) removed from the remaining surfaces — skills, a config template,
shipped assets, and a test fixture. 8 edits across 6 files. Vocabulary consistent with
S29a-S29c: "thought leadership angle(s)" -> "content angle(s)"; the "Value Test" family ->
"Authority Value Test"; "Standard Thought Leadership Post" -> "Standard Post" (matches the
S29a/S29c "Standard Post Structure" equivalent).
Edits: skills/linkedin-content-creation (3) "Identify Thought Leadership Angles" -> "Content
Angles", "Standard Thought Leadership Post" -> "Standard Post", "The 8 Thought Leadership
Angles" -> "The 8 Content Angles"; skills/linkedin-strategy:176 phase-focus cell "Thought
leadership, cross-platform" -> "Signature content, ..." (avoids the Authority-phase column
echo); config/user-profile.template:35 "Build thought leadership & authority" -> "Build
authority & influence"; assets/checklists/quality-scorecard:136 "Passes thought leadership
test" -> "Passes Authority Value Test"; assets/case-studies/case-study-template:173 "Best for:
Thought leadership" -> "Authority content"; hooks/scripts/__tests__/clipboard-helper.test:61
unicode fixture "Thought leadership" -> "Authority" (emoji + arrow retained; coverage unchanged).
Scope (operator-locked, inherits S29a-S29c): FORM A only. FORM B = 8 untouched -- the 5
buzzword-denylist entries MUST list the banned word ("thought leader"), plus 3 role usages
(linkedin-networking x2, README "niche thought leaders"). The 21 thought-leadership-angles.md
filename pointers deferred to S29e. No anchor links to the changed headers anywhere in the
plugin. Forward-looking docs already clean.
Verify: FORM A in scope = NONE; FORM B unchanged (8); filename pointers unchanged (21);
clipboard-helper test 13/0; gate 81/0/0; counts 29/19/26/6 + v0.5.0 unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE