@lorekit/cli 1.69.0 → 1.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -210,6 +210,27 @@ itself:
210
210
  A revoked token is a **failure**, not a warning: fix it by creating a new token
211
211
  and running `lorekit install --force`, which offers to replace the stored one.
212
212
 
213
+ ### `lorekit update`
214
+
215
+ Offline refresh of the bundled skills (and their hook command string) to the
216
+ version shipped with the running CLI — the fix `doctor`'s outdated-skill
217
+ warning and the SessionStart drift nudge (`updates.notify`) both point at.
218
+ Fully offline: the shipped skill source travels in the same npm tarball as
219
+ this CLI, so "installed vs shipped" is a filesystem version compare, never a
220
+ network call.
221
+
222
+ ```bash
223
+ lorekit update # refresh every scope that already has an install
224
+ lorekit update --check # dry run: report drift, write nothing
225
+ lorekit update --project # only the project (.claude/skills) install
226
+ lorekit update --global # only the global (~/.claude/skills) install
227
+ ```
228
+
229
+ `update` never CREATES a fresh install — that stays `install`'s job, so a
230
+ scope with nothing installed is reported and left alone. Refreshing prunes
231
+ any file the shipped skill no longer ships before re-copying, so a
232
+ dropped rule file doesn't silently outlive the version that removed it.
233
+
213
234
  ### `lorekit list` (alias `ls`)
214
235
 
215
236
  Shows the lessons that apply to **where you are** — the scopes `deriveScope`
@@ -1110,8 +1131,8 @@ also returns their headroom against the plan's memory cap.
1110
1131
  | Flag | Meaning |
1111
1132
  |------|---------|
1112
1133
  | `-d, --dir <path>` | Target project root (default: cwd) |
1113
- | `--project` | Install into this repo: `.claude/skills` + `.mcp.json` (`install`; default) |
1114
- | `--global` | Install for every project: `~/.claude/skills` + `~/.claude.json` (`install`) |
1134
+ | `--project` | Install into this repo: `.claude/skills` + `.mcp.json` (`install`; default). Narrows to the project scope (`update`) |
1135
+ | `--global` | Install for every project: `~/.claude/skills` + `~/.claude.json` (`install`). Narrows to the global scope (`update`) |
1115
1136
  | `-e, --endpoint <url>` | LoreKit MCP endpoint |
1116
1137
  | `-t, --token <token>` | LoreKit token |
1117
1138
  | `--mode <mode>` | Memory mode override for `doctor`: `off` / `local` / `remote` |
@@ -1125,6 +1146,7 @@ also returns their headroom against the plan's memory cap.
1125
1146
  | `--mcp-json` | Also write a committable project `.mcp.json` (auth via `${LOREKIT_TOKEN}`, no embedded token) for Claude Code on the web (`install`) |
1126
1147
  | `--force` | Overwrite existing skill files (`install`) |
1127
1148
  | `--deep` | Write/read/delete round-trip (`doctor`) |
1149
+ | `--check` | Dry run: report skill-install drift without writing anything (`update`) |
1128
1150
  | `--json` | Machine-readable output (`list` / `search` / `show` / `stats` / `scopes` / `diff` / `tree` / `lint` / `dedupe` / `obligations` / `invariants candidates` / `link` / `purge` / `purge-expired`) |
1129
1151
  | `--scope <scope>` | Restrict to a single scope (`list` / `search` / `stats` / `diff` / `tree` / `lint` / `dedupe` / `link`; default: all applicable). For `scopes` it is a **substring filter** over the inventory. On `show` / `write` it **names** the scope, overriding the positional |
1130
1152
  | `--key <key>` | Name the key outright (`show` / `write` / `link`) — the way to address a key that itself contains `::` |
package/bin/lorekit.mjs CHANGED
@@ -39,6 +39,11 @@ ${c.bold('Commands')}
39
39
  other servers, hooks, and settings are left untouched. Prompts
40
40
  project vs global; --project / --global choose non-interactively.
41
41
  doctor Verify the skill install, remote connectivity, token, and scope.
42
+ update Offline refresh of the bundled skills (and their hook command
43
+ string) to the version shipped with the running CLI — the fix
44
+ for doctor's outdated-skill warning. Never creates a fresh
45
+ install (that's \`install\`'s job); --project / --global narrow
46
+ to one scope; --check reports drift without writing anything.
42
47
  list (ls) List the memories that apply to the current directory, split into
43
48
  an Offline section (local .lorekit/ + ~/.lorekit/) and a Remote
44
49
  section (the hosted LoreKit API). Groups by scope (project/branch/repo/global).
@@ -169,6 +174,7 @@ ${c.bold('Options')}
169
174
  --force Overwrite existing skill files (install)
