linkedin-studio/docs/second-brain/brief-sb-s3a.md
Kjell Tore Guttormsen d2ec7482be docs(linkedin-studio): SB-S3b brief + plan — supersede arm (light-Voyage hardened)
Second sub-slice of SB-S3 (after S3a's reader landed). Adds an operator-gated,
explicitly-signalled supersede op to the consolidation engine: a temporal-update
candidate retires a stale fact (status: superseded, re-minted to an archival id,
replaced in place, retained as audit) and installs the new winner under the
canonical key-id. Bounded to scripts/brain/ — pure, deterministic, fully
unit-testable.

Operator-confirmed scope: temporal-update→supersede ONLY · winner→dynamic
(re-earn) · minimal `supersedes?: string` · retain superseded as audit.

Light-Voyage-hardened:
- brief-reviewer APPROVE-WITH-FIXES — 8 FIX folded (the load-bearing one: id
  lifecycle → winner-takes-key-id, retired fact re-minted, so mintEntityId(key)
  always points at the live fact).
- plan-critic REVISE → 2 BLOCK + 6 FIX folded: applyDiff must REPLACE-IN-PLACE
  (not copy → would dup the canonical key-id); proposeDiff must value-guard the
  supersede fork (a re-sent stale signal would self-supersede every run); plus
  --gather active-only filter + decay excludes superseded (else retired facts
  leak back as live context / spam stale flags).
- scope-guardian ALIGNED (0 creep / 0 gap).

Also reconciles brief-sb-s3a.md's stale DRAFT status (S3a landed 4fa411f).
No code yet — awaiting go-before-code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4
2026-06-23 18:51:25 +02:00

79 lines
9.1 KiB
Markdown
Raw 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.

# Brief — SB-S3a: the first profile.md READER
> **Slice:** SB-S3a (first sub-slice of SB-S3, the cross-silo + ops-centre arc step).
> **Status:** LANDED `4fa411f` (2026-06-23) — gate 95/0/0, `ASSERT_BASELINE_FLOOR` 80. Light-Voyage-hardened (brief-review APPROVE · scope-guardian ALIGNED · plan-critic REVISE→folded). SC1/2/3/6 gate-proven; SC4 inspection-pass; SC5 (read-back) deferred to a reloaded session.
> **Predecessors:** SB-S0 (id/profile substrate) · SB-S1 (published-gold ingest) · SB-S2 (consolidation motor — *motor-only, no reader*).
## 1. Operator decision (2026-06-23)
SB-S3 is the arc's largest slice and was decomposed into four sub-slices (value-first / risk-managed): **S3a reader · S3b supersede · S3c cross-silo id-threading · S3d hygiene+ops**. The operator picked **S3a — the profile.md reader — first**, and within S3a the first reader is **one agent: `strategy-advisor`**, wired **READ-only**.
## 2. Why a reader, and why first
The whole arc was built *"so S3's reader inherits rich data"* (`consolidation-loop.md:67`). S2 grows `brain/profile.md` but **no agent or command consumes it today** — verified: 0 references to `brain/profile.md` in `agents/`/`commands/`; every "profile" hit points at the *legacy* flat `profile/user-profile.md`, not the SB-S0 two-layer brain profile. The reader is the deferred payoff of S0→S2: it turns a motor-only system into one that **feeds content generation** — the first end-to-end proof of capture → consolidate → read-back-into-generation.
It is first because it has **no dependency on id-threading** (it reads the profile *facts*, not the cross-silo graph), it is **additive / non-breaking**, and it is the **lowest-risk** of the four (read-only consumption of an existing file).
## 3. Scope — what is IN (S3a)
1. **Wire `strategy-advisor` to read `brain/profile.md`.** Add the brain profile to the agent's existing **Step 0: Load Context** list:
`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/brain/profile.md`
The agent reads the markdown in-context and reasons over it — no parser, consistent with the arc thesis (Claude *is* the retrieval engine; `architecture.md:14`).
2. **Consumption contract (how the agent uses it).** A short subsection in the agent prompt that tells it:
- The profile has two layers — `## Static` (stable, high-confidence) and `## Dynamic` (emerging) — and each fact line carries `evidence_count` + `last_seen`. Higher `evidence_count` / more recent `last_seen` = stronger/fresher signal; weight accordingly.
- **Anti-sycophancy (binding, `architecture.md:58`):** treat every profile fact as *evidence to TEST, not flatter*. Counter-pressure it against analytics/state each time it is used (e.g. "the profile says you lean X, but your last imports show Y — test that"). The profile informs, it never dictates or flatters.
3. **Graceful absence.** `brain/profile.md` does not exist until `brain init` runs (fresh installs have no brain). The agent must **degrade silently** when the file is missing or empty — no error, no "I couldn't find your profile" noise; it simply proceeds on its other context sources.
4. **A deterministic wiring test** in the structure-lint / `test-runner.sh` gate: two UNCONDITIONAL assertions — (a) `strategy-advisor.md`'s context-load declares `brain/profile.md`; (b) the agent carries the anti-sycophancy sentinel **literal `evidence to TEST`** (exact-literal grep + a non-vacuity self-test, per the repo lint idiom in `test-runner.sh` Sections 13/16c — loose patterns match vacuously). This is the TDD anchor (see §6) and protects the wiring against future agent-file edits.
- **Assertion-floor lockstep (binding):** the two new checks are UNCONDITIONAL, so `ASSERT_BASELINE_FLOOR` in `test-runner.sh` (Section 18; currently **78** — verify at plan time) must be bumped by exactly +2 → **80** in the same change, per the assertion-erosion guard. Do NOT pin to a deps-present TOTAL.
5. **Doc/count reconciliation.** Update the binding counts/docs touched (CLAUDE.md agent table note if needed, STATE telling, `consolidation-loop.md` reader-status line: "S3a wires the first reader — `strategy-advisor`").
## 4. Non-goals — what is OUT (deferred to later S3 sub-slices)
- **More than one reader.** `content-optimizer` is the obvious second reader and the pattern generalizes trivially, but S3a wires **exactly one** agent — the smallest valuable proof. Follow-on readers are separate work.
- **A hook-level / `user-prompt-context.mjs` digest reader.** A broad "inject a profile digest into every prompt" reader is attractive but carries a twin `.mjs` parser (twin-sync with `profile.ts`) + per-prompt context cost — *not* a good first proof. Deferred.
- **Cross-silo id-threading (S3c).** The reader consumes profile facts, not the post↔specific↔trend↔analytics graph. No `mintEntityId` threading here.
- **Supersede arm (S3b)**, **content-history retirement / triple-post reconciliation + operations.md (S3d)** — separate sub-slices.
- **Any WRITE to `brain/profile.md`.** The profile is mutated ONLY via `brain consolidate --apply --confirm` (operator-gated). S3a is strictly read-only; it adds no write path.
- **A new parser or new `.mjs`.** S3a is markdown-agent edits + a lint assertion only.
## 5. Boundaries / invariants (must hold)
- **READ-only** — S3a never writes the profile; the `--apply --confirm` gate stays the sole writer.
- **Anti-sycophancy is a built-in default, not a toggle** (`architecture.md:58`).
- **Provenance spine intact** — the profile already encodes provenance; the reader must not undermine the published-only learning guard.
- **No tributary schema changes** (that's S3c).
- **Fresh-clone safe** — missing brain → silent degrade, no crash, no nag from the agent.
- **TDD iron law** — the failing wiring test lands before the agent edit.
## 6. Success criteria (testable)
- **SC1 — wired:** `strategy-advisor.md` Step 0 context-load includes `brain/profile.md`. *(lint assertion, deterministic)*
- **SC2 — anti-sycophancy framed:** `strategy-advisor.md`'s profile-consumption subsection contains the exact durable sentinel literal **`evidence to TEST`**, asserted by an exact-literal grep + a non-vacuity self-test (repo idiom, `test-runner.sh` §§13/16c). *(lint assertion)*
- **SC3 — gate green:** `scripts/test-runner.sh` (the structure lint) stays green with the new assertions; its `BRAIN_TESTS_FLOOR` (82) is unchanged — no brain TS is touched. The hook suite (~136) runs under the **separate** `node --test hooks/scripts/__tests__/*.test.mjs` runner (NOT part of `test-runner.sh`) and is untouched because no hook code path changes.
- **SC6 — assertion floor honoured:** `ASSERT_BASELINE_FLOOR` bumped by exactly the number of new unconditional checks (+2 → 80); the gate's self-count check passes.
- **SC4 — graceful absence (manual/behavioural):** running `strategy-advisor` with **no** `brain/profile.md` produces normal output, no error/noise about the missing file.
- **SC5 — read-back works (manual/behavioural):** running `strategy-advisor` with a **populated** `brain/profile.md` surfaces ≥1 profile fact as *evidence-to-test* in its recommendation, counter-pressured against analytics/state — not parroted.
## 7. Verification
- **Deterministic (gate):** SC1SC3 via the structure-lint assertion in `test-runner.sh`. `cd scripts/brain` not required — this is an agent-file + lint change; brain TS is untouched.
- **Behavioural (manual, documented):** SC4 + SC5 — the honest limit. Agent-prompt behaviour is not unit-testable (the plugin's command-testing workstream is still open). The plan must include a documented manual run: (a) empty-brain run → clean output; (b) populated-brain run (seed via `brain init` + a `--apply --confirm` cycle, or a fixture profile) → confirm a profile fact appears as tested evidence. Record the result in STATE/changelog at land.
## 8. Open questions for brief-review / the operator
1. **Agent choice:** `strategy-advisor` recommended; operator may redirect to `content-optimizer` (post-level grounding) — confirm at the brief gate.
2. **Lint location:** which existing lint file in the `test-runner.sh` gate hosts the SC1/SC2 assertion (plan resolves precisely).
3. **Behavioural test honesty:** is the documented manual verification (SC4/SC5) acceptable for S3a's land, given agent-behaviour is not unit-testable — or does the operator want a fixture-driven harness scoped in (larger)?
## 9. Brief-review (light-Voyage) — folded
`voyage:brief-reviewer` verdict: **APPROVE** (scope crisp, non-goals correct, invariants inherited, deterministic-vs-behavioural split honest). Three [FIX]es folded above:
- SC3 no longer conflates two runners (brain-82 floor in `test-runner.sh` vs the separate hook runner). ✅
- `ASSERT_BASELINE_FLOOR` lockstep bump (+2 → 80) made binding in §3.4 + SC6. ✅
- SC2 pinned to the exact literal `evidence to TEST` + non-vacuity self-test (anti-vacuity idiom). ✅
Reviewer confirmed: `strategy-advisor` is the better first reader than `content-optimizer`; SC4/SC5 manual split is correct; do NOT scope a behavioural harness into S3a (keeps "smallest valuable proof").