# Plan — SB-S3a: wire the first profile.md READER (`strategy-advisor`)

> Brief: `docs/second-brain/brief-sb-s3a.md` (APPROVE, FIXes folded). Slice: SB-S3a.
> Operator confirmed: **`strategy-advisor`**, READ-only, **manual SC4/SC5** (no behavioural harness).
> Order is TDD: the failing lint check lands BEFORE the agent edit (iron law).

## Goal

`strategy-advisor` consumes `brain/profile.md` as *evidence-to-test*, guarded by a deterministic wiring lint. No write path, no parser, no tributary/hook change. Smallest end-to-end proof that the second brain feeds generation.

## Files touched (exhaustive — for scope-guardian)

| File | Change | Why |
|------|--------|-----|
| `scripts/test-runner.sh` | **NEW Section 16d** (2 unconditional checks) + bump `ASSERT_BASELINE_FLOOR` 78→80 + extend floor-history comment + header enumeration | SC1/SC2/SC6 — the TDD anchor |
| `agents/strategy-advisor.md` | add `brain/profile.md` to Step 0 Load Context + a short consumption subsection (anti-sycophancy literal + graceful-absence) | SC1/SC2/SC5 — the wiring itself |
| `docs/second-brain/consolidation-loop.md` | **append** an "SB-S3a landed" reader-status line (do NOT rewrite the existing :67 contract prose) | doc reconciliation |

**Not touched (scope fence):** any `scripts/brain/**` TS · any `hooks/scripts/**` · any tributary JSON · `architecture.md`/`brief.md` (arc-level "motor-only until S3" stays true until all of S3 lands) · `content-optimizer.md` (second reader, not S3a) · CLAUDE.md agent table (responsibility text unchanged — the count/model lint is unaffected; a note is optional and deferred to avoid churn).

## Step 1 — (RED) Lint: Section 16d + floor bump

Insert a new section after Section 16c (`test-runner.sh:780`, before Section 17 at `:782`), modelled byte-for-byte on 16c's idiom (literals + `*_wired()` + non-vacuity self-test + real-file grep with `grep -qF`):

- **Literals:** `READER_LIT='brain/profile.md'`, `ANTISYC_LIT='evidence to TEST'`.
- **`reader_wired()`** — text wired iff BOTH literals present (`grep -qF` each; echo twice as 16c does).
- **Check A (self-test, unconditional):** a fully-wired probe (`"reads brain/profile.md as evidence to TEST"`) MUST be detected; three under-wired probes MUST be rejected:
  1. `"reads brain/profile.md but never frames how to weigh it"` (has reader lit, missing anti-syc)
  2. `"treats facts as evidence to TEST but names no source file"` (has anti-syc, missing reader lit)
  3. `"reads profile/user-profile.md and tests the evidence"` (decoy: legacy path + lowercase "evidence" — must NOT match either exact literal)
  → `pass`/`fail` "profile-reader self-test: full wiring detected; 3 under-wired forms rejected".
- **Check B (real-file grep, unconditional):** `grep -qF "$READER_LIT" agents/strategy-advisor.md && grep -qF "$ANTISYC_LIT" agents/strategy-advisor.md` → `pass` "strategy-advisor.md wired to brain profile reader (names '…profile.md', frames 'evidence to TEST')" else `fail`.
- **Header enumeration (`:19-35`):** add a sentence — "the brain profile-reader guard (SB-S3a: `strategy-advisor` names `brain/profile.md` AND carries the anti-sycophancy literal `evidence to TEST`, with a non-vacuity self-test) in Section 16d". Range "Sections 8–18" unchanged (16d is in range).
- **Floor (`:849-865`):** `ASSERT_BASELINE_FLOOR=78` → `80`; extend the history comment: "+2 for SB-S3a's two UNCONDITIONAL Section-16d checks (profile-reader self-test + strategy-advisor wiring grep) = 80." Both new checks are deps-free → they lift the deps-absent minimum, so the +2 bump is correct (NOT pinned to deps-present TOTAL).

- **Non-collisions (verified by plan-critic):** the new prose carries no algorithm-magnitude token (Section 8 stat-consistency safe) and no `Significant?(` column (Section 11 render-chain safe); `brain` is absent from Section 13's `BARE_DATA` regex (`test-runner.sh:514`) AND the edit uses the exempt `${LINKEDIN_STUDIO_DATA:-…}` external form → Section 13 data-dir guard safe. Agent count (19) + `strategy-advisor` model/frontmatter unchanged → Sections 2/9/10 unaffected.

**RED gate:** `bash scripts/test-runner.sh` → Check A passes, **Check B FAILS** (agent not yet wired), floor check passes (TOTAL 95 ≥ 80) → exit 1. This is the required failing test.

## Step 2 — (GREEN) Wire `strategy-advisor.md`