170
175
  --deep Do a write→read→delete round-trip (doctor)
171
176
  --telemetry Verify the OTLP export credential works (doctor)
177
+ --check Report drift without writing anything (update)
172
178
  --adapter <name> Host framework for hook: claude | cursor | codex
173
179
  --event <name> Host hook event (else read from stdin payload)
174
180
  -h, --help Show this help
@@ -193,6 +199,7 @@ ${c.bold('Examples')}
193
199
  npx @lorekit/cli install --global # set up memory for every project (~/.claude)
194
200
  npx @lorekit/cli uninstall --global # tear that global setup back down
195
201
  npx @lorekit/cli doctor --deep
202
+ npx @lorekit/cli update --check # report outdated skills without writing
196
203
  npx @lorekit/cli migrate --from .lore # preview a rename
197
204
  npx @lorekit/cli migrate --from .lore --to project --yes
198
205
  npx @lorekit/cli migrate --from .lorekit --to remote --yes # push local lore up
@@ -302,6 +309,32 @@ ${c.bold('Examples')}
302
309
  npx @lorekit/cli doctor --deep
303
310
  npx @lorekit/cli doctor --telemetry
304
311
  npx @lorekit/cli doctor --mode local
312
+ `,
313
+ update: `${c.bold('lorekit update')} — offline refresh of the bundled skills to the shipped version
314
+
315
+ ${c.bold('Usage')}
316
+ npx @lorekit/cli update [options]
317
+
318
+ Re-copies every bundled skill (force) into whichever scope(s) already have an
319
+ install, and refreshes that scope's hook command string — the same skill-copy
320
+ path and hook-command call \`install --force\` uses, so the two can never
321
+ disagree on what "installing a skill" means. Fully offline: the shipped skill
322
+ source travels in the same npm tarball as this running CLI, so "installed vs
323
+ shipped" is a filesystem version compare, not a network call. Never creates a
324
+ fresh install — that's \`install\`'s job — so a scope with nothing installed is
325
+ reported and left alone.
326
+
327
+ ${c.bold('Options')}
328
+ -d, --dir <path> Target project root (default: current directory)
329
+ --project Only refresh this project's install (.claude/skills)
330
+ --global Only refresh the global install (~/.claude/skills)
331
+ --check Dry run: report drift (installed vs shipped version
332
+ per skill) and write nothing
333
+
334
+ ${c.bold('Examples')}
335
+ npx @lorekit/cli update
336
+ npx @lorekit/cli update --check
337
+ npx @lorekit/cli update --global
305
338
  `,
306
339
  list: `${c.bold('lorekit list')} — list the memories that apply to the current directory ${c.dim('(alias: ls)')}
307
340
 
@@ -1090,6 +1123,8 @@ const KNOWN_FLAGS = [
1090
1123
  'clear-max-opened-count', 'off',
1091
1124
  // `obligations`
1092
1125
  'files', 'strict', 'strict-all',
1126
+ // `update`
1127
+ 'check',
1093
1128
  ];
1094
1129
 
1095
1130
  async function main() {
@@ -1102,7 +1137,7 @@ async function main() {
1102
1137
  const argv = process.argv.slice(2);
1103
1138
  const args = parseArgs(argv, {
1104
1139
  aliases: { d: 'dir', e: 'endpoint', t: 'token', y: 'yes', h: 'help', v: 'version' },
1105
- booleans: ['yes', 'force', 'deep', 'apply', 'help', 'version', 'global', 'project', 'no-hooks', 'mcp-json', 'no-origin', 'json', 'remote', 'local', 'link', 'archived', 'clear-ttl', 'telemetry', 'all', 'run', 'enabled', 'disabled', 'off', 'clear-min-age-days', 'clear-unseen-days', 'clear-max-seen-count', 'clear-max-read-count', 'clear-max-opened-count', 'strict', 'strict-all'],
1140
+ booleans: ['yes', 'force', 'deep', 'apply', 'help', 'version', 'global', 'project', 'no-hooks', 'mcp-json', 'no-origin', 'json', 'remote', 'local', 'link', 'archived', 'clear-ttl', 'telemetry', 'all', 'run', 'enabled', 'disabled', 'off', 'clear-min-age-days', 'clear-unseen-days', 'clear-max-seen-count', 'clear-max-read-count', 'clear-max-opened-count', 'strict', 'strict-all', 'check'],
1106
1141
  known: KNOWN_FLAGS,
1107
1142
  });
1108
1143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lorekit/cli",
3
- "version": "1.69.0",
3
+ "version": "1.71.0",
4
4
  "description": "Install the LoreKit shared-memory skill and run health checks for the LoreKit MCP server.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -18,7 +18,7 @@ argument-hint: '[scope-hint]'
18
18
  license: MIT
19
19
  metadata:
20
20
  author: mthines
21
- version: '1.0.0'
21
+ version: '1.1.0'
22
22
  workflow_type: shared-memory-grooming-and-consolidation
23
23
  tags:
24
24
  - lorekit
@@ -60,6 +60,14 @@ Findings are structural, not semantic — each names its rule:
60
60
  entry has no `kind`, so it renders as a raw JSON blob in every SessionStart
61
61
  digest instead of being excluded by `isGeneralLesson`. Set `--kind bus` or
62
62
  `--kind signal` on the record.
63
+ - **hidden-metadata** — the body carries an HTML comment, a leading front-matter
64
+ block, or a `key=value` header before the `#` title. This is the **repudiated
65
+ legacy `<!-- meta: seen_count=… status=… trigger-context=… -->` convention** the
66
+ `lorekit-setup` skill once prescribed and now forbids: an HTML comment renders to
67
+ nothing (so a human sees a lesson starting mid-sentence) while a baked-in
68
+ `seen_count` / `status` / `trigger` silently disagrees with the store's own
69
+ column / tag / field. **This is grooming's job to clean, not just report** — see
70
+ the cleanup step below.
63
71
 
