linkedin-studio/docs/second-brain/brief.md
Kjell Tore Guttormsen d6acb5d36c docs(linkedin-studio): second-brain SB-S0 brief + plan (review-hardened)
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
2026-06-23 13:24:18 +02:00

100 lines
13 KiB
Markdown
Raw Permalink 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.

# Task Brief — Second Brain (compounding per-user knowledge engine)
> **Status:** revised after adversarial brief-review (light-Voyage step 2 → findings folded in). Architecture approved by operator 2026-06-23 (`architecture.md`).
> **Inputs:** `architecture.md` (approved design) · `research/{connector-egress,secondbrain-sota,silo-inventory}.md`.
> **Build target of this brief:** the arc objective + scope fence, and the **first slice SB-S0** in detail. SB-S1..S4 are scoped one-line; their detailed plans come later, one per slice.
> **Review delta (2026-06-23):** B1 fold-source rewritten (template-as-field-source + source-absent no-op); B2 pathguard claim corrected (discrepancy flagged); M1 floors as `≥`; M2 scaffold trigger decided (idempotent CLI/TS subcommand, session-start wiring deferred to SB-S2); M3 profile grammar contract pinned; M4 `brain/` is runtime-only (no committed seed); minors m1m4 folded.
---
## 1. Objective
Turn the plugin's existing siloed per-user stores into **one compounding "second brain"**: a per-user knowledge engine that accumulates everything about a creator (posts, articles, newsletters, plans, ideas), keeps a two-layer profile that improves over time, and stays aligned to who the user *is now* — drift-resistant and collapse-resistant. It is **memory AND an operations centre**.
The engine is a **plugin capability** (domain-general, shareable). The user's data lives in the per-user data dir. The user's personal cockpit lives in Maskinrommet (out of scope here).
## 2. Background (why this shape)
- **Nothing to migrate** — M0 already routed the **data-root silos** (trends, specifics, analytics, drafts/queue, plans, voice, profile, scaffolds) through one tested seam (`getDataRoot`), all surviving reinstall. The **state file** (silo A) stays on its own resolver (`getStateFile()`, `~/.claude/linkedin-studio.local.md`) and is NOT under the seam (see §7). The task is **unify**, not move. (`silo-inventory.md`)
- **Plain-text + JSON + git, not a vector/graph DB** — Claude *is* the retrieval engine (reads files in-context); a heavyweight store adds infra + lock-in for no gain. (`secondbrain-sota.md` §B.1)
- **Ingest is manual-first as the contract** — only EU/EEA DMA portability gives automated *content* pull (no analytics); scraping is a ToS breach. Connector is an additive tributary, never a dependency. (`connector-egress.md`)
## 3. Confirmed decisions (not open for re-litigation in review)
1. **Boundary:** engine → plugin · user data → data dir · cockpit → Maskinrommet.
2. **Shape:** thin Markdown `brain/` hub (two-layer `profile.md` + episodic `journal/` + `operations.md` + `index.md` MOC) over the **existing typed tributaries** (voice-samples, specifics-bank, trends, analytics), fed by a provenance-tagged `ingest/` seam.
3. **Tributaries keep their own schema/dedupe/lifecycle** — do NOT force one schema across silos. Only the user-profile field-set folds into `brain/profile.md`.
4. **Invariants:** provenance-weighted learning (`published` only, never `ai-draft`); one canonical entity id + one provenance shape (`provenance ∈ {human, published, ai-draft}`); episodic/semantic split; evidence-threshold promotion; temporal-validity + freshness nudge; anti-sycophancy default; frozen-past-self anchor; zero required curation.
Review SHOULD challenge: feasibility, sequencing, hidden coupling, testability, scope leakage, missing risks. Review SHOULD NOT re-open the four decisions above (operator-approved) unless it finds they are internally contradictory or technically impossible.
## 4. Scope — the arc (slices)
| Slice | One-line scope |
|-------|----------------|
| **SB-S0 — Foundation** *(this brief)* | `brain/` scaffold + two-layer `profile.md` (fold in the user-profile field-set) + `index.md` + `operations.md` + `journal/`; entity-id + provenance shape as a small typed, tested module; `ingest/{inbox,published}` dirs + the manual-import **contract** (shape only). No loop, no ingest-processing logic, no connector. |
| SB-S1 — Ingest + gold signal | Manual import populates `ingest/published/` with `provenance=published`; wire profile/voice to learn from published-only. |
| SB-S2 — Evolution loop | Sleep-time consolidation: journal+published+tributary deltas → profile diff w/ evidence_count/provenance/timestamps; threshold promotion; contradiction reconciliation; brain-wide freshness nudge. **Owns the session-start wiring of the scaffold init.** |
| SB-S3 — Cross-silo graph + ops centre | Thread the id through tributaries (post↔specific↔trend↔analytics); flesh out `operations.md`; retire dead `content-history.md` + triple-post reconciliation. |
| SB-S4 *(optional, later)* | EU/EEA DMA portability API as auto-tributary into `ingest/inbox/`. |
## 5. SB-S0 in detail (the build target)
**Deliverables:**
1. **`brain/` scaffold initialiser — an idempotent CLI/TS subcommand** (the trends-store idiom: runtime-created under the data-dir seam, NOT a tracked repo seed). It creates `brain/{index.md,profile.md,operations.md}` + `brain/journal/` + `ingest/{inbox,published}/` under `getDataRoot(subdir)` (which already accepts arbitrary subdirs — no new seam function needed). **Idempotent + migration-safe** (running twice is a no-op; never clobbers existing content). **No per-session cost** — session-start *wiring* of this init is explicitly deferred to SB-S2; SB-S0 ships only the invokable subcommand.
2. **`brain/profile.md` — two-layer schema** with `§ Static` and `§ Dynamic` sections. **Pinned contract (fixed):** one fact = one parseable record carrying all six fields `value · first_seen · last_seen · evidence_count · provenance(human|published|ai-draft) · status(active|superseded)`, and `serialize ∘ parse = identity`. **Open to the plan:** only the surface serialization syntax (frontmatter vs a defined line grammar).
3. **Fold the user-profile field-set into `brain/profile.md` — lossless + idempotent, with a defined source-absent path.** Source of the field *structure* is `config/user-profile.template.md` (the only file that ships; `profile/user-profile.md` is `expected-absent`, migrate-data.mjs:36). Behaviour:
- **Source-absent (the common case):** scaffold an empty two-layer `profile.md` whose static layer contains every template field present-but-unfilled.
- **Runtime instance present** (`${data}/profile/user-profile.md` exists): fold its filled values into the static layer.
- Re-running never duplicates fields and never overwrites already-folded values.
4. **Entity-id + provenance module** — a small typed module (TS, matching the trends/specifics idiom) that mints one canonical entity id at creation and defines the single provenance shape (`{human|published|ai-draft}`). Pure functions, deterministic, unit-tested. **Not yet threaded through tributaries** (that's SB-S3) — SB-S0 only establishes the module + shape.
5. **`index.md` MOC + `operations.md` seeds (runtime-generated)** — minimal but real: `index.md` points to every tributary with a freshness placeholder; `operations.md` has the "who I am now" anchor section. Generated by the initialiser (D1), not committed to the repo tree.
6. **Tests** — unit tests for the module + the scaffold + the fold (incl. the source-absent path). No structure-lint count change expected (SB-S0 adds no `agents/`, `commands/`, `references/`, or `skills/` file); a version bump, if any, updates README/CHANGELOG badges only.
## 6. Success criteria (SB-S0 — testable)
- **SC1 (scaffold, runtime):** with `LINKEDIN_STUDIO_DATA` set to a temp dir, invoking the init subcommand creates `brain/{index,profile,operations}.md` + `brain/journal/` + `ingest/{inbox,published}/` **at the runtime data-path** (not the repo tree), through `getDataRoot`; invoking it twice is a no-op (idempotent, no clobber). Verified by a test that sets the env var, runs init twice, asserts existence + unchanged content.
- **SC2 (profile parse):** `brain/profile.md` parses into `§Static`/`§Dynamic`; a fact round-trips through the pinned grammar with all six fields intact (`serialize ∘ parse = identity`). Verified by a parse/serialize unit test.
- **SC3 (fold lossless + source-absent):** (a) **source-absent:** running the fold with no runtime user-profile yields a `profile.md` whose static layer lists every `config/user-profile.template.md` field; (b) **populated:** given a synthetic populated fixture, the fold carries every filled field into the static layer (diff shows none dropped); (c) re-running either case does not duplicate or overwrite. Verified by fixture-based diff tests.
- **SC4 (id/provenance module):** the module mints a deterministic id (same input → same id) and **rejects** a malformed provenance value (throws; only `{human,published,ai-draft}` accepted); ≥1 unit test per public function.
- **SC5 (no regression):** `scripts/test-runner.sh` is green (currently 89 passed / 0 failed / 0 warnings); each floored suite stays **≥ its floor** (trends ≥24, specifics ≥28, contract ≥33); all hook tests pass.
- **SC6 (seam, no-op):** SB-S0 adds **no new seam function** (`brain/`/`ingest/` use the existing `getDataRoot(subdir)`), so the twin-sync surface is unchanged. Asserted as a no-op; *if* a future slice adds a seam function, the existing twin-sync test (`hooks/scripts/__tests__/data-root.test.mjs`) must be extended.
## 7. Non-goals / scope fence (SB-S0 does NOT)
- **No evolution loop / consolidation** (SB-S2).
- **No ingest processing logic** — `ingest/` dirs + the documented manual-import contract only; no parser that reads inbox files yet (SB-S1).
- **No connector** — no LinkedIn API code (SB-S4).
- **No cross-silo threading** — the id module exists but is NOT yet woven into trends/specifics/analytics (SB-S3).
- **No session-start wiring** of the scaffold init (SB-S2 owns it) — SB-S0 ships only the invokable subcommand.
- **Do NOT touch tributary schemas** (voice/specifics/trends/analytics stores keep their current shape).
- **Do NOT reconcile the two-roots question** (state file at `~/.claude/linkedin-studio.local.md` stays a sibling on `getStateFile()`; not folded in SB-S0).
- **Do NOT retire `content-history.md` or reconcile the triple-recorded post** (SB-S3).
- **No GUI / no cockpit** (Maskinrommet, out of repo scope).
## 8. Research plan / knowledge status
- **Done:** the three threads (connector reality, second-brain SOTA, silo inventory) are complete and persisted. No further research is required to start SB-S0. (Brief-review confirmed both §8 open assumptions are correctly non-blocking.)
- **Open assumptions to test (flagged, not blocking SB-S0):**
- "Claude-as-retrieval-engine over plain files is sufficient (no vector DB)" — testable once the brain holds content (post-SB-S1). Marked assumption.
- Newsletter-edition coverage by the portability `ARTICLES` domain — only relevant to SB-S4; verify against a real export then.
## 9. Constraints / risks
- **TDD iron law:** no production code without a failing test first.
- **New code placement:** SB-S0's new code is a **TS module under `scripts/`** (the trends/specifics idiom) + a runtime scaffold + edits to existing files. It creates **no new `.mjs` under `hooks/scripts/`**, so the pathguard does not affect SB-S0. **Verified 2026-06-23 (code-checked; STATE.md line 39 CONFIRMED correct, brief-review B2 was WRONG):** the llm-security plugin guard `pre-write-pathguard.mjs` IS enabled (`~/.claude/settings.json:148`) and registered as a PreToolUse hook with `matcher:"Write"` (Write-only); its Category 6 regex `/[\\/]hooks[\\/]scripts[\\/].*\.mjs$/` matches any `…/hooks/scripts/*.mjs`, **including this repo**. ⇒ a NEW `.mjs` under `hooks/scripts/` **cannot be written with the Write tool**; **editing an EXISTING `.mjs` is fine** (matcher is Write-only). Relevant to SB-S2 (which touches hook code → create new hook `.mjs` via Bash-heredoc, or edit an existing file). SB-S0 stays entirely in `scripts/`, so it is unaffected.
- **Twin data-root implementations** must stay byte-compatible (`hooks/scripts/data-root.mjs``scripts/analytics/src/utils/storage.ts`); SB-S0 adds no new seam function so the twin contract is untouched (SC6).
- **Public repo (`open/` Forgejo):** confirm before push; push only in the allowed window; STATE.md stays gitignored.
- **Structure lint** counts `agents/`, `commands/`, `references/`, `skills/` + the version badge. SB-S0 adds none of these, so no count guard fires absent a version bump.
- **Node v25 test quirk:** `node --test <dir>` doesn't work — use glob `__tests__/*.test.mjs`; run TS tools from their own dir.
- **Risk — fold ambiguity:** the user-profile→profile.md fold is the one data-shaping step in SB-S0; if the grammar is under-specified it risks lossy or non-idempotent folding. Mitigate with fixture-based diff tests (SC3) covering both the source-absent and populated paths before wiring.
## 10. Definition of done (SB-S0)
SC1SC6 all pass; SB-S0 deliverables 16 exist; non-goals untouched; committed (push per window); `architecture.md` slice status updated to "SB-S0 landed"; `CLAUDE.md`/README touched only if a version bump is taken.