# Plan — SB-S3d: wire the operations centre (`operations.md` READER + dated anchor)

> Brief: `docs/second-brain/brief-sb-s3d.md`. Slice: SB-S3d (ops centre only; hygiene+reconciliation → SB-S3e).
> Operator confirmed (2026-06-23): **Split, ops-centre first.** Recommendations pending the go-gate: **Option B** (enrich seed) + **advisory** deprecation + **one reader** (`strategy-advisor`).
> Order is TDD: the failing lint/seed checks land BEFORE the agent/seed edits (iron law).

## Goal

`strategy-advisor` reads `brain/operations.md` and honours the **dated frozen-past-self anchor** — preferring it over older/contradicting profile facts (the guard, advisory) — guarded by a deterministic wiring lint, with an enriched seed that ships the dated-anchor convention. No engine write, no post-silo contact, no parser.

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

| File | Change | Why |
|------|--------|-----|
| `scripts/test-runner.sh` | **NEW Section 16e** (2 unconditional checks) + bump `ASSERT_BASELINE_FLOOR` 80→82 + extend the Section-18 history comment + header enumeration (`:33-36`) | SC1/SC2/SC5 — the TDD anchor |
| `scripts/brain/tests/scaffold.test.ts` | **NEW test()** asserting the dated-anchor seed convention (Option B) | SC3 — fails first (RED) |
| `scripts/brain/src/scaffold.ts` | enrich `operationsSeed()` (`:52-64`): dated-anchor convention + light Plans/Ideas guidance | SC3 — makes the guard operational |
| `agents/strategy-advisor.md` | add `brain/operations.md` to Step 0 Load-Context (`:41-48`) + a consumption subsection after `:63` (anchor authoritative + `deprecates older inferences` literal + graceful absence) | SC1/SC2/SC6/SC7 — the wiring |
| `docs/second-brain/architecture.md` | edit the SB-S3 build-row (`:80`) to the post-split truth (S3d ✅ ops · S3e remains hygiene+reconciliation) | doc reconciliation |

**Not touched (scope fence):** any post silo (`hooks/scripts/state-updater.mjs`, `## Recent Posts`, `content-history*`, analytics, tributary JSON) — that is **S3e** · `consolidate.ts`/`assemble.ts`/`ingest.ts`/`cli.ts` (no engine/graph change; advisory guard only) · `consolidation-loop.md` (profile-motor contract, not this loop) · `content-planner.md` (second reader, not S3d) · CLAUDE.md agent table (count/model lint unaffected — no new agent, frontmatter unchanged) · Section 16d (the profile reader stays exactly as-is).

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

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

- **Literals:** `OPS_LIT='brain/operations.md'`, `ANCHOR_LIT='deprecates older inferences'`.
- **`ops_reader_wired()`** — wired iff BOTH literals present. Pin the body VERBATIM from 16d (`test-runner.sh:802`), `grep -qF` (fixed-string — `OPS_LIT` contains `.`/`/`, so `-F` is mandatory or the `.` is a regex wildcard): `echo "$1" | grep -qF "$OPS_LIT" && echo "$1" | grep -qF "$ANCHOR_LIT"` (echo twice — grep consumes stdin).
- **Check A (self-test, unconditional):** a fully-wired probe (`"reads brain/operations.md; the anchor deprecates older inferences"`) MUST be detected; three under-wired probes MUST be rejected:
  1. `reads brain/operations.md but never says the anchor outranks anything` (file lit, missing anchor lit)
  2. `the anchor deprecates older inferences but names no source file` (anchor lit, missing file lit)
  3. `reads brain/profile.md and tests the evidence` (**sibling-file decoy** — names the OTHER brain file + no anchor lit; must NOT match either operations literal)
  → `pass`/`fail` "ops-reader self-test: full wiring detected; 3 under-wired forms rejected (incl. sibling-file `brain/profile.md` decoy)".
