@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.
Files changed (112) hide show
  1. package/.claude/skills/writing-tests/SKILL.md +27 -0
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.github/workflows/release.yml +14 -14
  4. package/.release-please-manifest.json +7 -7
  5. package/AGENTS.md +116 -0
  6. package/CHANGELOG.md +175 -0
  7. package/CLAUDE.md +5 -0
  8. package/docs/lesson-promotion-pipeline.md +6 -6
  9. package/docs/superpowers/plans/2026-08-10-lesson-confirmation.md +1273 -0
  10. package/docs/superpowers/plans/2026-08-11-lesson-judging.md +1232 -0
  11. package/docs/superpowers/plans/2026-08-11-lesson-unconfirm.md +458 -0
  12. package/docs/superpowers/plans/2026-08-12-author-key.md +567 -0
  13. package/docs/superpowers/plans/2026-08-13-approved-pool.md +747 -0
  14. package/docs/superpowers/plans/2026-08-13-librarian-cluster.md +392 -0
  15. package/docs/superpowers/plans/2026-08-14-criterion-scores-consumers.md +1374 -0
  16. package/docs/superpowers/plans/2026-08-14-criterion-scores-schema.md +277 -0
  17. package/docs/superpowers/plans/2026-08-16-cartographer-undocumented-entity.md +741 -0
  18. package/docs/superpowers/specs/2026-08-10-lesson-confirmation-design.md +213 -0
  19. package/docs/superpowers/specs/2026-08-11-lesson-judging-design.md +267 -0
  20. package/docs/superpowers/specs/2026-08-11-lesson-unconfirm-design.md +152 -0
  21. package/docs/superpowers/specs/2026-08-12-author-key-design.md +317 -0
  22. package/docs/superpowers/specs/2026-08-13-approved-pool-design.md +232 -0
  23. package/docs/superpowers/specs/2026-08-14-criterion-scores-design.md +254 -0
  24. package/docs/superpowers/specs/2026-08-16-cartographer-undocumented-entity-design.md +249 -0
  25. package/package.json +2 -2
  26. package/plugins/assayer/.claude-plugin/plugin.json +1 -1
  27. package/plugins/assayer/CHANGELOG.md +7 -0
  28. package/plugins/assayer/scripts/lib/assayer-config.sh +6 -0
  29. package/plugins/cartographer/.claude-plugin/plugin.json +1 -1
  30. package/plugins/cartographer/CHANGELOG.md +54 -0
  31. package/plugins/cartographer/README.md +56 -1
  32. package/plugins/cartographer/config.json +7 -1
  33. package/plugins/cartographer/scripts/hooks/cartographer-post-write.sh +4 -4
  34. package/plugins/cartographer/scripts/hooks/cartographer-session-start.sh +7 -4
  35. package/plugins/cartographer/scripts/lib/cartographer-collect.sh +8 -1
  36. package/plugins/cartographer/scripts/lib/cartographer-config.sh +26 -0
  37. package/plugins/cartographer/scripts/lib/cartographer-events.sh +90 -0
  38. package/plugins/cartographer/scripts/lib/cartographer-filter.sh +105 -0
  39. package/plugins/cartographer/scripts/lib/cartographer-omission.sh +130 -0
  40. package/plugins/cartographer/scripts/lib/cartographer-resolve.sh +132 -0
  41. package/plugins/cartographer/scripts/run-audit.sh +136 -53
  42. package/plugins/cartographer/skills/cartographer/SKILL.md +33 -9
  43. package/plugins/curator/.claude-plugin/plugin.json +1 -1
  44. package/plugins/curator/CHANGELOG.md +7 -0
  45. package/plugins/curator/scripts/lib/curator-emit.sh +2 -1
  46. package/plugins/historian/.claude-plugin/plugin.json +1 -1
  47. package/plugins/historian/CHANGELOG.md +7 -0
  48. package/plugins/historian/scripts/lib/historian-emit.sh +2 -1
  49. package/plugins/librarian/.claude-plugin/plugin.json +1 -1
  50. package/plugins/librarian/CHANGELOG.md +71 -0
  51. package/plugins/librarian/config.json +30 -1
  52. package/plugins/librarian/docs/adr/002-agent-definitions-are-shared-assets.md +82 -0
  53. package/plugins/librarian/scripts/hooks/librarian-session-end.sh +28 -0
  54. package/plugins/librarian/scripts/hooks/librarian-session-start.sh +42 -12
  55. package/plugins/librarian/scripts/lib/librarian-author-key.sh +279 -0
  56. package/plugins/librarian/scripts/lib/librarian-cli.sh +346 -2
  57. package/plugins/librarian/scripts/lib/librarian-emit.sh +2 -1
  58. package/plugins/librarian/scripts/lib/librarian-lesson-judge.sh +349 -0
  59. package/plugins/librarian/scripts/lib/librarian-lesson-promote.sh +225 -0
  60. package/plugins/librarian/scripts/lib/librarian-lesson-review.sh +312 -0
  61. package/plugins/librarian/scripts/lib/librarian-lesson-rubric.sh +56 -0
  62. package/plugins/librarian/scripts/lib/librarian-lesson-storage.sh +82 -13
  63. package/plugins/librarian/scripts/lib/librarian-lesson-transform.sh +6 -1
  64. package/plugins/librarian/scripts/lib/librarian-lesson-validate.sh +131 -62
  65. package/plugins/librarian/skills/librarian/SKILL.md +98 -1
  66. package/plugins/tribunal/.claude-plugin/plugin.json +1 -1
  67. package/plugins/tribunal/CHANGELOG.md +63 -0
  68. package/plugins/tribunal/agents/tribunal-judge-adversarial.md +34 -0
  69. package/plugins/tribunal/agents/tribunal-judge-security.md +33 -0
  70. package/plugins/tribunal/agents/tribunal-judge-standard.md +12 -0
  71. package/plugins/tribunal/config.json +1 -0
  72. package/plugins/tribunal/scripts/lib/tribunal-aggregate.sh +154 -9
  73. package/plugins/tribunal/scripts/lib/tribunal-gate.sh +195 -16
  74. package/plugins/tribunal/skills/tribunal/SKILL.md +5 -3
  75. package/scripts/lib/prompt-rules.sh +6 -1
  76. package/test/bats/archivist-inject.bats +1 -1
  77. package/test/bats/assayer-extract.bats +2 -2
  78. package/test/bats/bursar-session-start.bats +3 -3
  79. package/test/bats/cartographer-config.bats +43 -0
  80. package/test/bats/cartographer-events.bats +267 -0
  81. package/test/bats/cartographer-filter.bats +169 -0
  82. package/test/bats/cartographer-lock.bats +3 -3
  83. package/test/bats/cartographer-omission.bats +202 -0
  84. package/test/bats/cartographer-resolve.bats +287 -0
  85. package/test/bats/cartographer-run-audit.bats +325 -0
  86. package/test/bats/compass-sanitizer.bats +11 -11
  87. package/test/bats/compass-transcript.bats +2 -2
  88. package/test/bats/config.bats +15 -15
  89. package/test/bats/curator-session-start.bats +10 -3
  90. package/test/bats/emit-payload-default.bats +52 -0
  91. package/test/bats/governor-ledger.bats +1 -1
  92. package/test/bats/historian-prompt-submit.bats +1 -1
  93. package/test/bats/inspector-post-write-hook.bats +4 -4
  94. package/test/bats/librarian-author-key.bats +477 -0
  95. package/test/bats/librarian-cli.bats +16 -16
  96. package/test/bats/librarian-lesson-judge.bats +884 -0
  97. package/test/bats/librarian-lesson-promote.bats +552 -0
  98. package/test/bats/librarian-lesson-review.bats +900 -0
  99. package/test/bats/librarian-lesson-transform.bats +17 -3
  100. package/test/bats/librarian-session-end.bats +161 -0
  101. package/test/bats/librarian-session-start.bats +36 -2
  102. package/test/bats/lineage-config.bats +1 -1
  103. package/test/bats/lineage-redact.bats +5 -5
  104. package/test/bats/session-tracker.bats +4 -4
  105. package/test/bats/tribunal-aggregate.bats +354 -1
  106. package/test/bats/tribunal-gate.bats +631 -0
  107. package/test/bats/tribunal-judge-agents.bats +119 -0
  108. package/test/bats/tribunal-jury.bats +1 -1
  109. package/test/bats/turn-tracker.bats +1 -1
  110. package/test/bats/warden-sanitizer.bats +3 -3
  111. package/test/bats/worktree-tracker.bats +2 -2
  112. 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 finds an artifact already promoted into the approved pool" {
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 '{artifact_id: "01KZ45MKAM734ZS7JK24D2DK0R"}' \
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 0 ]
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
+ }
@@ -43,6 +43,6 @@ setup() {
43
43
  lineage_config_load ""
44
44
  run lineage_config_ignore_globs
45
45
  [ "$status" -eq 0 ]
46
- [[ "$output" == *"node_modules"* ]]
46
+ [[ "$output" == *"node_modules"* ]] || return 1
47
47
  [[ "$output" == *".lock"* ]]
48
48
  }
@@ -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