@onlooker-community/ecosystem 0.34.0 → 0.43.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/.claude/skills/writing-tests/SKILL.md +27 -0
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/release.yml +14 -14
- package/.release-please-manifest.json +7 -7
- package/AGENTS.md +116 -0
- package/CHANGELOG.md +175 -0
- package/CLAUDE.md +5 -0
- package/docs/lesson-promotion-pipeline.md +6 -6
- package/docs/superpowers/plans/2026-08-10-lesson-confirmation.md +1273 -0
- package/docs/superpowers/plans/2026-08-11-lesson-judging.md +1232 -0
- package/docs/superpowers/plans/2026-08-11-lesson-unconfirm.md +458 -0
- package/docs/superpowers/plans/2026-08-12-author-key.md +567 -0
- package/docs/superpowers/plans/2026-08-13-approved-pool.md +747 -0
- package/docs/superpowers/plans/2026-08-13-librarian-cluster.md +392 -0
- package/docs/superpowers/plans/2026-08-14-criterion-scores-consumers.md +1374 -0
- package/docs/superpowers/plans/2026-08-14-criterion-scores-schema.md +277 -0
- package/docs/superpowers/plans/2026-08-16-cartographer-undocumented-entity.md +741 -0
- package/docs/superpowers/specs/2026-08-10-lesson-confirmation-design.md +213 -0
- package/docs/superpowers/specs/2026-08-11-lesson-judging-design.md +267 -0
- package/docs/superpowers/specs/2026-08-11-lesson-unconfirm-design.md +152 -0
- package/docs/superpowers/specs/2026-08-12-author-key-design.md +317 -0
- package/docs/superpowers/specs/2026-08-13-approved-pool-design.md +232 -0
- package/docs/superpowers/specs/2026-08-14-criterion-scores-design.md +254 -0
- package/docs/superpowers/specs/2026-08-16-cartographer-undocumented-entity-design.md +249 -0
- package/package.json +2 -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/cartographer/.claude-plugin/plugin.json +1 -1
- package/plugins/cartographer/CHANGELOG.md +54 -0
- package/plugins/cartographer/README.md +56 -1
- package/plugins/cartographer/config.json +7 -1
- package/plugins/cartographer/scripts/hooks/cartographer-post-write.sh +4 -4
- package/plugins/cartographer/scripts/hooks/cartographer-session-start.sh +7 -4
- package/plugins/cartographer/scripts/lib/cartographer-collect.sh +8 -1
- package/plugins/cartographer/scripts/lib/cartographer-config.sh +26 -0
- package/plugins/cartographer/scripts/lib/cartographer-events.sh +90 -0
- package/plugins/cartographer/scripts/lib/cartographer-filter.sh +105 -0
- package/plugins/cartographer/scripts/lib/cartographer-omission.sh +130 -0
- package/plugins/cartographer/scripts/lib/cartographer-resolve.sh +132 -0
- package/plugins/cartographer/scripts/run-audit.sh +136 -53
- package/plugins/cartographer/skills/cartographer/SKILL.md +33 -9
- 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 +71 -0
- package/plugins/librarian/config.json +30 -1
- package/plugins/librarian/docs/adr/002-agent-definitions-are-shared-assets.md +82 -0
- package/plugins/librarian/scripts/hooks/librarian-session-end.sh +28 -0
- package/plugins/librarian/scripts/hooks/librarian-session-start.sh +42 -12
- package/plugins/librarian/scripts/lib/librarian-author-key.sh +279 -0
- package/plugins/librarian/scripts/lib/librarian-cli.sh +346 -2
- package/plugins/librarian/scripts/lib/librarian-emit.sh +2 -1
- package/plugins/librarian/scripts/lib/librarian-lesson-judge.sh +349 -0
- package/plugins/librarian/scripts/lib/librarian-lesson-promote.sh +225 -0
- package/plugins/librarian/scripts/lib/librarian-lesson-review.sh +312 -0
- package/plugins/librarian/scripts/lib/librarian-lesson-rubric.sh +56 -0
- package/plugins/librarian/scripts/lib/librarian-lesson-storage.sh +82 -13
- package/plugins/librarian/scripts/lib/librarian-lesson-transform.sh +6 -1
- package/plugins/librarian/scripts/lib/librarian-lesson-validate.sh +131 -62
- package/plugins/librarian/skills/librarian/SKILL.md +98 -1
- package/plugins/tribunal/.claude-plugin/plugin.json +1 -1
- package/plugins/tribunal/CHANGELOG.md +63 -0
- package/plugins/tribunal/agents/tribunal-judge-adversarial.md +34 -0
- package/plugins/tribunal/agents/tribunal-judge-security.md +33 -0
- package/plugins/tribunal/agents/tribunal-judge-standard.md +12 -0
- package/plugins/tribunal/config.json +1 -0
- package/plugins/tribunal/scripts/lib/tribunal-aggregate.sh +154 -9
- package/plugins/tribunal/scripts/lib/tribunal-gate.sh +195 -16
- package/plugins/tribunal/skills/tribunal/SKILL.md +5 -3
- package/scripts/lib/prompt-rules.sh +6 -1
- 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-config.bats +43 -0
- package/test/bats/cartographer-events.bats +267 -0
- package/test/bats/cartographer-filter.bats +169 -0
- package/test/bats/cartographer-lock.bats +3 -3
- package/test/bats/cartographer-omission.bats +202 -0
- package/test/bats/cartographer-resolve.bats +287 -0
- package/test/bats/cartographer-run-audit.bats +325 -0
- 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-author-key.bats +477 -0
- package/test/bats/librarian-cli.bats +16 -16
- package/test/bats/librarian-lesson-judge.bats +884 -0
- package/test/bats/librarian-lesson-promote.bats +552 -0
- package/test/bats/librarian-lesson-review.bats +900 -0
- package/test/bats/librarian-lesson-transform.bats +17 -3
- package/test/bats/librarian-session-end.bats +161 -0
- package/test/bats/librarian-session-start.bats +36 -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-aggregate.bats +354 -1
- package/test/bats/tribunal-gate.bats +631 -0
- package/test/bats/tribunal-judge-agents.bats +119 -0
- 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-validate-agreement.test.mjs +35 -6
|
@@ -220,13 +220,27 @@ _storage_setup() {
|
|
|
220
220
|
[ "$status" -eq 0 ]
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
@test "seen
|
|
223
|
+
@test "seen ignores the approved pool, which cannot identify an artifact" {
|
|
224
|
+
# This replaces a test that wrote {artifact_id: ...} into approved/ and
|
|
225
|
+
# asserted seen found it. That shape is unreachable — a pool entry is
|
|
226
|
+
# ZLesson, a strictObject with no artifact_id — so the old test fed the scan
|
|
227
|
+
# a file promote could never produce and reported coverage for a branch that
|
|
228
|
+
# never matched anything real. It was the reason the dead scan survived.
|
|
229
|
+
#
|
|
230
|
+
# ecosystem-d0m settled this: proposals/ is the sole dedup source. The
|
|
231
|
+
# end-to-end tripwire lives in librarian-lesson-promote.bats, against a real
|
|
232
|
+
# promoted entry; this one pins that a pool-shaped file is simply not
|
|
233
|
+
# consulted.
|
|
224
234
|
_storage_setup
|
|
225
235
|
librarian_lesson_storage_init "$PROJECT_KEY"
|
|
226
|
-
jq -n '{
|
|
236
|
+
jq -n '{id: "01KZ45MKGQ7QZWMABQ4H12SHSV", schema_version: 2, claim: "c",
|
|
237
|
+
rationale: "r", evidence: [], applies_to: [], visibility: "public",
|
|
238
|
+
consensus: {judges: 2, agreed: 2, decided_at: "2026-01-01T00:00:00Z"},
|
|
239
|
+
status: "active", superseded_by: null, source: "s",
|
|
240
|
+
author_key: "k", promoted_at: "2026-01-01T00:00:00Z"}' \
|
|
227
241
|
> "${LESSONS_DIR}/approved/01KZ45MKGQ7QZWMABQ4H12SHSV.json"
|
|
228
242
|
run librarian_lesson_seen "$PROJECT_KEY" "01KZ45MKAM734ZS7JK24D2DK0R"
|
|
229
|
-
[ "$status" -eq
|
|
243
|
+
[ "$status" -eq 1 ]
|
|
230
244
|
}
|
|
231
245
|
|
|
232
246
|
@test "seen still finds a declined artifact when declined.jsonl has a truncated trailing line" {
|
|
@@ -42,10 +42,17 @@ setup() {
|
|
|
42
42
|
# based on the artifact's summary contents.
|
|
43
43
|
STUB_BIN="${BATS_TEST_TMPDIR}/bin"
|
|
44
44
|
mkdir -p "$STUB_BIN"
|
|
45
|
+
# Every invocation is recorded so budget tests can assert on how many model
|
|
46
|
+
# calls a scan actually made — the only evidence that distinguishes an
|
|
47
|
+
# analyzer skipped by the budget from one that ran and produced nothing.
|
|
48
|
+
export CLAUDE_CALL_LOG="${BATS_TEST_TMPDIR}/claude-calls"
|
|
49
|
+
: > "$CLAUDE_CALL_LOG"
|
|
50
|
+
|
|
45
51
|
cat > "${STUB_BIN}/claude" <<'STUB'
|
|
46
52
|
#!/usr/bin/env bash
|
|
47
53
|
# Read the prompt from stdin and decide which classifier response to emit.
|
|
48
54
|
prompt=$(cat)
|
|
55
|
+
[[ -n "${CLAUDE_CALL_LOG:-}" ]] && echo call >> "$CLAUDE_CALL_LOG"
|
|
49
56
|
if [[ "$prompt" == *"prefer-functional-stub"* ]]; then
|
|
50
57
|
printf '%s' '{"type":"feedback","title":"Prefer functional patterns","body":"User prefers functional patterns over class-based.\n\n**Why:** Stated explicitly during code review.\n**How to apply:** Default to plain functions and composition.","confidence":0.84}'
|
|
51
58
|
elif [[ "$prompt" == *"compliance-stub"* ]]; then
|
|
@@ -176,3 +183,157 @@ _hook_input() {
|
|
|
176
183
|
grep '"event_type":"librarian.scan.complete"' "$ONLOOKER_EVENTS_LOG" \
|
|
177
184
|
| jq -e '.payload.outcome == "empty" and .payload.candidates_proposed == 0 and .payload.candidates_dropped >= 1' >/dev/null
|
|
178
185
|
}
|
|
186
|
+
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
# Stage 5 aggregate budget (ecosystem-qwi).
|
|
189
|
+
#
|
|
190
|
+
# Each lesson transform carries a 20s ceiling of its own, but nothing bounded
|
|
191
|
+
# KEPT_COUNT of them end to end, so a backlog could hold SessionEnd open for
|
|
192
|
+
# minutes. These assert on model call counts, because that is what separates
|
|
193
|
+
# "the budget skipped the loop" from "the loop ran and found nothing".
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
_llm_calls() {
|
|
197
|
+
wc -l < "$CLAUDE_CALL_LOG" | tr -d ' '
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_settings() {
|
|
201
|
+
cat > "${PROJECT_REPO}/.claude/settings.json"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Carries the marker phrase the durability filter wants AND a version token,
|
|
205
|
+
# without which the lesson pregate rejects it before any model call and the
|
|
206
|
+
# budget tests could not tell the two states apart.
|
|
207
|
+
_seed_lessonable() {
|
|
208
|
+
_seed_artifact "decisions" "01LESSONBUDGETARTIFACT000" \
|
|
209
|
+
"User prefers functional patterns prefer-functional-stub" \
|
|
210
|
+
"User explicitly said: always prefer plain functions over classes. Pinned vite 5.4.21 to avoid the regression."
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@test "stage 5 runs a lesson transform when the budget allows" {
|
|
214
|
+
_seed_lessonable
|
|
215
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
216
|
+
[ "$status" -eq 0 ] || return 1
|
|
217
|
+
# One classifier call plus one lesson call.
|
|
218
|
+
[ "$(_llm_calls)" -ge 2 ]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
# A zero budget trips on the first iteration, so stage 5 spends nothing. The
|
|
222
|
+
# classifier call still happens, which is what makes this a measurement of the
|
|
223
|
+
# stage 5 guard rather than of the scan being disabled.
|
|
224
|
+
@test "a zero budget skips stage 5 entirely" {
|
|
225
|
+
echo '{"librarian":{"lesson_transform":{"total_budget_ms":0}}}' | _settings
|
|
226
|
+
_seed_lessonable
|
|
227
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
228
|
+
[ "$status" -eq 0 ] || return 1
|
|
229
|
+
[ "$(_llm_calls)" = "1" ]
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# The guard must fail toward skipping stage 5, never toward skipping the
|
|
233
|
+
# watermark advance — an unadvanced watermark re-scans the same backlog every
|
|
234
|
+
# session, turning a one-time cost into a permanent one.
|
|
235
|
+
@test "the watermark still advances when the budget trips" {
|
|
236
|
+
echo '{"librarian":{"lesson_transform":{"total_budget_ms":0}}}' | _settings
|
|
237
|
+
_seed_lessonable
|
|
238
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
239
|
+
[ "$status" -eq 0 ] || return 1
|
|
240
|
+
[ -f "${LIBRARIAN_DIR}/last_scan.json" ] || return 1
|
|
241
|
+
jq -e '.scanned_at | test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T")' "${LIBRARIAN_DIR}/last_scan.json" >/dev/null
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@test "scan.complete is still emitted when the budget trips" {
|
|
245
|
+
echo '{"librarian":{"lesson_transform":{"total_budget_ms":0}}}' | _settings
|
|
246
|
+
_seed_lessonable
|
|
247
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
248
|
+
[ "$status" -eq 0 ] || return 1
|
|
249
|
+
grep '"event_type":"librarian.scan.complete"' "$ONLOOKER_EVENTS_LOG" \
|
|
250
|
+
| jq -e '.payload.outcome == "ok" or .payload.outcome == "empty"' >/dev/null
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# Classification is upstream of stage 5, so a tripped lesson budget must not
|
|
254
|
+
# cost the user their proposals.
|
|
255
|
+
@test "a tripped lesson budget does not discard classifier proposals" {
|
|
256
|
+
echo '{"librarian":{"lesson_transform":{"total_budget_ms":0}}}' | _settings
|
|
257
|
+
_seed_lessonable
|
|
258
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
259
|
+
[ "$status" -eq 0 ] || return 1
|
|
260
|
+
grep '"event_type":"librarian.scan.complete"' "$ONLOOKER_EVENTS_LOG" \
|
|
261
|
+
| jq -e '.payload.candidates_proposed >= 1' >/dev/null
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@test "the hook still exits 0 when the budget trips" {
|
|
265
|
+
echo '{"librarian":{"lesson_transform":{"total_budget_ms":0}}}' | _settings
|
|
266
|
+
_seed_lessonable
|
|
267
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
268
|
+
[ "$status" -eq 0 ]
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
# ---------------------------------------------------------------------------
|
|
272
|
+
# Budget telemetry reaches the bus (ecosystem-1p1).
|
|
273
|
+
#
|
|
274
|
+
# librarian_emit swallows a validation failure and returns 0, appending to the
|
|
275
|
+
# log only when the emitter accepted the payload. So presence on the bus is
|
|
276
|
+
# proof the event validated — which is exactly the property that was missing:
|
|
277
|
+
# the classifier gate's outcome:"budget_exceeded" was rejected by the schema
|
|
278
|
+
# and silently dropped for as long as the gate has existed.
|
|
279
|
+
# ---------------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
_scan_complete() {
|
|
282
|
+
grep '"event_type":"librarian.scan.complete"' "$ONLOOKER_EVENTS_LOG" | tail -1
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
@test "a truncated stage 5 reports how many artifacts it skipped" {
|
|
286
|
+
echo '{"librarian":{"lesson_transform":{"total_budget_ms":0}}}' | _settings
|
|
287
|
+
_seed_lessonable
|
|
288
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
289
|
+
[ "$status" -eq 0 ] || return 1
|
|
290
|
+
_scan_complete | jq -e '.payload.lessons_skipped >= 1' >/dev/null
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
# lessons_skipped accompanies a healthy outcome rather than replacing it: the
|
|
294
|
+
# scan completed, only the one stage stopped early.
|
|
295
|
+
@test "the skip count rides alongside a normal outcome" {
|
|
296
|
+
echo '{"librarian":{"lesson_transform":{"total_budget_ms":0}}}' | _settings
|
|
297
|
+
_seed_lessonable
|
|
298
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
299
|
+
[ "$status" -eq 0 ] || return 1
|
|
300
|
+
_scan_complete | jq -e '.payload.outcome == "ok" or .payload.outcome == "empty"' >/dev/null
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@test "an untruncated scan reports zero skipped" {
|
|
304
|
+
_seed_lessonable
|
|
305
|
+
run bash -c "printf '%s' '$(_hook_input)' | '$HOOK'"
|
|
306
|
+
[ "$status" -eq 0 ] || return 1
|
|
307
|
+
_scan_complete | jq -e '.payload.lessons_skipped == 0' >/dev/null
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# The regression guard for the original bug. This payload is what the classifier
|
|
311
|
+
# gate builds verbatim; before schema 2.14.0 it failed validation and never
|
|
312
|
+
# appeared on the bus at all.
|
|
313
|
+
@test "the classifier gate's budget_exceeded payload reaches the bus" {
|
|
314
|
+
# shellcheck disable=SC1091
|
|
315
|
+
export _LIBRARIAN_EVENT_JS="${REPO_ROOT}/scripts/lib/onlooker-event.mjs"
|
|
316
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-emit.sh"
|
|
317
|
+
mkdir -p "$(dirname "$ONLOOKER_EVENTS_LOG")"
|
|
318
|
+
|
|
319
|
+
librarian_emit "librarian.scan.complete" "sess-budget" "$(jq -cn \
|
|
320
|
+
'{ outcome: "budget_exceeded", duration_ms: 1200, candidates_proposed: 0,
|
|
321
|
+
candidates_dropped: 3, artifact_count_in_window: 5 }')"
|
|
322
|
+
|
|
323
|
+
_scan_complete | jq -e '.payload.outcome == "budget_exceeded"' >/dev/null
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@test "an outcome outside the enum is still refused" {
|
|
327
|
+
export _LIBRARIAN_EVENT_JS="${REPO_ROOT}/scripts/lib/onlooker-event.mjs"
|
|
328
|
+
# shellcheck disable=SC1091
|
|
329
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-emit.sh"
|
|
330
|
+
mkdir -p "$(dirname "$ONLOOKER_EVENTS_LOG")"
|
|
331
|
+
|
|
332
|
+
librarian_emit "librarian.scan.complete" "sess-bad" \
|
|
333
|
+
'{"outcome":"gave_up","duration_ms":5}'
|
|
334
|
+
|
|
335
|
+
# A refused payload never reaches the log, so the file may not exist at all.
|
|
336
|
+
# grep on a missing file errors rather than printing 0, which is why this
|
|
337
|
+
# asserts absence directly instead of comparing a count.
|
|
338
|
+
! grep -q '"outcome":"gave_up"' "$ONLOOKER_EVENTS_LOG" 2>/dev/null
|
|
339
|
+
}
|
|
@@ -83,7 +83,7 @@ _seed_proposal() {
|
|
|
83
83
|
[ "$status" -eq 0 ]
|
|
84
84
|
local ctx
|
|
85
85
|
ctx=$(echo "$output" | jq -r '.hookSpecificOutput.additionalContext')
|
|
86
|
-
[[ "$ctx" == *"Librarian has 3 pending memory promotion proposals"* ]]
|
|
86
|
+
[[ "$ctx" == *"Librarian has 3 pending memory promotion proposals"* ]] || return 1
|
|
87
87
|
[[ "$ctx" == *"/librarian review"* ]]
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -94,7 +94,7 @@ _seed_proposal() {
|
|
|
94
94
|
[ "$status" -eq 0 ]
|
|
95
95
|
local ctx
|
|
96
96
|
ctx=$(echo "$output" | jq -r '.hookSpecificOutput.additionalContext')
|
|
97
|
-
[[ "$ctx" == *"Librarian has 1 pending memory promotion proposal"* ]]
|
|
97
|
+
[[ "$ctx" == *"Librarian has 1 pending memory promotion proposal"* ]] || return 1
|
|
98
98
|
[[ "$ctx" != *"proposals."* ]]
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -124,3 +124,37 @@ _seed_proposal() {
|
|
|
124
124
|
ctx=$(echo "$output" | jq -r '.hookSpecificOutput.additionalContext')
|
|
125
125
|
[[ "$ctx" == *"Librarian has 3+ pending memory promotion proposals"* ]]
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
@test "session-start surfaces a pending lesson count as its own line" {
|
|
129
|
+
# Reuse this file's existing project/hook setup, then seed one pending
|
|
130
|
+
# lesson through the storage lib.
|
|
131
|
+
# shellcheck disable=SC1091
|
|
132
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-storage.sh"
|
|
133
|
+
# shellcheck disable=SC1091
|
|
134
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-ulid.sh"
|
|
135
|
+
# shellcheck disable=SC1091
|
|
136
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-lesson-storage.sh"
|
|
137
|
+
librarian_lesson_storage_init "$PROJECT_KEY"
|
|
138
|
+
librarian_lesson_write_proposal "$PROJECT_KEY" \
|
|
139
|
+
"$(jq -cn '{claim: "c", rationale: "r"}')" "01KZ45MKAM734ZS7JK24D2DK0R" >/dev/null
|
|
140
|
+
|
|
141
|
+
run bash -c "printf '%s' '$(_input)' | '$HOOK'"
|
|
142
|
+
[ "$status" -eq 0 ]
|
|
143
|
+
[[ "$output" == *"lesson"* ]] || return 1
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@test "session-start surfaces lessons even with zero memory proposals" {
|
|
147
|
+
# shellcheck disable=SC1091
|
|
148
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-storage.sh"
|
|
149
|
+
# shellcheck disable=SC1091
|
|
150
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-ulid.sh"
|
|
151
|
+
# shellcheck disable=SC1091
|
|
152
|
+
source "${PLUGIN_ROOT}/scripts/lib/librarian-lesson-storage.sh"
|
|
153
|
+
librarian_lesson_storage_init "$PROJECT_KEY"
|
|
154
|
+
librarian_lesson_write_proposal "$PROJECT_KEY" \
|
|
155
|
+
"$(jq -cn '{claim: "c", rationale: "r"}')" "01KZ45MKAM734ZS7JK24D2DK0R" >/dev/null
|
|
156
|
+
|
|
157
|
+
run bash -c "printf '%s' '$(_input)' | '$HOOK'"
|
|
158
|
+
[ "$status" -eq 0 ]
|
|
159
|
+
[[ "$output" == *"lesson"* ]] || return 1
|
|
160
|
+
}
|
|
@@ -21,7 +21,7 @@ setup() {
|
|
|
21
21
|
local tok out
|
|
22
22
|
tok="sk-ant-$(printf 'a%.0s' $(seq 1 28))"
|
|
23
23
|
out=$(printf '%s' "key=\"${tok}\"" | lineage_redact 4000 true)
|
|
24
|
-
[[ "$out" == *"[REDACTED:secret]"* ]]
|
|
24
|
+
[[ "$out" == *"[REDACTED:secret]"* ]] || return 1
|
|
25
25
|
[[ "$out" != *"$tok"* ]]
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -29,7 +29,7 @@ setup() {
|
|
|
29
29
|
local tok out
|
|
30
30
|
tok="ghp_$(printf '0%.0s' $(seq 1 36))"
|
|
31
31
|
out=$(printf '%s' "$tok" | lineage_redact 4000 true)
|
|
32
|
-
[[ "$out" == *"[REDACTED:secret]"* ]]
|
|
32
|
+
[[ "$out" == *"[REDACTED:secret]"* ]] || return 1
|
|
33
33
|
[[ "$out" != *"$tok"* ]]
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -37,8 +37,8 @@ setup() {
|
|
|
37
37
|
local val out
|
|
38
38
|
val=$(printf 'x%.0s' $(seq 1 24))
|
|
39
39
|
out=$(printf '%s' "API_TOKEN=${val}" | lineage_redact 4000 true)
|
|
40
|
-
[[ "$out" == *"API_TOKEN="* ]]
|
|
41
|
-
[[ "$out" == *"[REDACTED:secret]"* ]]
|
|
40
|
+
[[ "$out" == *"API_TOKEN="* ]] || return 1
|
|
41
|
+
[[ "$out" == *"[REDACTED:secret]"* ]] || return 1
|
|
42
42
|
[[ "$out" != *"$val"* ]]
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -52,7 +52,7 @@ setup() {
|
|
|
52
52
|
@test "content longer than the cap is truncated with a marker" {
|
|
53
53
|
local out
|
|
54
54
|
out=$(printf '%s' "abcdefghij" | lineage_redact 4 true)
|
|
55
|
-
[[ "$out" == "abcd"* ]]
|
|
55
|
+
[[ "$out" == "abcd"* ]] || return 1
|
|
56
56
|
[[ "$out" == *"truncated 6 chars"* ]]
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -90,7 +90,7 @@ seed_start_ms_ago() {
|
|
|
90
90
|
@test "session_tracker_now_ms returns 13-digit epoch milliseconds" {
|
|
91
91
|
local ms
|
|
92
92
|
ms=$(session_tracker_now_ms)
|
|
93
|
-
[[ "$ms" =~ ^[0-9]+$ ]]
|
|
93
|
+
[[ "$ms" =~ ^[0-9]+$ ]] || return 1
|
|
94
94
|
[ "${#ms}" -eq 13 ]
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -231,7 +231,7 @@ seed_start_ms_ago() {
|
|
|
231
231
|
seed_start_ms_ago "$sid" 3000
|
|
232
232
|
local dur
|
|
233
233
|
dur=$(session_tracker_duration_ms "$sid")
|
|
234
|
-
[[ "$dur" =~ ^[0-9]+$ ]]
|
|
234
|
+
[[ "$dur" =~ ^[0-9]+$ ]] || return 1
|
|
235
235
|
[ "$dur" -ge 3000 ]
|
|
236
236
|
}
|
|
237
237
|
|
|
@@ -285,7 +285,7 @@ seed_start_ms_ago() {
|
|
|
285
285
|
|
|
286
286
|
local out
|
|
287
287
|
out=$(session_tracker_build_duration_context "$sid")
|
|
288
|
-
[[ "$out" == *"turn 3"* ]]
|
|
288
|
+
[[ "$out" == *"turn 3"* ]] || return 1
|
|
289
289
|
[[ "$out" == *"2m 34s"* ]]
|
|
290
290
|
}
|
|
291
291
|
|
|
@@ -294,7 +294,7 @@ seed_start_ms_ago() {
|
|
|
294
294
|
rm -f "${ONLOOKER_SESSION_TRACKERS_DIR}/${sid}"
|
|
295
295
|
local out
|
|
296
296
|
out=$(session_tracker_build_duration_context "$sid")
|
|
297
|
-
[[ "$out" == *"turn 1"* ]]
|
|
297
|
+
[[ "$out" == *"turn 1"* ]] || return 1
|
|
298
298
|
[[ "$out" == *"0s"* ]]
|
|
299
299
|
}
|
|
300
300
|
|