- **Check B (real-file grep, unconditional):** `grep -qF "$OPS_LIT" agents/strategy-advisor.md && grep -qF "$ANCHOR_LIT" agents/strategy-advisor.md` → `pass` "strategy-advisor.md wired to brain operations reader (names '…operations.md', frames 'deprecates older inferences')" else `fail`.
- **Header enumeration (`:33-37`):** insert the 16e clause **immediately after** "…with a non-vacuity self-test) in Section 16d;" (mid-`:36`), BEFORE the "the assertion-count anti-erosion floor (SC6) in Section 18. All are live below (Sections 8–18)." tail — preserving the 16d → 16e → 18 ordering. Clause: "the brain operations-reader guard (SB-S3d: `strategy-advisor` names `brain/operations.md` AND carries the frozen-past-self literal `deprecates older inferences`, with a non-vacuity self-test) in Section 16e;". The "Sections 8–18" range is unchanged (16e is in range).
- **Floor (`:905-913`):** `ASSERT_BASELINE_FLOOR=80` → `82`; extend the history comment: "+2 for SB-S3d's two UNCONDITIONAL Section-16e checks (ops-reader self-test + strategy-advisor ops-wiring grep) = 82." Both new checks are deps-free → they lift the deps-absent minimum, so +2 is correct (NOT pinned to deps-present TOTAL).

**Non-collisions (verify with plan-critic):** new prose carries no algorithm-magnitude token (Section 8 stat-consistency safe) and no `Significant?(` column (Section 11 render-chain safe); 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; Section 16d's two literals remain present in the (unchanged) profile subsection.

## Step 2 — (RED) Brain seed test (Option B)

In `scripts/brain/tests/scaffold.test.ts`, add ONE new `test()` (bumps the suite count → floor +1) asserting the enriched seed — e.g. `test("operations.md ships a dated frozen-past-self anchor convention", ...)`:
- `initBrain()`, read `brain/operations.md`.
- **The single RED-bearing assertion** is the **date convention**, which is verified ABSENT from the current seed (`grep "As of" scaffold.ts` = 0 → the test fails first): `assert.match(ops, /_As of YYYY-MM-DD:_/)` (or `/As of/` — but the underscore form is the exact seed literal).
- Do NOT assert `deprecates older inferences` as the failing literal — it ALREADY ships in the current seed comment (`scaffold.ts:58`, verified `grep -c` = 1), so it passes pre-enrichment and would make the RED gate vacuous. It may be asserted only as an explicit **non-RED companion** (documented as such), or omitted.
- Keep matching the existing `Who I am now` / `## Plans` / `## Ideas` anchors so the new case is a strict superset of the old guarantee.
- Leave the existing 5 tests untouched (the no-clobber test at `:70-77` already proves idempotency of the new seed).
- **Baseline (verified live, 2026-06-23):** brain suite reports `tests 113` and the gate reports 95 checks — so the floor becomes **recorded + 1 = 114** (not a hardcoded guess) and the gate goes 95→97.

**RED proof (recorded):** `(cd scripts/brain && npm test)` against the un-enriched seed → the new test FAILS (no `_As of` in the seed yet). `bash scripts/test-runner.sh` → Section 16e Check A passes, **Check B FAILS** (agent not yet wired), floor self-count passes (TOTAL 96 ≥ 82). Capture this failing output in STATE/changelog as the iron-law artifact. This is the required failing state.

## Step 3 — (GREEN) Enrich the seed

In `scripts/brain/src/scaffold.ts`, rewrite `operationsSeed()` (`:52-64`, the full function incl. the closing brace at `:64` — body string is `:53-63`) to ship the dated-anchor convention + light guidance. **Load-bearing:** the rewrite MUST retain `## Who I am now`, `## Plans`, and `## Ideas` verbatim — the existing anchor test `scaffold.test.ts:48-58` asserts all three, so dropping one regresses that test. Idempotency is untouched: `initBrain` **existence-skips** (writes a seed only when the file is absent, `scaffold.ts:105-113`), so the new seed string never reaches an already-created `operations.md`. Sketch:

```ts
function operationsSeed(): string {
  return `# Operations

