Slice 2 of fix #2 (kilde-så-draft): the per-edition bridge between the global specifics-bank (slice 1) and a draft. In scripts/specifics-bank: - binding.ts — livedSpecifics slot-map model + validateBinding, the «vaghet avvises» gate (G4). BLOCK on unresolved / dangling specific-id / unjustified abstrakt; WARN on an unverified-number-backed slot (regel 6/7); PASS otherwise. - kilder.ts — renderKilder, the read-only NN-kilder.md sources ledger, rendered deterministically from the slot-map + bank (coverage via validateBinding, so the artifact and the skeleton-gate cannot disagree). - cli.ts — validate-binding (exit 1 = BLOCK) + render-kilder subcommands. edition-state schema gains articles.NN.livedSpecifics ({ slots, status }) + _doc. Design record + the Step 2 research re-scope spec: docs/fix2/slice2-binding.md. Wiring into newsletter.md (Step 1.5 elicitation + Step 2.5 gate + pipeline 17->18) is slice 3 — not in this commit. Tests 28/28 (specifics-bank; +9 binding +4 kilder), tsc clean, plugin gate 83/0/0 unaffected, gitleaks clean on all 8 files. [skip-docs] internal plumbing — no command/agent/pipeline surface change until slice 3 wires it; documenting it in the user-facing root CLAUDE.md/README now would overclaim unreachable functionality. Docs ARE updated where they belong: the design record docs/fix2/slice2-binding.md, scripts/specifics-bank/README.md, and the edition-state _doc. Root-doc update lands with the slice-3 wiring feat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| src | ||
| tests | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
specifics-bank — the lived-specifics inventory (Fix #2, kilde-så-draft)
A deterministic, topic-tagged store of the operator's real, un-generatable raw material — the specific measured number, the named (anonymized-but-real) case, the thing that actually broke, the unfashionable opinion privately held, the moment they changed their mind. The point (retning §3, dream-spec «Lived-Specifics Extraction»): sourcing, not styling, is where authenticity is won. Drafts must draw from real inventory instead of inventing plausible filler, so every downstream voice gate polishes grounded content, not hollow content.
This is the store half (deterministic + tested). The elicitation half — a guided
interview that refuses vague answers (escape hatches: mark a slot abstrakt or ekstern)
— lives in the command layer (/linkedin:newsletter Step 1.5, slice 3). The bank never
holds AI-generated lived experience; every content is the operator's own words.
Where the bank lives
${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/specifics-bank/specifics-bank.json
It sits in the per-user data dir (M0 data-path convention), outside the plugin install, so it survives plugin upgrades and reinstalls. It is the first concrete tributary of the broader profile-evolution / "second brain" architecture.
Setup
cd scripts/specifics-bank && npm install
Use
# Surface inventory the operator already has for an edition's topics
node --import tsx src/cli.ts query --tags "saksbehandling,produktivitet"
# Fold newly-elicited material back into the bank
node --import tsx src/cli.ts add --type number \
--content "Saksbehandleren sparte to dager i uka." \
--tags "saksbehandling,produktivitet" --source "seres/05"
# Inspect the whole bank
node --import tsx src/cli.ts list
Flags: --bank <path> overrides the default location; --json emits machine output for
query/list. add defaults --verification to unverified (numbers stay
guilty-until-checked — regel 6/7) and --source to manual.
--type ∈ number | named-case | what-broke | contrarian | mind-change | other.
Model
- Dedupe key =
sha256(normalizeContent(content)).slice(0,12)(lowercase + trimmed + whitespace-collapsed). Re-capturing the same material under a new edition's topic does not add a second entry — itstopicTagsare unioned in, enriching the record. - Query returns active specifics whose tags overlap, ranked by overlap (desc) then
recency (
capturedAtdesc); archived material is kept but never offered. - Content is stored verbatim; only the id is normalized.
Per-edition binding (slice 2)
The bank is the global inventory; an edition binds its load-bearing claims to real material before any prose exists. That bridge lives here too:
binding.ts— the per-edition slot-map model +validateBinding, the «vaghet avvises» gate (a slot must bespecific/abstrakt/ekstern; anunresolvedslot, a dangling specific id, or an unjustifiedabstrakt→ BLOCK; an unverified-number-backed slot → WARN per regel 6/7).kilder.ts—renderKilder, the read-onlyNN-kilder.mdsources artifact, rendered deterministically from the slot-map + the bank.
# Gate one edition's binding (exit 1 = BLOCK)
node --import tsx src/cli.ts validate-binding --edition "<abs>/linkedin/edition-state.json"
# Render the sources ledger
node --import tsx src/cli.ts render-kilder --edition "<abs>/linkedin/edition-state.json" --out "<abs>/linkedin/NN-kilder.md"
Design record + the Step 2 research re-scope spec: docs/fix2/slice2-binding.md.
The elicitation interview that POPULATES the slot-map (Step 1.5) is slice 3.
Test
npm test # node:test, deterministic (callers supply capturedAt)
npm run build # tsc — must stay clean