linkedin-studio/docs/second-brain/plan-sb-s3d.md
Kjell Tore Guttormsen 0061bf2bb5 docs(linkedin-studio): SB-S3d brief + plan — ops centre (operations.md reader, light-Voyage hardened)
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
2026-06-23 21:13:50 +02:00

141 lines
16 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.

# 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 818)." 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 818" 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.