> The operations centre — where you're headed now, what you're working on, what you might do next.
> User-authored: the brain motor never writes here (a \`brain init\` re-run never clobbers your edits).

## Who I am now (anchor)

<!-- Frozen-past-self guard: a DATED, user-authored "where I'm headed now" statement.
     It DEPRECATES older inferences — when a profile.md fact predates or contradicts this
     anchor, the anchor wins. Re-date it when your direction shifts. -->

_As of YYYY-MM-DD:_ <one or two sentences on your current direction>

## Plans

<!-- Active commitments — what you're working toward now. One per line. -->

## Ideas

<!-- Parking lot — possible content/strategy ideas, not yet commitments. One per line. -->
`;
}
```

**GREEN gate (brain):** `(cd scripts/brain && npm test)` → new test passes; suite reports `tests 114` (verified baseline 113 + 1); bump `BRAIN_TESTS_FLOOR` 113→**114** (`test-runner.sh:716`) + extend its inline breakdown comment ("+ SB-S3d 1 [scaffold dated-anchor seed]"). The no-clobber test (`scaffold.test.ts:70-77`) runs in this same gate and must stay green.

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

1. **Step 0 Load Context** (`strategy-advisor.md:41-48`): insert one line INSIDE the fenced block, immediately before the closing ``` fence (`:48`), after the profile line (`:47`):
   `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/brain/operations.md → the operations centre: dated "who I am now" anchor + plans + ideas (user-authored)`
2. **New subsection** immediately after the profile subsection's graceful-absence line (`:63`), e.g. `### Consuming the operations centre (brain/operations.md)`:
   - **Anchor is authoritative:** "The `## Who I am now` anchor is the user's own dated declaration of current direction. It is authoritative and **deprecates older inferences**: when a `brain/profile.md` Dynamic fact predates the anchor's date or contradicts its stated direction, prefer the anchor and flag the older fact as possibly-stale — do not parrot it." ← carries the exact literal `deprecates older inferences`.
   - **Inversion (make the contrast explicit):** "Unlike profile facts, which you treat as **evidence to TEST**, the anchor is user-declared direction, not an inference to challenge — honour it. (It is direction, not praise; honouring it never licenses flattery.)"
   - **Plans / Ideas:** "`## Plans` are active commitments (current intent); `## Ideas` are a parking lot (suggestions, not commitments) — weigh accordingly."
   - **Graceful absence:** "If `brain/operations.md` (or its anchor) is missing or empty, proceed silently on the other context sources — no error, no note about a missing ops centre."

**GREEN gate (lint):** `bash scripts/test-runner.sh` → Section 16e Check A + Check B pass, FAIL=0, **97/0/0**, ASSERT floor 82 honoured. (16d still green — profile literals untouched.)

## Step 5 — Doc reconciliation

Edit `architecture.md:80` (the SB-S3 build-row) so the post-split status is true: S3a/b/c ✅ as today, then "**S3d ✅ ops centre** — `operations.md` is a read tributary; `strategy-advisor` honours the dated anchor that *deprecates older inferences* (advisory). **S3e remains** — dead `content-history.md` retirement + triple-post (post-tracking↔published↔analytics) reconciliation." No other architecture edit (the §59 guard spec already stands; the §80 row was the stale claim).

## Step 6 — Behavioural verification (manual, recorded — SC6/SC7)

Agent-prompt behaviour is not unit-testable (operator-accepted). Honest procedure, result recorded in STATE/changelog at land:
- **SC6 (graceful absence):** with no `brain/operations.md` at the data-root, the wiring degrades silently — inspect the subsection's graceful-absence clause + (best-effort) a `strategy-advisor` run on an empty root → no missing-file noise.
- **SC7 (guard fires):** seed a temp root (`LINKEDIN_STUDIO_DATA=<tmp> brain init`), write a dated anchor (`_As of 2026-06-20:_ pivoting from X to Z`) and a contradicting older profile fact (`leans toward X`, `last_seen` < 2026-06-20) → run the agent → confirm it prefers the anchor (Z) and flags the older X fact as deprecated, not parroted. If the harness cannot exercise a per-call data-root, record that honestly and fall back to inspection of the wiring + framing (no overclaim).

## Step 7 — Land

STATE "Telling" + "👉 NESTE" updated (S3d done → **S3e** is the new last slice); commit. Mixed change: the seed/agent/lint are code → `[skip-docs]` judged per the repo convention; brief+plan+architecture are docs. **Push only inside the window** (`date '+%u %H:%M'` first; `origin` = PUBLIC `open/` → confirm before push). No version bump (S3d additive within v0.5.2 dev; release is a separate decision).

