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
13 KiB
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; M4brain/is runtime-only (no committed seed); minors m1–m4 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)
- Boundary: engine → plugin · user data → data dir · cockpit → Maskinrommet.
- Shape: thin Markdown
brain/hub (two-layerprofile.md+ episodicjournal/+operations.md+index.mdMOC) over the existing typed tributaries (voice-samples, specifics-bank, trends, analytics), fed by a provenance-taggedingest/seam. - 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. - Invariants: provenance-weighted learning (
publishedonly, neverai-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:
-
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 createsbrain/{index.md,profile.md,operations.md}+brain/journal/+ingest/{inbox,published}/undergetDataRoot(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. -
brain/profile.md— two-layer schema with§ Staticand§ Dynamicsections. Pinned contract (fixed): one fact = one parseable record carrying all six fieldsvalue · first_seen · last_seen · evidence_count · provenance(human|published|ai-draft) · status(active|superseded), andserialize ∘ parse = identity. Open to the plan: only the surface serialization syntax (frontmatter vs a defined line grammar). -
Fold the user-profile field-set into
brain/profile.md— lossless + idempotent, with a defined source-absent path. Source of the field structure isconfig/user-profile.template.md(the only file that ships;profile/user-profile.mdisexpected-absent, migrate-data.mjs:36). Behaviour:- Source-absent (the common case): scaffold an empty two-layer
profile.mdwhose static layer contains every template field present-but-unfilled. - Runtime instance present (
${data}/profile/user-profile.mdexists): fold its filled values into the static layer. - Re-running never duplicates fields and never overwrites already-folded values.
- Source-absent (the common case): scaffold an empty two-layer
-
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. -
index.mdMOC +operations.mdseeds (runtime-generated) — minimal but real:index.mdpoints to every tributary with a freshness placeholder;operations.mdhas the "who I am now" anchor section. Generated by the initialiser (D1), not committed to the repo tree. -
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/, orskills/file); a version bump, if any, updates README/CHANGELOG badges only.
6. Success criteria (SB-S0 — testable)
- SC1 (scaffold, runtime): with
LINKEDIN_STUDIO_DATAset to a temp dir, invoking the init subcommand createsbrain/{index,profile,operations}.md+brain/journal/+ingest/{inbox,published}/at the runtime data-path (not the repo tree), throughgetDataRoot; 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.mdparses 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.mdwhose static layer lists everyconfig/user-profile.template.mdfield; (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.shis 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 existinggetDataRoot(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.mdstays a sibling ongetStateFile(); not folded in SB-S0). - Do NOT retire
content-history.mdor 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
ARTICLESdomain — 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.mjsunderhooks/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 guardpre-write-pathguard.mjsIS enabled (~/.claude/settings.json:148) and registered as a PreToolUse hook withmatcher:"Write"(Write-only); its Category 6 regex/[\\/]hooks[\\/]scripts[\\/].*\.mjs$/matches any…/hooks/scripts/*.mjs, including this repo. ⇒ a NEW.mjsunderhooks/scripts/cannot be written with the Write tool; editing an EXISTING.mjsis fine (matcher is Write-only). Relevant to SB-S2 (which touches hook code → create new hook.mjsvia Bash-heredoc, or edit an existing file). SB-S0 stays entirely inscripts/, 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)
SC1–SC6 all pass; SB-S0 deliverables 1–6 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.