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>
87 lines
3.8 KiB
Markdown
87 lines
3.8 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
cd scripts/specifics-bank && npm install
|
|
```
|
|
|
|
## Use
|
|
|
|
```bash
|
|
# 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 — its `topicTags` are unioned in, enriching the record.
|
|
- **Query** returns active specifics whose tags overlap, ranked by overlap (desc) then
|
|
recency (`capturedAt` desc); 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 be `specific` / `abstrakt` / `ekstern`; an
|
|
`unresolved` slot, a dangling specific id, or an unjustified `abstrakt` → BLOCK;
|
|
an unverified-number-backed slot → WARN per regel 6/7).
|
|
- `kilder.ts` — `renderKilder`, the read-only `NN-kilder.md` sources artifact,
|
|
rendered deterministically from the slot-map + the bank.
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
npm test # node:test, deterministic (callers supply capturedAt)
|
|
npm run build # tsc — must stay clean
|
|
```
|