@onlooker-community/ecosystem 0.33.1 → 0.34.1
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/.agents/skills/beads/SKILL.md +80 -0
- package/.agents/skills/beads/agents/openai.yaml +4 -0
- package/.claude/settings.json +13 -0
- package/.claude/skills/writing-tests/SKILL.md +27 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.codex/config.toml +2 -0
- package/.codex/hooks.json +51 -0
- package/.markdownlint.json +3 -0
- package/.release-please-manifest.json +6 -6
- package/AGENTS.md +246 -0
- package/CHANGELOG.md +14 -0
- package/CLAUDE.md +56 -1
- package/docs/lesson-promotion-pipeline.md +210 -0
- package/docs/superpowers/plans/2026-08-09-lesson-transform.md +1537 -0
- package/docs/superpowers/specs/2026-08-09-lesson-transform-design.md +261 -0
- package/package.json +3 -2
- package/plugins/assayer/.claude-plugin/plugin.json +1 -1
- package/plugins/assayer/CHANGELOG.md +7 -0
- package/plugins/assayer/scripts/lib/assayer-config.sh +6 -0
- package/plugins/curator/.claude-plugin/plugin.json +1 -1
- package/plugins/curator/CHANGELOG.md +7 -0
- package/plugins/curator/scripts/lib/curator-emit.sh +2 -1
- package/plugins/historian/.claude-plugin/plugin.json +1 -1
- package/plugins/historian/CHANGELOG.md +7 -0
- package/plugins/historian/scripts/lib/historian-emit.sh +2 -1
- package/plugins/librarian/.claude-plugin/plugin.json +1 -1
- package/plugins/librarian/CHANGELOG.md +14 -0
- package/plugins/librarian/config.json +4 -0
- package/plugins/librarian/schema/PROVENANCE.json +7 -0
- package/plugins/librarian/schema/lesson-applies-to.subschema.json +74 -0
- package/plugins/librarian/schema/lesson-evidence.subschema.json +36 -0
- package/plugins/librarian/scripts/hooks/librarian-session-end.sh +26 -0
- package/plugins/librarian/scripts/lib/librarian-cli.sh +2 -1
- package/plugins/librarian/scripts/lib/librarian-emit.sh +2 -1
- package/plugins/librarian/scripts/lib/librarian-lesson-storage.sh +135 -0
- package/plugins/librarian/scripts/lib/librarian-lesson-transform.sh +311 -0
- package/plugins/librarian/scripts/lib/librarian-lesson-validate.sh +140 -0
- package/plugins/tribunal/.claude-plugin/plugin.json +1 -1
- package/plugins/tribunal/CHANGELOG.md +7 -0
- package/plugins/tribunal/scripts/lib/tribunal-aggregate.sh +3 -1
- package/plugins/tribunal/scripts/lib/tribunal-gate.sh +2 -1
- package/scripts/lib/prompt-rules.sh +6 -1
- package/scripts/lint/check-lesson-schema-drift.mjs +36 -0
- package/test/bats/archivist-inject.bats +1 -1
- package/test/bats/assayer-extract.bats +2 -2
- package/test/bats/bursar-session-start.bats +3 -3
- package/test/bats/cartographer-lock.bats +3 -3
- package/test/bats/compass-sanitizer.bats +11 -11
- package/test/bats/compass-transcript.bats +2 -2
- package/test/bats/config.bats +15 -15
- package/test/bats/curator-session-start.bats +10 -3
- package/test/bats/emit-payload-default.bats +52 -0
- package/test/bats/governor-ledger.bats +1 -1
- package/test/bats/historian-prompt-submit.bats +1 -1
- package/test/bats/inspector-post-write-hook.bats +4 -4
- package/test/bats/librarian-cli.bats +16 -16
- package/test/bats/librarian-lesson-transform.bats +609 -0
- package/test/bats/librarian-session-start.bats +2 -2
- package/test/bats/lineage-config.bats +1 -1
- package/test/bats/lineage-redact.bats +5 -5
- package/test/bats/session-tracker.bats +4 -4
- package/test/bats/tribunal-jury.bats +1 -1
- package/test/bats/turn-tracker.bats +1 -1
- package/test/bats/warden-sanitizer.bats +3 -3
- package/test/bats/worktree-tracker.bats +2 -2
- package/test/node/lesson-schema-drift.test.mjs +28 -0
- package/test/node/lesson-validate-agreement.test.mjs +154 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Lesson Promotion Pipeline
|
|
2
|
+
|
|
3
|
+
**Status:** Not started. Design is settled; implementation lives in this repo.
|
|
4
|
+
**Tracked by:** `onlooker-97e` in the onlooker beads tracker.
|
|
5
|
+
**Authoritative design:** `docs/superpowers/specs/2026-08-08-promotion-pipeline-design.md`
|
|
6
|
+
in the [onlooker](https://github.com/onlooker-community/onlooker) repo, Sections 2 and 3.
|
|
7
|
+
Read that before making design decisions — this document is orientation, not the spec.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What we are building
|
|
12
|
+
|
|
13
|
+
A fourth destination for archivist artifacts: a **shared** pool of lessons that
|
|
14
|
+
can cross machines and people, rather than the local, per-machine typed memory
|
|
15
|
+
store librarian writes to today.
|
|
16
|
+
|
|
17
|
+
This is easy to misread as "another memory type." It is not. Compare with
|
|
18
|
+
[memory-architecture.md](memory-architecture.md):
|
|
19
|
+
|
|
20
|
+
| | destination | scope | who sees it |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| librarian → typed memory store | `~/.claude/projects/<encoded>/memory/` | one machine | you |
|
|
23
|
+
| **librarian → lesson pool** | `~/.onlooker/librarian/<project-key>/lessons/` | shared, eventually cross-person | you, your org, or the public |
|
|
24
|
+
|
|
25
|
+
Everything upstream is unchanged. Artifacts are still captured by archivist,
|
|
26
|
+
still filtered for durability, still classified, still deduped. The new work
|
|
27
|
+
hangs off the end of that existing chain.
|
|
28
|
+
|
|
29
|
+
## The shape
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
archivist artifacts EXISTS session-scoped facts
|
|
33
|
+
└→ durability filter EXISTS cheap, pre-LLM
|
|
34
|
+
└→ type classifier (Haiku) EXISTS user/feedback/project/reference
|
|
35
|
+
└→ conflict/dup detect (Jaccard) EXISTS keeps the queue high-signal
|
|
36
|
+
══════════════════════════════════════
|
|
37
|
+
└→ lesson transform (Haiku) NEW claim, rationale, applies_to
|
|
38
|
+
└→ human picks + visibility NEW propose-only, per librarian ADR-001
|
|
39
|
+
└→ tribunal gate NEW one-shot, visibility-scoped
|
|
40
|
+
└→ approved pool NEW local; the sync service drains it later
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Three new steps, in two existing plugins. **No new plugin.** Librarian already
|
|
44
|
+
owns the artifact reader, the durability filter, the classifier, the dedup pass
|
|
45
|
+
and the proposal queue — the transform is a fifth stage on a chain that exists,
|
|
46
|
+
and its `last_scan.json` watermark already tracks which artifacts have been
|
|
47
|
+
considered. A separate plugin would need a second copy of that state, free to
|
|
48
|
+
drift.
|
|
49
|
+
|
|
50
|
+
Tribunal contributes a rubric and reuses `tribunal-judge-security`, which it
|
|
51
|
+
already ships disabled by default.
|
|
52
|
+
|
|
53
|
+
## Why lessons rot, and what the contract does about it
|
|
54
|
+
|
|
55
|
+
The design exists because of a real artifact in this repo's own storage: a
|
|
56
|
+
vitest/vite version-mismatch decision that was true when captured and is false
|
|
57
|
+
now. Shared and auto-injected, it would send someone else down a dead end.
|
|
58
|
+
|
|
59
|
+
So staleness is **structural**, not procedural. A lesson carries version ranges,
|
|
60
|
+
and a session outside those ranges simply never matches it. No review queue, no
|
|
61
|
+
expiry job, nothing to forget to run. The contract enforces this by construction:
|
|
62
|
+
`applies_to.scope` is a tagged union, and the branch that claims version
|
|
63
|
+
independence must carry a written justification the tribunal scores. A transform
|
|
64
|
+
that failed to infer versions has nothing to put there, so it cannot silently
|
|
65
|
+
mint a lesson that never expires.
|
|
66
|
+
|
|
67
|
+
## The contract
|
|
68
|
+
|
|
69
|
+
Defined in `packages/lesson-contract` in the onlooker repo and **published as
|
|
70
|
+
JSON Schema**:
|
|
71
|
+
|
|
72
|
+
- `packages/lesson-contract/schema/lesson.schema.json`
|
|
73
|
+
- `packages/lesson-contract/schema/counter-observation.schema.json`
|
|
74
|
+
|
|
75
|
+
Currently `schema_version: 2`.
|
|
76
|
+
|
|
77
|
+
**The producing side cannot import the definition.** Plugins here are
|
|
78
|
+
bash-based and live in a different repo; the zod source is not available to
|
|
79
|
+
them. Validate against the published JSON Schema, or simply conform to it. The
|
|
80
|
+
sync endpoint in `apps/api` is the real enforcement boundary and validates
|
|
81
|
+
regardless, so client-side validation is a convenience, not a trust boundary.
|
|
82
|
+
|
|
83
|
+
Fields worth knowing before you start:
|
|
84
|
+
|
|
85
|
+
| Field | Note |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `claim` / `rationale` | what is asserted, and why it follows |
|
|
88
|
+
| `evidence.resolution` | **required.** "this breaks" without "and this fixed it" is a warning, not a lesson |
|
|
89
|
+
| `evidence.project_key` | the opaque hash, never the repo name |
|
|
90
|
+
| `applies_to.scope` | `{kind: "versioned", versions}` or `{kind: "version_independent", justification}` |
|
|
91
|
+
| `applies_to.stack` | every key of `scope.versions` must name an entry here — see cross-field rules below |
|
|
92
|
+
| `author_key` | 32 lowercase hex, `HMAC(user_secret, scope)`, derived **per visibility scope** so org and public identities are unlinkable |
|
|
93
|
+
| `visibility` | `private` / `org` / `public` |
|
|
94
|
+
| `status` | `active` / `refuted` / `superseded` / `retracted`. There is deliberately no `expired` |
|
|
95
|
+
|
|
96
|
+
**Cross-field rules the schema cannot express**, documented in the contract's
|
|
97
|
+
`.describe()` text and enforced at ingest. Worth self-checking before emitting:
|
|
98
|
+
|
|
99
|
+
- `consensus.agreed <= consensus.judges`
|
|
100
|
+
- every key of `applies_to.scope.versions` names an entry in `applies_to.stack`
|
|
101
|
+
|
|
102
|
+
## What is already decided
|
|
103
|
+
|
|
104
|
+
Do not re-litigate these; they came out of a full design cycle and are recorded
|
|
105
|
+
with reasoning in the spec.
|
|
106
|
+
|
|
107
|
+
**The gate is one-shot.** `max_iterations: 1`. Below threshold, a candidate is
|
|
108
|
+
dropped rather than repaired. Refutation should be cheaper to trigger than
|
|
109
|
+
promotion — a wrong lesson actively misleads, a missing one merely fails to
|
|
110
|
+
help. Fail toward removal. This also bounds token cost per promotion and stops
|
|
111
|
+
the transform learning to satisfy judges rather than the evidence.
|
|
112
|
+
|
|
113
|
+
**The human confirms before judging, not after.** The transform is Haiku and
|
|
114
|
+
cheap; the jury is Opus and not. Splitting the filters this way means the human
|
|
115
|
+
judges *intent* ("do I want to share anything about this?"), which only they can
|
|
116
|
+
do and which costs nothing, and the jury judges *quality*, which only it can do
|
|
117
|
+
and which costs real money. Opus tokens are then only ever spent on candidates
|
|
118
|
+
someone already wants shared.
|
|
119
|
+
|
|
120
|
+
**Gating is scoped by visibility.** Not every lesson passes a jury:
|
|
121
|
+
|
|
122
|
+
| visibility | gate | why |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `private` | none | you are the only consumer |
|
|
125
|
+
| `org` | `lesson-promotion` rubric | the org boundary already implies trust |
|
|
126
|
+
| `public` | rubric + disclosure lens | self-reported consensus is gameable by a modified client |
|
|
127
|
+
|
|
128
|
+
**The rubric**, in tribunal's existing `config.json` shape:
|
|
129
|
+
|
|
130
|
+
| criterion | weight | `min_pass` | asks |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `grounding` | 0.45 | 0.7 | does the claim follow from `evidence` and `resolution`? |
|
|
133
|
+
| `scope_accuracy` | 0.35 | 0.7 | does `applies_to` correctly bound the claim? |
|
|
134
|
+
| `generality` | 0.20 | 0.6 | is this a lesson, or a session-scoped fact? |
|
|
135
|
+
|
|
136
|
+
`score_threshold: 0.75`, `gate_policy: majority`, `aggregation_method:
|
|
137
|
+
weighted_mean`, `judge_types: ["standard", "adversarial"]` — all tribunal
|
|
138
|
+
defaults. The only override is `max_iterations: 1`.
|
|
139
|
+
|
|
140
|
+
For `public`, add the disclosure lens using the already-shipped
|
|
141
|
+
`tribunal-judge-security`:
|
|
142
|
+
|
|
143
|
+
| criterion | weight | `min_pass` | asks |
|
|
144
|
+
|---|---|---|---|
|
|
145
|
+
| `disclosure` | 0.30 | **0.9** | leaks a secret or identity, or advocates a harmful practice? |
|
|
146
|
+
|
|
147
|
+
The high floor is deliberate. Correctness rots and `applies_to` retires it;
|
|
148
|
+
harm does not. A leaked credential never expires on its own, so disclosure gets
|
|
149
|
+
a floor a strong weighted mean cannot average away.
|
|
150
|
+
|
|
151
|
+
**State layout**, under librarian's existing project key:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
~/.onlooker/librarian/<project-key>/
|
|
155
|
+
lessons/approved/<ulid>.json jury passed; awaiting sync
|
|
156
|
+
lessons/declined.jsonl artifact_id + verdict + reason
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The declined ledger matters more than it looks. The watermark advances past a
|
|
160
|
+
rejected artifact, so without a record a drop is either silently permanent or —
|
|
161
|
+
on a rescan — re-pays Opus tokens to re-judge the same failures every session.
|
|
162
|
+
Append-only, never re-judged automatically.
|
|
163
|
+
|
|
164
|
+
**"Judged and failed" is not "could not judge."** Only real verdicts go in
|
|
165
|
+
`declined.jsonl`. A tribunal API error, or a jury below quorum, leaves the
|
|
166
|
+
candidate in proposals untouched. Conflating them lets one transient outage
|
|
167
|
+
permanently bury good lessons behind a watermark that has already moved.
|
|
168
|
+
|
|
169
|
+
## Conventions this repo imposes
|
|
170
|
+
|
|
171
|
+
From [CLAUDE.md](../CLAUDE.md) — these differ from the onlooker repo:
|
|
172
|
+
|
|
173
|
+
- **Hooks are bash.** No Python or Node entry points in hook scripts, though they
|
|
174
|
+
may shell out to `node` for event emission or heavy lifting.
|
|
175
|
+
- **Event names** follow `<plugin>.<noun>.<verb>`. Likely additions here:
|
|
176
|
+
`librarian.lesson.proposed`, `librarian.lesson.approved`,
|
|
177
|
+
`librarian.lesson.declined`, `tribunal.lesson.judged`.
|
|
178
|
+
- **ULIDs, not UUIDs**, and each plugin ships its own helper. Librarian will need
|
|
179
|
+
one for lesson ids if it does not have one — copy
|
|
180
|
+
`plugins/tribunal/scripts/lib/tribunal-ulid.sh` and rename the prefix.
|
|
181
|
+
- **Config defaults** live in the plugin's `config.json`; user overrides go under
|
|
182
|
+
the plugin's namespace key in settings. See ADR-004.
|
|
183
|
+
|
|
184
|
+
## Out of scope
|
|
185
|
+
|
|
186
|
+
**Counter-observations and re-judgment.** They need consumers of shared
|
|
187
|
+
lessons, which needs the sync service and retrieval. The contract already
|
|
188
|
+
defines `ZCounterObservation`, and the counter-observation threshold is
|
|
189
|
+
explicitly still an open number — do not invent one.
|
|
190
|
+
|
|
191
|
+
**Publishing anywhere.** The pipeline stops at the local approved pool. Nothing
|
|
192
|
+
crosses the network. The sync service drains that queue later, and does not
|
|
193
|
+
exist yet.
|
|
194
|
+
|
|
195
|
+
**The server-side re-judge for public lessons.** Local consensus for public
|
|
196
|
+
lessons runs here; the pool records the intended visibility so the server knows
|
|
197
|
+
what still needs independent judging.
|
|
198
|
+
|
|
199
|
+
## Open questions
|
|
200
|
+
|
|
201
|
+
- **Where the human confirmation surfaces.** Librarian already has a proposal
|
|
202
|
+
queue and a SessionStart surfacer for memory promotions. Reuse it, or keep
|
|
203
|
+
lesson proposals separate so the two kinds of confirmation are not confused?
|
|
204
|
+
- **`author_key` derivation.** The contract pins the format — 32 hex,
|
|
205
|
+
`HMAC(user_secret, scope)` — but nothing derives it yet. Where does
|
|
206
|
+
`user_secret` live, and how is it created on first use?
|
|
207
|
+
- **Whether the transform self-validates** against the published JSON Schema
|
|
208
|
+
before writing to the pool, or leaves all validation to ingest. Validating
|
|
209
|
+
locally catches a bad transform earlier; it also means fetching and caching
|
|
210
|
+
a schema from another repo.
|