64
72
  These are the cheapest wins and the least controversial, so clear them first.
65
73
  For each: either **fix it in place** (rewrite a too-short value into a real
@@ -68,6 +76,32 @@ updates in place) or, if the lesson is genuinely empty of meaning, **queue it fo
68
76
  removal** in the plan. `lint` exits non-zero while findings remain, which also
69
77
  makes it a clean CI gate — a passing `lint` is your Phase 6 proof.
70
78
 
79
+ ### Cleaning a `hidden-metadata` finding (fold, then strip)
80
+
81
+ A hidden-metadata lesson is **fixed in place**, never deleted for it (the prose is
82
+ usually a real lesson wearing a bad header). Recover the facts into their proper
83
+ homes, then strip the block:
84
+
85
+ 1. **Read the whole record** (`lorekit show <scope::key> --json`) so you see the raw
86
+ body, including the `<!--` block the SessionStart digest hides.
87
+ 2. **Fold each buried fact into its first-class home**, never back into prose:
88
+ - `seen_count=N` → **drop it entirely.** The column is the only true copy; a
89
+ baked number is stale. Do not try to "restore" it — the store's counter is
90
+ authoritative.
91
+ - `status=structural` / `status=promoted` → a `status::<value>` **tag** on the write.
92
+ (`status=active` is the default — drop it.)
93
+ - `trigger-context="…"` → the visible **`Applies when:`** line at the top of the
94
+ body, and/or the `trigger` **field** for the category.
95
+ - `expires=…` / `ttl=…` → a `ttl_days` on the write.
96
+ 3. **Rewrite the body as pure markdown** to the lesson shape (takeaway title →
97
+ `Applies when` → bold-labelled paragraphs), with the `<!--`/front-matter/header
98
+ gone, via a `memory.write` to the same `scope`+`key` (updates in place, carrying
99
+ the recovered tags/fields).
100
+ 4. **Re-lint** the record to confirm `hidden-metadata` no longer fires.
101
+
102
+ Do this whenever a pass surfaces the finding — that is how the store stops
103
+ re-teaching the pattern to the next agent that reads a neighbouring lesson.
104
+
71
105
  ## Phase 3 — Dedupe (read-only)
72
106
 