## Verification (testable)

| SC | Check | Command | Expected |
|----|-------|---------|----------|
| SC1+SC2 | ops reader wired + literal | `bash scripts/test-runner.sh` | Section 16e Check B passes |
| SC3 | seed convention | `(cd scripts/brain && npm test)` | new test passes; floor 114 |
| SC4 | gate green | `bash scripts/test-runner.sh` | 97/0/0; 16d still green; hook suite (separate runner) untouched |
| SC5 | assertion floor | same run | "anti-erosion: 97 … >= floor 82" passes |
| (red proof) | failing-test-first | gate run BETWEEN Step 1/2 and Step 3/4 | exit 1: scaffold test fails (Step 2), 16e Check B fails (Step 1) |
| SC6 | graceful absence | manual, empty root | clean output, no missing-file noise |
| SC7 | guard fires | manual, seeded anchor+stale fact | anchor wins, older fact flagged (or honest fallback recorded) |

## Risks

- **R1 — vacuous lint.** Mitigated by the 16d-style non-vacuity self-test with the sibling-file (`brain/profile.md`) + "outranks anything" decoys.
- **R2 — floor false-fail on fresh clone.** Both new lint checks are deps-free (lift the deps-absent minimum) → +2 → 82 correct, not pinned to deps-present TOTAL. The brain floor (114) only checked when the brain suite runs (deps-present); a fresh clone warn-skips it (unchanged mechanism).
- **R3 — behavioural overclaim.** Step 6 honest-fallback clause (verifiseringsplikt): never report a guard-fires pass not actually run.
- **R4 — anti-sycophancy weakening.** The anchor's "authoritative" stance must not read as "flatter the user." Mitigated by the explicit "direction, not praise" clause + the profile `evidence to TEST` stance staying verbatim (Section 16d unchanged, still grepped).
- **R5 — seed breaks idempotency.** Mitigated: the enriched seed is still a static string; `scaffold.test.ts:70-77` (no-clobber) stays green and is run in Step 3's GREEN gate.
- **R6 — stale §80 doc.** The build-row claim "S3d remains (ops + hygiene + reconciliation)" becomes false at land; mitigated by EDITING `:80` to the split truth (no automated guard covers `architecture.md`; Section 11 scans non-docs dirs only).

## Plan-critic — folded

`voyage:plan-critic` (no blockers; 3 major + 3 minor) folded above:
- **Major-1 — vacuous brain RED:** the seed already ships `deprecates older inferences` (`scaffold.ts:58`), so the RED-bearing literal must be the **date convention** `_As of YYYY-MM-DD:_` (verified absent). Folded into Step 2. ✅
- **Major-2 — unverified brain floor:** recorded the live count (`tests 113`) → floor = recorded + 1 = 114, not a hardcoded guess. Folded into Step 2/3. ✅
- **Major-3 — `grep -qF` fixed-string:** pinned the `ops_reader_wired()` body verbatim from `test-runner.sh:802` (`-F` mandatory — `OPS_LIT` has `.`/`/`). Folded into Step 1. ✅
- **Minor-1 — header range:** corrected to `:33-37`; the 16e clause inserts after "…in Section 16d;", before the SC6/Section-18 tail. Folded into Step 1. ✅
- **Minor-2 — function range:** cite `operationsSeed()` as `:52-64` incl. the closing brace (body `:53-63`). Folded into Step 3. ✅
- **Minor-3 — retained anchors:** the three `##` anchors are load-bearing for `scaffold.test.ts:48-58`; the rewrite retains them verbatim. Folded into Step 3. ✅

Cross-checks the critic confirmed sound: floor arithmetic (ASSERT 80→82, BRAIN 113→114), the 3 decoys each miss ≥1 literal, the sibling-file decoy `brain/profile.md` cannot match `OPS_LIT`, Section 16d stays green (profile literals untouched; the new subsection adds a *second* `evidence to TEST` occurrence, harmless), and no collision with Sections 8/11/13.