1. **Step 0 Load Context** (`strategy-advisor.md:41-47`): insert one line **inside** the fenced code block, immediately BEFORE the closing ``` fence (currently `:47`) — i.e. the new path becomes `:47` and the fence shifts to `:48`. Do NOT place it after the fence (that breaks the code block). The line:
   `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/brain/profile.md   → evolving second-brain profile (consolidated, evidence-weighted facts about the user)`
2. **New subsection** immediately after the Load Context block (after `:49`), e.g. `### Consuming the evolving profile (brain/profile.md)`:
   - Two layers: `## Static` (stable, high-confidence) vs `## Dynamic` (emerging); each fact carries `evidence_count` + `last_seen` — weight by strength/recency.
   - **Anti-sycophancy (binding):** "Treat every profile fact as **evidence to TEST**, not flatter — counter-pressure it against analytics/state each time (e.g. *the profile says you lean X, but your imports show Y — test that*). The profile informs; it never dictates or flatters." ← carries the exact literal `evidence to TEST`.
   - **Graceful absence:** "If `brain/profile.md` is missing or empty (fresh installs have no brain yet), proceed silently on the other context sources — no error, no note about a missing profile."

**GREEN gate:** `bash scripts/test-runner.sh` → Check A + Check B pass, FAIL=0, **95/0/0**, brain floor 82 untouched.

## Step 3 — Doc reconciliation

`consolidation-loop.md:67-68` currently reads *"`brain/profile.md` has no reader yet … wiring content agents/commands to consume it is SB-S3."* — which becomes **false** the moment S3a lands a reader. **No lint guards `docs/`** (Section 11 scans `references/ commands/ skills/ hooks/prompts/ agents/ assets/` only, and `PROP_FORBIDDEN` matches only the significance-verdict column — verified by plan-critic; `grep consolidation-loop test-runner.sh` = 0 hits). So **EDIT the bullet** (it lives under "## Honest limits" — keep it a limit) to the true partial state, e.g.:
"**`brain/profile.md` has one reader as of SB-S3a.** S2 evolved the profile motor-only; SB-S3a wired the first consumer — `strategy-advisor` reads it as *evidence-to-test*. Broader consumption (more content agents, a hook-level digest) remains later S3 work, and the profile is still mutated only via `brain consolidate --apply --confirm`."
This removes the stale claim rather than appending a self-contradicting line beneath it.

## Step 4 — Behavioural verification (manual, recorded — SC4/SC5)

Agent-prompt behaviour is not unit-testable (operator-accepted). Honest procedure, result recorded in STATE/changelog at land:
- **SC4 (graceful absence):** with no/empty `brain/profile.md` at the data-root, the wiring degrades silently — verified by inspection of the subsection's graceful-absence clause + (best-effort) a strategy-advisor run on the empty real root → no missing-file noise.
- **SC5 (read-back works):** seed a temp profile (e.g. `LINKEDIN_STUDIO_DATA=<tmp> brain init` + a couple of hand-written facts) and run the agent → confirm ≥1 fact surfaces as tested evidence, counter-pressured, not parroted. If the harness cannot fully exercise a per-call data-root, record that honestly and fall back to inspection of the wiring + framing (do NOT overclaim a behavioural pass we did not run).

## Step 5 — Land

STATE "Telling" + "👉 NESTE" updated (S3a done → S3b/c/d remain); commit (Conventional, `[skip-docs]` on the code/feat parts; brief+plan+consolidation-loop are docs → judge suffix per the mixed change); **push only inside the window** (`date '+%u %H:%M'` first; `origin` is the PUBLIC `open/` remote → confirm before push). No version bump (S3a is additive within v0.5.2 dev; a release bump is a separate decision).

## Verification (testable)

| SC | Check | Command | Expected |
|----|-------|---------|----------|
| SC1+SC2 | reader wired + literal | `bash scripts/test-runner.sh` | Section 16d Check B passes |
| SC3 | gate green, brain floor intact | same run | 95/0/0; `BRAIN_TESTS_FLOOR=82` unchanged; hook suite (separate runner) untouched |
| SC6 | assertion floor honoured | same run | "assertion-count anti-erosion: 95 … >= floor 80" passes |
| (red proof) | failing-test-first | gate run BETWEEN Step 1 and Step 2 | exit 1, exactly Check B fails |
| SC4 | graceful absence | manual run, empty root | clean output, no missing-file noise |
| SC5 | read-back | manual run, seeded profile | ≥1 fact surfaced as evidence-to-test (or honest fallback recorded) |

## Risks

- **R1 — vacuous lint.** Mitigated by the 16c-style non-vacuity self-test with the legacy-path/lowercase decoy.
- **R2 — floor false-fail on fresh clone.** Mitigated: both new checks are deps-free (lift the deps-absent minimum), so +2 → 80 is the correct floor; not pinned to deps-present TOTAL.
- **R3 — behavioural overclaim.** Mitigated by Step 4's honest-fallback clause (verifiseringsplikt): never report a behavioural pass we did not run.
- **R4 — stale doc claim.** `consolidation-loop.md:67` ("no reader yet") becomes false once S3a lands; **no automated guard protects `docs/`** (Section 11 scans non-docs dirs + only the significance column — verified). Mitigated by EDITING :67 to the true partial state (one reader, broader consumption deferred) within the "Honest limits" framing — by discipline, not by a blind append.