73
107
  ```bash
@@ -17,7 +17,7 @@ argument-hint: '[read|write] [scope-hint or lesson]'
17
17
  license: MIT
18
18
  metadata:
19
19
  author: mthines
20
- version: '1.0.0'
20
+ version: '1.1.0'
21
21
  workflow_type: shared-memory-intake-and-retrospective
22
22
  tags:
23
23
  - lorekit
@@ -1,31 +1,29 @@
1
1
  ---
2
2
  name: lorekit-setup
3
3
  description: >
4
- Sets up a self-improvement loop for a skill, workflow, or agent using LoreKit,
5
- so a host gets better across runs by reading its own accumulated lessons at
6
- the start of every run and hardening the proven ones into permanent rules.
7
- Designs the lessons loop (a fast episodic tier of LoreKit lessons, advisory-only;
8
- a slow procedural tier that promotes a recurring lesson into a host rule; and,
9
- for the rarer judgement-free, independently-checkable case, a third rung that
10
- compiles a recurring lesson into a mechanically-enforced CI invariant instead),
11
- chooses the lesson bucket (tag + key namespace) and scopes, and installs the
12
- entrenchment guards that stop a learning loop from reinforcing its own
13
- mistakes. Also covers the non-LLM case: giving a deterministic job (a GitHub
14
- Actions workflow, a cron script, a release pipeline) durable JSON state
15
- records so it knows what happened on its last run — flaky tests, a benchmark
16
- baseline, the last deployed SHA — in the same store agents read. Runtime
17
- reading and writing of lessons is the lorekit-memory skill; this is the
18
- authoring counterpart. Use when giving a host durable cross-run memory or
19
- wiring a lessons loop. Triggers on "set up memory for my skill", "add a
20
- self-improvement loop", "give my workflow memory", "make this learn from its
21
- mistakes", "self-improving memory", "memory in CI", "GitHub Actions state",
22
- "remember the last CI run", "/lorekit-setup".
4
+ Turns a skill, workflow, agent, or CI job into one that gets better across runs
5
+ and can PROVE it. Walks a six-step lifecycle — Find where a loop pays off, Wire
6
+ it from a ready-made recipe card, Seed it so it delivers on run one, Prove it
7
+ improved something, Maintain it so it does not rot, and Scale it to a team — with
8
+ a 15-minute quickstart that ends in a loop firing once, live. Under the hood it
9
+ is a LoreKit lessons loop: a fast episodic tier of advisory lessons read at the
10
+ start of every run and written on failure; a slow procedural tier that promotes a
11
+ recurring lesson into a permanent rule; and, for the rare judgement-free case, a
12
+ third rung that compiles a lesson into a mechanically-checked CI invariant. Also
13
+ covers the non-LLM case (durable JSON state records for a deterministic job) and
14
+ the guards that stop a learning loop from reinforcing its own mistakes. Runtime
15
+ reading and writing of lessons is the lorekit-memory skill; this is the authoring
16
+ counterpart. Use when giving a host durable cross-run memory or wiring a lessons
17
+ loop. Triggers on "set up memory for my skill", "add a self-improvement loop",
18
+ "give my workflow memory", "make this learn from its mistakes", "where should I
19
+ add a loop", "prove my loop is working", "self-improving memory", "memory in CI",
20
+ "GitHub Actions state", "remember the last CI run", "/lorekit-setup".
23
21
  user-invocable: true
24
22
  argument-hint: '[host-name]'
25
23
  license: MIT
26
24
  metadata:
27
25
  author: mthines
28
- version: '1.0.0'
26
+ version: '2.0.0'
29
27
  workflow_type: memory-loop-authoring
30
28
  tags:
31
29
  - lorekit
@@ -41,40 +39,74 @@ metadata:
41
39
 
42
40
  # LoreKit Setup
43
41
 
44
- Give a host durable cross-run memory. For a model-driven host that means a
45
- **self-improvement loop**: it reads its own accumulated lessons at the start of
46
- every run and hardens the proven ones into permanent rules, so it gets better the
47
- more it runs. For a deterministic host — a CI job — it means **state records**: it
48
- reads what was true at the end of its last run instead of rediscovering it.
49
-
50
- This is the **authoring** counterpart to `lorekit-memory`. `lorekit-memory` does
51
- the runtime read/write of individual lessons; `lorekit-setup` wires the durable
52
- memory that calls those primitives on a host's behalf. Both run on the same
53
- LoreKit store — over the `memory.*` MCP tools for agents, over the `lorekit` CLI
54
- or REST for jobs.
55
-
56
- ## The rungs of a lessons loop (in one screen)
57
-
58
- The runtime loop below is two tiers, fast and slow; a third, rarer rung sits past
59
- promotion, for the minority of recurring lessons that can be turned into a
60
- mechanically-checked rule instead of text a reader has to notice.
61
-
62
- | Rung | Mechanism | Changes behavior? | Advisory or enforced? |
63
- | ---- | --------- | ----------------- | ---------------------- |
64
- | **Fast (episodic)** | LoreKit lessons in a per-host bucket, read at the start of a run, written on failure | **No** — advisory input only | Advisory |
65
- | **Slow (procedural)** | A human-reviewed edit that hardens a recurring lesson into a host rule | **Yes** | Advisory (works only if the next reader notices it) |
66
- | **Compiled invariant** | A declarative `obligations-map.mjs` entry a CI gate checks against a changed-file set — see [rules/compiled-invariants.md](./rules/compiled-invariants.md) | **Yes** | Enforced once `gating`; most lessons never qualify |
67
-
68
- A recurrence gate connects the first two: a lesson that recurs (`seen_count >= 3`)
69
- or carries the `status::structural` tag becomes promotion-eligible. Entrenchment guards
70
- keep the fast tier from reinforcing its own wrong conclusions. The third rung has
71
- its own, stricter gate — the compilability test — and most promotion-eligible
72
- lessons stop at the second rung because they fail it.
73
-
74
- ## Pick the shape first
75
-
76
- Two kinds of host want memory, and they want a different record. Decide which
77
- before reading further:
42
+ Give a host durable cross-run memory, and make its improvement **visible**. For a
43
+ model-driven host that means a **self-improvement loop**: it reads its own
44
+ accumulated lessons at the start of every run and hardens the proven ones into
45
+ permanent rules, so it gets better the more it runs. For a deterministic host — a
46
+ CI job — it means **state records**: it reads what was true at the end of its last
47
+ run instead of rediscovering it.
48
+
49
+ This is the **authoring** counterpart to `lorekit-memory`. `lorekit-memory` does the
50
+ runtime read/write of individual lessons; `lorekit-setup` wires the durable memory
51
+ that calls those primitives on a host's behalf. Both run on the same LoreKit store —
52
+ over the `memory.*` MCP tools for agents, over the `lorekit` CLI or REST for jobs.
53
+
54
+ > A loop that nobody can see helping is indistinguishable from no loop. This skill
55
+ > treats "the host measurably got better" as the deliverable — not "a loop is
56
+ > wired." Every step below is judged against that.
57
+
58
+ ## The lifecycle (start here)
59
+
60
+ A working loop is six steps. Do them in order; each links to the rule that covers it.
61
+
62
+ | # | Step | What it answers | Read |
63
+ | - | ---- | --------------- | ---- |
64
+ | 1 | **Find** | *Where* does a loop actually pay off? (Do not guess — the data already knows.) | [rules/self-improvement-loops.md § Find where a loop pays off](./rules/self-improvement-loops.md#find-where-a-loop-pays-off) |
65
+ | 2 | **Wire** | Add the read/write steps with the least effort — copy a recipe card. | [templates/](./templates/) + [rules/self-improvement-loops.md](./rules/self-improvement-loops.md) |
66
+ | 3 | **Seed** | Deliver value on run **one**, before any lesson has been earned. | [rules/cold-start-seeding.md](./rules/cold-start-seeding.md) |
67
+ | 4 | **Prove** | Show the loop reduced a real failure — not just that lessons exist. | [rules/proving-improvement.md](./rules/proving-improvement.md) |
68
+ | 5 | **Maintain** | Keep it firing, keep the bucket clean, roll back a bad lesson. | [rules/loop-health.md](./rules/loop-health.md) |
69
+ | 6 | **Scale** | Turn one person's loop into a team practice that compounds. | [rules/team-and-portfolio.md](./rules/team-and-portfolio.md) |
70
+
71
+ If invoked with a `host-name`, set up memory for that host and walk this lifecycle.
72
+ Otherwise ask which skill / workflow / agent / job it is for, pick the shape from
73
+ [Pick the shape](#pick-the-shape), then walk it.
74
+
75
+ ## Quickstart (15 minutes, one loop firing once)
76
+
77
+ The shortest path from "I have a host" to "I watched the loop close." Do it against a
78
+ real host with `memory.*` connected.
79
+
80
+ 1. **Name the bucket.** From the host's name `<host>`: tag `loop::<host>-lessons`,
81
+ key namespace `<host>-lessons::<slug>`. One bucket per host.
82
+ 2. **Copy a recipe card.** Pick the archetype in [templates/](./templates/) that
83
+ matches your host (code-changing agent, reviewer/reconcile host, multi-step
84
+ orchestrator, CI job) and paste its read step at the host's start and its write
85
+ step at the host's existing failure points. The card already carries the
86
+ injection cap, the TTL, and the entrenchment defaults — do not hand-roll them.
87
+ 3. **Seed one real lesson** (optional but recommended) so run one is not empty —
88
+ [rules/cold-start-seeding.md](./rules/cold-start-seeding.md). Keep it to a
89
+ *handful*, hand-checked.
90
+ 4. **Fire it once, on purpose.** Trigger the failure the loop is meant to catch.
91
+ Confirm a lesson was written to the right scope with the right tag:
92
+
93
+ ```text
94
+ memory.list { scope: "<the scope you expect>", tags: ["loop::<host>-lessons"], limit: 10 }
95
+ ```
96
+
97
+ 5. **Run again and watch it read back.** Start a second run over the same
98
+ situation. Confirm the lesson surfaces in the read step and biases the run.
99
+ That is the loop closing — the whole point, demonstrated once.
100
+ 6. **Write down the success metric** you will use to prove it keeps helping —
101
+ [rules/proving-improvement.md](./rules/proving-improvement.md). The required one
102
+ is the *immunity re-challenge*: after you promote a lesson to a rule, the failure
103
+ signature should stop recurring.
104
+
105
+ That is a live, working loop. Everything below is depth on each step.
106
+
107
+ ## Pick the shape
108
+
109
+ Two kinds of host want memory, and they want a different record. Decide which first:
78
110
 
79
111
  | The host is… | Wants | Read |
80
112
  | ------------ | ----- | ---- |
@@ -85,20 +117,44 @@ before reading further:
85
117
  A host can want both, in separate buckets: the state record carries *what is true
86
118
  right now*, the lesson carries *what we learned about it*.
87
119
 
88
- ## Set up a loop (model-driven hosts)
120
+ ## The rungs of a lessons loop (in one screen)
89
121
 
90
- Follow [rules/self-improvement-loops.md](./rules/self-improvement-loops.md).
91
- It covers: when to add a loop (and when not to), the bucket convention (tag
92
- `loop::<host>-lessons` + key namespace), scope selection, the lesson schema, the
93
- read/write steps, the promotion gate, the entrenchment guards, a wiring
94
- checklist, and an interactive setup flow.
122
+ The runtime loop is two tiers, fast and slow; a third, rarer rung sits past promotion.
123
+
124
+ | Rung | Mechanism | Changes behavior? | Advisory or enforced? |
125
+ | ---- | --------- | ----------------- | ---------------------- |
126
+ | **Fast (episodic)** | LoreKit lessons in a per-host bucket, read at the start of a run, written on failure | **No** — advisory input only | Advisory |
127
+ | **Slow (procedural)** | A human-reviewed edit that hardens a recurring lesson into a host rule | **Yes** | Advisory (works only if the next reader notices it) |
128
+ | **Compiled invariant** | A declarative `obligations-map.mjs` entry a CI gate checks against a changed-file set — see [rules/compiled-invariants.md](./rules/compiled-invariants.md) | **Yes** | Enforced once `gating`; most lessons never qualify |
129
+
130
+ A recurrence gate connects the first two: a lesson that recurs (`seen_count >= 3`) or
131
+ carries the `status::structural` tag becomes promotion-eligible. Entrenchment guards
132
+ keep the fast tier from reinforcing its own wrong conclusions.
133
+
134
+ ## Non-negotiables (do not "optimize these away")
135
+
136
+ Five co-requirements make the difference between a loop that helps and one that
137
+ quietly rots or crowds out the run it was meant to help. Every recipe card bakes
138
+ them in; if you wire by hand, wire these too:
139
+
140
+ 1. **A lesson body is markdown for humans — nothing hidden.** No HTML comment, no
141
+ front-matter, no JSON blob, no `key=value` header. Every fact the store already
142
+ models goes in its own field, never restated in prose — see
143
+ [the body contract](#a-lesson-body-is-markdown-for-humans--nothing-hidden) below.
144
+ 2. **Every loop declares a per-host injection cap.** A loop reads *at most* N lessons
145
+ into a run (the cards default to a small N). Many cheap loops with no cap tax
146
+ every session's context until agents ignore injected lore entirely.
147
+ 3. **Every lesson expires.** `ttl_days` on the write, refreshed on recurrence, so a
148
+ stale belief decays instead of entrenching. Decay is automatic, never staffed.
149
+ 4. **Lessons are advisory, never auto-applied.** The only path from a lesson to
150
+ changed behavior is the human-reviewed slow tier.
151
+ 5. **Prove it, or it did not happen.** A loop earns its keep only when a real failure
152
+ stops recurring — [rules/proving-improvement.md](./rules/proving-improvement.md).
95
153
 
96
154
  ### A lesson body is markdown for humans — nothing hidden
97
155
 
98
- Whatever host you wire, its write step carries one non-negotiable contract: the
99
- `value` is **markdown a person reads**, and it carries no HTML comment, no
100
- front-matter, no JSON blob, and no `key=value` header. Every fact the store
101
- already models goes in its own write field, never restated in the prose:
156
+ The `value` is **markdown a person reads**, and it carries no hidden payload. Every
157
+ fact the store already models goes in its own write field:
102
158
 
103
159
  | Fact | Field, not prose |
104
160
  | ---- | ---------------- |
@@ -109,46 +165,27 @@ already models goes in its own write field, never restated in the prose:
109
165
  | Repo / branch / commit / PR | the `origin_*` fields |
110
166
  | What triggered the write | `trigger` |
111
167
 
112
- A hidden block does not just look untidy — it is *wrong*. A `seen_count=1` baked
113
- into prose is stale the first time the lesson recurs, while the column that
114
- governs promotion moves without it, and a markdown reader is shown a lesson that
115
- begins mid-sentence. Structure the prose instead: a takeaway title, a visible
116
- **Applies when** line, then short bold-labelled paragraphs under ~1,500
117
- characters. The full shape, the field-by-field rationale, and the writing rules
118
- are [the lesson record](./rules/self-improvement-loops.md#the-lesson-record).
168
+ A hidden block does not just look untidy — it is *wrong*. A `seen_count=1` baked into
169
+ prose is stale the first time the lesson recurs, while the column that governs
170
+ promotion moves without it; an HTML comment renders to nothing, so a human is shown a
171
+ lesson that begins mid-sentence. The full shape, the field-by-field rationale, and the
172
+ writing rules are [the lesson record](./rules/self-improvement-loops.md#the-lesson-record).
173
+ This contract is now checkable — `lorekit lint` flags a body that violates it, and the
174
+ `lorekit-groom` skill cleans legacy offenders.
119
175
 
120
176
  ## The shared codebase-knowledge layer (automatic cross-loop synergy)
121
177
 
122
- A per-host lessons bucket is private to one host. There is also **one shared
123
- bucket every code-touching host reads and, under a contract, writes**:
124
- `codebase-knowledge` — a repo-scoped, structurally-keyed record
125
- (`knowledge::<symbol>@<path>` facts, `hotspot::<path>` counters) of what the
126
- codebase has taught every LoreKit loop that touched it. Because the name is fixed
127
- and the key is structural, a loop wired by one person compounds with a loop wired
128
- by another: a host about to change code reads the history for exactly the files it
129
- will touch, and a host that verifies a structural fact contributes it back. That
130
- is the synergy that appears for a user who wired a single skill and nothing else.
131
-
132
- Wire it whenever a host changes code (read at its plan/apply seam) or verifies a
133
- durable structural fact (write under the contract). The full specification — the
134
- bucket table, the automatic read side, and the seven-bullet multi-writer write
135
- contract — is [rules/self-improvement-loops.md § Shared codebase-knowledge](./rules/self-improvement-loops.md#shared-codebase-knowledge-the-standard-cross-loop-layer).
136
-
137
- ## Set up CI state (deterministic hosts)
138
-
139
- Follow [rules/ci-state-records.md](./rules/ci-state-records.md). It covers: when
140
- LoreKit beats `actions/cache` (and when it does not), the `ci::<job>-state` bucket
141
- convention, the versioned JSON envelope, the read/write steps with the `lorekit`
142
- CLI or REST, a full GitHub Actions example, the guards (bounded cardinality, no
143
- secrets, explicit expiry, never on the critical path, last-write-wins), and a
144
- wiring checklist.
145
-
146
- If invoked with a `host-name`, set up memory for that host; otherwise ask which
147
- skill / workflow / agent / job it is for, pick the shape from the table above,
148
- then walk that rule file's setup.
149
-
150
- A lessons loop's runtime tier needs LoreKit's `memory.*` tools connected; if they
151
- are not, the host's loop is a silent no-op (the slow tier — a normal source edit —
152
- still works). A CI job needs a `lk_*` token in its environment instead, and
153
- degrades to its first-run path when the store is unreachable. Designing either
154
- needs no connection.
178
+ A per-host lessons bucket is private to one host. There is also **one shared bucket
179
+ every code-touching host reads and, under a contract, writes**: `codebase-knowledge` —
180
+ a repo-scoped, structurally-keyed record (`knowledge::<symbol>@<path>` facts,
181
+ `hotspot::<path>` counters) of what the codebase has taught every LoreKit loop that
182
+ touched it. Because the name is fixed and the key is structural, a loop wired by one
183
+ person compounds with a loop wired by another. The full specification is
184
+ [rules/self-improvement-loops.md § Shared codebase-knowledge](./rules/self-improvement-loops.md#shared-codebase-knowledge-the-standard-cross-loop-layer).
185
+
186
+ ## Connectivity
187
+
188
+ A lessons loop's runtime tier needs LoreKit's `memory.*` tools connected; if they are
189
+ not, the host's loop is a silent no-op (the slow tier — a normal source edit — still
190
+ works). A CI job needs a `lk_*` token in its environment instead, and degrades to its
191
+ first-run path when the store is unreachable. Designing either needs no connection.
@@ -0,0 +1,102 @@
1
+ # Cold-start seeding — value on run one
2
+
3
+ The honest cold start is a lie. A loop does not really begin empty — the lessons already
4
+ exist, scattered across git history, PR review threads, CI guard scripts, and the
5
+ `obligations-map`. They were just never written down as reusable memory. Seeding
6
+ harvests a *handful* of them so the loop's very first run reads real institutional
7
+ knowledge instead of nothing.
8
+
9
+ This is optional, and it trades one thing for another — read
10
+ [the measurement trade-off](#the-hard-rule-never-seed-a-loop-you-will-measure) before
11
+ you reach for it.
12
+
13
+ ## Contents
14
+
15
+ - [When to seed (and when not to)](#when-to-seed-and-when-not-to)
16
+ - [The cap: a handful, hand-checked](#the-cap-a-handful-hand-checked)
17
+ - [The hard rule: never seed a loop you will measure](#the-hard-rule-never-seed-a-loop-you-will-measure)
18
+ - [Where the lessons already are](#where-the-lessons-already-are)
19
+ - [The flow](#the-flow)
20
+ - [Guards](#guards)
21
+
22
+ ---
23
+
24
+ ## When to seed (and when not to)
25
+
26
+ Seed when the host's value is **invisible for its first week** — lessons only accrue on
27
+ repeated runs, so a freshly-wired loop delivers config, not a win, and people abandon
28
+ it before it pays off. A few real seeded lessons make run one useful.
29
+
30
+ Do **not** seed when you intend to *prove* the loop's lift (below), or when you cannot
31
+ find genuinely recurring pain to seed from — a fabricated lesson is worse than an empty
32
+ bucket.
33
+
34
+ ## The cap: a handful, hand-checked
35
+
36
+ **Seed a handful (roughly ≤ 5), each one hand-checked. Never bulk-mine.** This is the
37
+ single most important rule here, and it runs against the instinct to "import everything."
38
+
39
+ Bulk seeding floods the bucket with generic, machine-drafted lessons that never get
40
+ opened. That tanks the exact pull-through ratio [proving-improvement.md](./proving-improvement.md)
41
+ reads, triggers the bucket-bloat [loop-health.md](./loop-health.md#keep-the-bucket-from-bloating)
42
+ fights, and buries the few seeds that were actually good — on day one. A small,
43
+ curated set is worth more than a large, automatic one.
44
+
45
+ ## The hard rule: never seed a loop you will measure
46
+
47
+ Seeding writes lessons before the loop has run, so it **erases the clean baseline** the
48
+ immunity re-challenge needs. A seeded loop's improvement is unattributable — you cannot
49
+ tell the loop's own learning from the head start you handed it.
50
+
51
+ So it is one or the other, per loop:
52
+
53
+ | You want… | Then… |
54
+ | --------- | ----- |
55
+ | Value on run one | Seed it (a handful, curated). Accept that its lift is not cleanly provable. |
56
+ | Provable lift | Leave it cold. The first weeks are quieter, but the immunity check reads a real baseline. |
57
+
58
+ ## Where the lessons already are
59
+
60
+ Mine these — they are recurring pain the team already paid for:
61
+
62
+ - **Git fix/revert chains** — the same pattern fixed the same way 3+ times, or a
63
+ revert-then-refix. `git log`, `git log --grep=revert`, `git log -p` over a hot file.
64
+ - **Recurring PR review comments** — the same review note left across many PRs.
65
+ - **Existing CI guard scripts** — a guard that exists *is* a lesson someone learned;
66
+ seed a `codebase-knowledge` fact pointing at it.
67
+ - **The `obligations-map`** — its path-keyed partnerships are compiled lessons; the
68
+ ones not yet gating are candidates. `lorekit invariants candidates` surfaces clusters.
69
+ - **`lorekit dedupe`** — near-duplicate existing lessons that should be one seed.
70
+
71
+ ## The flow
72
+
73
+ 1. **Mine** one or two of the sources above for genuinely recurring pain.
74
+ 2. **Draft** ≤ 5 candidate lessons to the normal body shape
75
+ ([the lesson record](./self-improvement-loops.md#the-lesson-record)) — a takeaway
76
+ title, a concrete **Applies when**, a prescriptive **Do this instead**.
77
+ 3. **A human approves** the survivors. This is not skippable; seeding is the one place
78
+ a bad lesson enters with no recurrence evidence behind it.
79
+ 4. **Write** each with provenance and a `source::seed` tag, so seeds are auditable and
80
+ distinguishable from earned lessons:
81
+
82
+ ```text
83
+ memory.write {
84
+ scope: "<global | repo::<owner>/<repo>>",
85
+ key: "<host>-lessons::<slug>",
86
+ value: "<markdown lesson body — no hidden blocks>",
87
+ tags: ["loop::<host>-lessons", "source::seed"],
88
+ trigger: "manual",
89
+ origin_commit: "<the commit this was learned from, if any>",
90
+ ttl_days: 90
91
+ }
92
+ ```
93
+
94
+ ## Guards
95
+
96
+ - **The body contract still applies** — seeds are markdown for humans, no hidden blocks.
97
+ - **Tag seeds `source::seed`** so [loop-health](./loop-health.md) and grooming can tell a
98
+ head start from an earned lesson.
99
+ - **Privacy pre-flight is not skipped** — a seed mined from history can carry a secret or
100
+ a name; drop it, do not write it. The bar is stricter for `repo::` (team-visible).
101
+ - **Re-check the cap after mining.** If your candidate list is 40 long, you are
102
+ bulk-seeding — cut to the handful that actually recur.