@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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# 1. discover — collect all auditable files
|
|
9
9
|
# 2. extract — per-file content hash (incremental cache key)
|
|
10
10
|
# 3. relate — contradiction + dead_rule analysis (LLM)
|
|
11
|
-
# 4. synthesize — stale_ref + scope_collision +
|
|
11
|
+
# 4. synthesize — stale_ref + scope_collision + undocumented_entity + hash
|
|
12
12
|
# 5. emit — persist findings atomically, emit events for new findings
|
|
13
13
|
#
|
|
14
14
|
# Environment:
|
|
@@ -28,13 +28,18 @@ source "$PLUGIN_ROOT/scripts/lib/cartographer-config.sh"
|
|
|
28
28
|
source "$PLUGIN_ROOT/scripts/lib/cartographer-ulid.sh"
|
|
29
29
|
source "$PLUGIN_ROOT/scripts/lib/cartographer-project-key.sh"
|
|
30
30
|
source "$PLUGIN_ROOT/scripts/lib/cartographer-events.sh"
|
|
31
|
+
source "$PLUGIN_ROOT/scripts/lib/cartographer-resolve.sh"
|
|
32
|
+
source "$PLUGIN_ROOT/scripts/lib/cartographer-filter.sh"
|
|
31
33
|
source "$PLUGIN_ROOT/scripts/lib/cartographer-collect.sh"
|
|
32
34
|
source "$PLUGIN_ROOT/scripts/lib/cartographer-analyze.sh"
|
|
35
|
+
source "$PLUGIN_ROOT/scripts/lib/cartographer-omission.sh"
|
|
33
36
|
|
|
34
37
|
CARTOGRAPHER_DIR="${CARTOGRAPHER_DIR:?CARTOGRAPHER_DIR must be set}"
|
|
35
38
|
TRIGGER="${CARTOGRAPHER_TRIGGER:-manual}"
|
|
36
39
|
TARGET_FILE="${CARTOGRAPHER_TARGET_FILE:-}"
|
|
37
40
|
REPO_ROOT="${CARTOGRAPHER_REPO_ROOT:-$(pwd)}"
|
|
41
|
+
TYPE_FILTER="${CARTOGRAPHER_TYPE_FILTER:-}"
|
|
42
|
+
SCOPE_PATH="${CARTOGRAPHER_SCOPE_PATH:-}"
|
|
38
43
|
AUDIT_ID=$(cartographer_ulid)
|
|
39
44
|
START_TS=$(date +%s)
|
|
40
45
|
|
|
@@ -49,6 +54,16 @@ PHASES_COMPLETED=()
|
|
|
49
54
|
PHASES_FAILED=()
|
|
50
55
|
ALL_FINDINGS="[]"
|
|
51
56
|
|
|
57
|
+
# Load config before any accessor runs. Without this every accessor below falls
|
|
58
|
+
# through to its hardcoded default, so exclude_paths, the timeouts, the models,
|
|
59
|
+
# and the token caps were all silently unconfigurable — the orchestrator was the
|
|
60
|
+
# one place that never loaded (ecosystem-88v).
|
|
61
|
+
#
|
|
62
|
+
# This is the single load for the whole audit. The analysis sub-shells take
|
|
63
|
+
# their settings as positional parameters and read no config of their own, so
|
|
64
|
+
# the values resolved here are the ones that take effect everywhere.
|
|
65
|
+
cartographer_config_load "$REPO_ROOT"
|
|
66
|
+
|
|
52
67
|
_phase_timeout=$(cartographer_config_phase_timeout)
|
|
53
68
|
_total_timeout=$(cartographer_config_total_timeout)
|
|
54
69
|
log() { printf '[cartographer] %s\n' "$*" >>"$CARTOGRAPHER_DIR/audit.log" 2>&1; }
|
|
@@ -59,11 +74,36 @@ _model_synthesis=$(cartographer_config_model_synthesis)
|
|
|
59
74
|
_max_tokens_extraction=$(cartographer_config_max_output_tokens_extraction)
|
|
60
75
|
_max_tokens_synthesis=$(cartographer_config_max_output_tokens_synthesis)
|
|
61
76
|
_exclude_json=$(cartographer_config_exclude_paths)
|
|
77
|
+
_undocumented_enabled=$(cartographer_config_undocumented_enabled)
|
|
78
|
+
_undocumented_globs=$(cartographer_config_undocumented_globs)
|
|
79
|
+
_undocumented_exclude=$(cartographer_config_undocumented_exclude)
|
|
80
|
+
_undocumented_max=$(cartographer_config_undocumented_max_findings)
|
|
62
81
|
|
|
63
82
|
emit_safe() {
|
|
64
83
|
cartographer_emit_event "$1" "$2" 2>>"$CARTOGRAPHER_DIR/audit.log" || true
|
|
65
84
|
}
|
|
66
85
|
|
|
86
|
+
# Announce one finding the resolution sweep retired.
|
|
87
|
+
#
|
|
88
|
+
# Passed to cartographer_resolve_absent_findings by name rather than called
|
|
89
|
+
# after it, so the announcement happens inside the sweep's own guards — a run
|
|
90
|
+
# that must not resolve cannot announce either. Handing over a function name
|
|
91
|
+
# also keeps cartographer-resolve.sh free of any dependency on the event bus.
|
|
92
|
+
_emit_resolved() {
|
|
93
|
+
emit_safe "cartographer.issue.resolved" \
|
|
94
|
+
"$(cartographer_issue_resolved_payload "$AUDIT_ID" "$1")"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Reject an unrecognized type rather than filtering everything away. An empty
|
|
98
|
+
# audit is indistinguishable from a clean repo, so a typo would read as good
|
|
99
|
+
# news — the same class of silent-nothing this flag was filed for.
|
|
100
|
+
if [[ -n "$TYPE_FILTER" ]] && ! cartographer_filter_valid_type "$TYPE_FILTER"; then
|
|
101
|
+
log "error: unknown type filter '${TYPE_FILTER}' (expected one of: ${CARTOGRAPHER_FINDING_TYPES})"
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
[[ -n "$TYPE_FILTER" ]] && log "filter type=${TYPE_FILTER}"
|
|
105
|
+
[[ -n "$SCOPE_PATH" ]] && log "filter scope=${SCOPE_PATH}"
|
|
106
|
+
|
|
67
107
|
# ── Phase 1: Discover ──────────────────────────────────────────────────────────
|
|
68
108
|
run_discover() {
|
|
69
109
|
log "phase=discover starting"
|
|
@@ -82,6 +122,14 @@ run_discover() {
|
|
|
82
122
|
GLOBAL_FILES=$(printf '%s\n' "$raw_global" | grep -v '^$' | jq -R . | jq -s .)
|
|
83
123
|
fi
|
|
84
124
|
|
|
125
|
+
# Scope narrows what gets analyzed, not what the repo root is: stale_ref
|
|
126
|
+
# resolves path tokens against the real root, and re-rooting would make
|
|
127
|
+
# every reference outside the scope look broken.
|
|
128
|
+
if [[ -n "$SCOPE_PATH" ]]; then
|
|
129
|
+
DISCOVERED_FILES=$(cartographer_filter_scope \
|
|
130
|
+
"$DISCOVERED_FILES" "$REPO_ROOT" "$SCOPE_PATH")
|
|
131
|
+
fi
|
|
132
|
+
|
|
85
133
|
local file_count
|
|
86
134
|
file_count=$(printf '%s' "$DISCOVERED_FILES" | jq 'length')
|
|
87
135
|
log "phase=discover files=${file_count}"
|
|
@@ -119,11 +167,19 @@ run_extract() {
|
|
|
119
167
|
# ── Phase 3: Relate (contradiction + dead_rule) ────────────────────────────────
|
|
120
168
|
run_relate() {
|
|
121
169
|
log "phase=relate starting"
|
|
170
|
+
|
|
171
|
+
# One LLM pass emits both contradiction and dead_rule, so asking for either
|
|
172
|
+
# runs it and the unwanted type is filtered out of the result below.
|
|
173
|
+
if ! cartographer_filter_wants "contradiction" "$TYPE_FILTER"; then
|
|
174
|
+
log "phase=relate skipped by type filter"
|
|
175
|
+
RELATE_FINDINGS="[]"
|
|
176
|
+
PHASES_COMPLETED+=("relate")
|
|
177
|
+
return 0
|
|
178
|
+
fi
|
|
179
|
+
|
|
122
180
|
local findings
|
|
123
181
|
findings=$($_TIMEOUT_CMD "$_phase_timeout" bash -c \
|
|
124
|
-
"source '$PLUGIN_ROOT/scripts/lib/cartographer-
|
|
125
|
-
source '$PLUGIN_ROOT/scripts/lib/cartographer-analyze.sh'
|
|
126
|
-
cartographer_config_load '$REPO_ROOT'
|
|
182
|
+
"source '$PLUGIN_ROOT/scripts/lib/cartographer-analyze.sh'
|
|
127
183
|
cartographer_analyze_contradiction '$DISCOVERED_FILES' \
|
|
128
184
|
'$_model_extraction' '$_max_tokens_extraction' '$_phase_timeout'" \
|
|
129
185
|
2>>"$CARTOGRAPHER_DIR/audit.log") || {
|
|
@@ -131,7 +187,7 @@ run_relate() {
|
|
|
131
187
|
PHASES_FAILED+=("relate")
|
|
132
188
|
return 1
|
|
133
189
|
}
|
|
134
|
-
RELATE_FINDINGS
|
|
190
|
+
RELATE_FINDINGS=$(cartographer_filter_findings "${findings:-[]}" "$TYPE_FILTER")
|
|
135
191
|
local count
|
|
136
192
|
count=$(printf '%s' "$RELATE_FINDINGS" | jq 'length' 2>/dev/null || printf '0')
|
|
137
193
|
log "phase=relate findings=${count}"
|
|
@@ -142,22 +198,39 @@ run_relate() {
|
|
|
142
198
|
run_synthesize() {
|
|
143
199
|
log "phase=synthesize starting"
|
|
144
200
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
201
|
+
# Each analyzer skipped under a type filter is an LLM call not made, which
|
|
202
|
+
# is where the flag earns its keep.
|
|
203
|
+
local stale_findings="[]" scope_findings="[]"
|
|
204
|
+
if cartographer_filter_wants "stale_ref" "$TYPE_FILTER"; then
|
|
205
|
+
stale_findings=$($_TIMEOUT_CMD "$_phase_timeout" bash -c \
|
|
206
|
+
"source '$PLUGIN_ROOT/scripts/lib/cartographer-analyze.sh'
|
|
207
|
+
cartographer_analyze_stale_ref '$DISCOVERED_FILES' '$REPO_ROOT' \
|
|
208
|
+
'$_model_synthesis' '$_max_tokens_synthesis' '$_phase_timeout'" \
|
|
209
|
+
2>>"$CARTOGRAPHER_DIR/audit.log") || stale_findings="[]"
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
if cartographer_filter_wants "scope_collision" "$TYPE_FILTER"; then
|
|
213
|
+
scope_findings=$($_TIMEOUT_CMD "$_phase_timeout" bash -c \
|
|
214
|
+
"source '$PLUGIN_ROOT/scripts/lib/cartographer-analyze.sh'
|
|
215
|
+
cartographer_analyze_scope_collision '$GLOBAL_FILES' '$DISCOVERED_FILES' \
|
|
216
|
+
'$_model_synthesis' '$_max_tokens_synthesis' '$_phase_timeout'" \
|
|
217
|
+
2>>"$CARTOGRAPHER_DIR/audit.log") || scope_findings="[]"
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# Disk → doc. Skipped on targeted post-write audits: DISCOVERED_FILES is a
|
|
221
|
+
# single file there, so grepping it for every entity name would report
|
|
222
|
+
# nearly the whole enumeration as undocumented, and the emit phase would
|
|
223
|
+
# dedup-sentinel those false findings permanently. scope_collision already
|
|
224
|
+
# no-ops on targeted runs for the same reason.
|
|
225
|
+
local omission_findings="[]"
|
|
226
|
+
if [[ -z "$TARGET_FILE" && "$_undocumented_enabled" == "true" ]] \
|
|
227
|
+
&& cartographer_filter_wants "undocumented_entity" "$TYPE_FILTER"; then
|
|
228
|
+
omission_findings=$($_TIMEOUT_CMD "$_phase_timeout" bash -c \
|
|
229
|
+
"source '$PLUGIN_ROOT/scripts/lib/cartographer-omission.sh'
|
|
230
|
+
cartographer_analyze_undocumented_entity '$DISCOVERED_FILES' '$REPO_ROOT' \
|
|
231
|
+
'$_undocumented_globs' '$_undocumented_exclude' '$_undocumented_max'" \
|
|
232
|
+
2>>"$CARTOGRAPHER_DIR/audit.log") || omission_findings="[]"
|
|
233
|
+
fi
|
|
161
234
|
|
|
162
235
|
# Merge all raw findings
|
|
163
236
|
local raw_all
|
|
@@ -165,7 +238,8 @@ run_synthesize() {
|
|
|
165
238
|
--argjson relate "${RELATE_FINDINGS:-[]}" \
|
|
166
239
|
--argjson stale "${stale_findings:-[]}" \
|
|
167
240
|
--argjson scope "${scope_findings:-[]}" \
|
|
168
|
-
|
|
241
|
+
--argjson omission "${omission_findings:-[]}" \
|
|
242
|
+
'$relate + $stale + $scope + $omission')
|
|
169
243
|
|
|
170
244
|
# Add finding_hash to each finding
|
|
171
245
|
ALL_FINDINGS="[]"
|
|
@@ -213,14 +287,11 @@ run_emit() {
|
|
|
213
287
|
now=$(date +%s)
|
|
214
288
|
|
|
215
289
|
if [[ -f "$dedup_sentinel" ]]; then
|
|
216
|
-
# Known finding —
|
|
290
|
+
# Known finding — refresh it atomically, no bus event. Refreshing
|
|
291
|
+
# also reopens it; see cartographer_refresh_finding for why that is
|
|
292
|
+
# load-bearing rather than tidiness.
|
|
217
293
|
(( known_count++ )) || true
|
|
218
|
-
|
|
219
|
-
local updated
|
|
220
|
-
updated=$(jq --argjson ts "$now" '.last_seen_at=$ts' "$finding_file" 2>/dev/null) || true
|
|
221
|
-
[[ -n "$updated" ]] && printf '%s\n' "$updated" >"${finding_file}.tmp" \
|
|
222
|
-
&& mv -f "${finding_file}.tmp" "$finding_file"
|
|
223
|
-
fi
|
|
294
|
+
cartographer_refresh_finding "$finding_file" "$now" || true
|
|
224
295
|
else
|
|
225
296
|
# New finding — write file atomically, emit bus event, then mark dedup
|
|
226
297
|
local with_ts
|
|
@@ -229,22 +300,20 @@ run_emit() {
|
|
|
229
300
|
printf '%s\n' "$with_ts" >"${finding_file}.tmp"
|
|
230
301
|
mv -f "${finding_file}.tmp" "$finding_file"
|
|
231
302
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
--arg summary "$fdesc" \
|
|
247
|
-
'{"audit_id":$audit_id,"finding_hash":$finding_hash,"finding_type":$finding_type,"severity":$severity,"affected_files":$affected_files,"summary":$summary}')"
|
|
303
|
+
# Branch on the builder rather than passing its output straight
|
|
304
|
+
# to emit_safe. A typeless finding makes it return non-zero, and
|
|
305
|
+
# an unchecked substitution would hand emit_safe an empty payload
|
|
306
|
+
# that cartographer_emit_event rejects silently — relocating the
|
|
307
|
+
# silence this fix exists to end (ecosystem-ci0). Its stderr is
|
|
308
|
+
# appended to the same audit.log emit_safe writes to, because the
|
|
309
|
+
# substitution runs before emit_safe and escapes that redirect.
|
|
310
|
+
local found_payload
|
|
311
|
+
if found_payload=$(cartographer_issue_found_payload \
|
|
312
|
+
"$AUDIT_ID" "$fhash" "$finding" 2>>"$CARTOGRAPHER_DIR/audit.log"); then
|
|
313
|
+
emit_safe "cartographer.issue.found" "$found_payload"
|
|
314
|
+
else
|
|
315
|
+
log "emit: no issue.found for ${fhash} — finding carries no type"
|
|
316
|
+
fi
|
|
248
317
|
|
|
249
318
|
touch "$dedup_sentinel"
|
|
250
319
|
(( new_count++ )) || true
|
|
@@ -253,6 +322,23 @@ run_emit() {
|
|
|
253
322
|
|
|
254
323
|
log "phase=emit new=${new_count} known=${known_count}"
|
|
255
324
|
|
|
325
|
+
# Retire findings this run did not observe. Guarded inside against targeted
|
|
326
|
+
# audits and partial runs, both of which see too little to treat absence as
|
|
327
|
+
# evidence the drift is gone.
|
|
328
|
+
local resolved_count
|
|
329
|
+
resolved_count=$(cartographer_resolve_absent_findings \
|
|
330
|
+
"$FINDINGS_DIR" "$START_TS" "$TARGET_FILE" "${#PHASES_FAILED[@]}" "" _emit_resolved)
|
|
331
|
+
log "phase=emit resolved=${resolved_count}"
|
|
332
|
+
|
|
333
|
+
# Only a run that actually swept may report a count. A targeted or partial
|
|
334
|
+
# run leaves the field off entirely rather than reporting 0, which would
|
|
335
|
+
# read as "swept, found nothing to retire". This asks the same predicate the
|
|
336
|
+
# sweep guards on, so the two cannot drift apart on what the run proved.
|
|
337
|
+
local resolved_arg=""
|
|
338
|
+
if cartographer_resolution_is_sound "$TARGET_FILE" "${#PHASES_FAILED[@]}"; then
|
|
339
|
+
resolved_arg="$resolved_count"
|
|
340
|
+
fi
|
|
341
|
+
|
|
256
342
|
local end_ts duration_ms total_count
|
|
257
343
|
end_ts=$(date +%s)
|
|
258
344
|
duration_ms=$(( (end_ts - START_TS) * 1000 ))
|
|
@@ -265,20 +351,17 @@ run_emit() {
|
|
|
265
351
|
--arg trigger "$TRIGGER" \
|
|
266
352
|
--argjson new_finding_count "$new_count" \
|
|
267
353
|
--argjson known_finding_count "$known_count" \
|
|
354
|
+
--argjson resolved_finding_count "$resolved_count" \
|
|
268
355
|
--argjson total_finding_count "$total_count" \
|
|
269
356
|
--argjson duration_ms "$duration_ms" \
|
|
270
357
|
--argjson phases_completed "$(printf '%s\n' "${PHASES_COMPLETED[@]}" | jq -R . | jq -s .)" \
|
|
271
358
|
--argjson phases_failed "$(printf '%s\n' "${PHASES_FAILED[@]:-}" | jq -R . | jq -s .)" \
|
|
272
|
-
'{"audit_id":$audit_id,"trigger":$trigger,"new_finding_count":$new_finding_count,"known_finding_count":$known_finding_count,"total_finding_count":$total_finding_count,"duration_ms":$duration_ms,"phases_completed":$phases_completed,"phases_failed":$phases_failed}' \
|
|
359
|
+
'{"audit_id":$audit_id,"trigger":$trigger,"new_finding_count":$new_finding_count,"known_finding_count":$known_finding_count,"resolved_finding_count":$resolved_finding_count,"total_finding_count":$total_finding_count,"duration_ms":$duration_ms,"phases_completed":$phases_completed,"phases_failed":$phases_failed}' \
|
|
273
360
|
>"${run_file}.tmp" && mv -f "${run_file}.tmp" "$run_file"
|
|
274
361
|
|
|
275
|
-
emit_safe "cartographer.audit.complete"
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
--argjson new_finding_count "$new_count" \
|
|
279
|
-
--argjson total_finding_count "$total_count" \
|
|
280
|
-
--argjson duration_ms "$duration_ms" \
|
|
281
|
-
'{"audit_id":$audit_id,"trigger":$trigger,"new_finding_count":$new_finding_count,"total_finding_count":$total_finding_count,"duration_ms":$duration_ms}')"
|
|
362
|
+
emit_safe "cartographer.audit.complete" \
|
|
363
|
+
"$(cartographer_audit_complete_payload \
|
|
364
|
+
"$AUDIT_ID" "$TRIGGER" "$new_count" "$total_count" "$duration_ms" "$resolved_arg")"
|
|
282
365
|
|
|
283
366
|
PHASES_COMPLETED+=("emit")
|
|
284
367
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cartographer
|
|
3
|
-
description: Audit CLAUDE.md, AGENTS.md, and .claude/rules/ instruction files for contradictions, stale references, dead rules, and
|
|
3
|
+
description: Audit CLAUDE.md, AGENTS.md, and .claude/rules/ instruction files for contradictions, stale references, dead rules, scope collisions, and entities no instruction file mentions. Runs a full audit in the foreground (with lock). Use when the user explicitly invokes /cartographer, or when they want immediate feedback after editing an instruction file. Supports --scope, --type, --verbose, --status, and --force flags.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Cartographer Skill
|
|
@@ -55,14 +55,15 @@ After the audit completes, read findings and render them to the conversation gro
|
|
|
55
55
|
|
|
56
56
|
### `/cartographer --verbose` — all findings
|
|
57
57
|
|
|
58
|
-
Shows ALL known findings (new + previously seen) from `$CARTOGRAPHER_DIR/findings
|
|
58
|
+
Shows ALL known findings (new + previously seen) from `$CARTOGRAPHER_DIR/findings/`, including ones already resolved — that is what makes this the verbose view. Resolved findings are tagged so "did my fix take?" is answerable here. Does NOT re-emit bus events — renders to in-conversation output only.
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
echo "## Known Cartographer Findings"
|
|
62
62
|
echo ""
|
|
63
63
|
for f in "$CARTOGRAPHER_DIR/findings/"*.json; do
|
|
64
64
|
[[ -f "$f" ]] || continue
|
|
65
|
-
jq -r '
|
|
65
|
+
jq -r '(if .resolved == true then "[RESOLVED] " else "" end) as $r |
|
|
66
|
+
"\($r)**[\(.severity | ascii_upcase)]** \(.type) — \(.description)\n Files: \(.file_a // "n/a") / \(.file_b // "n/a")\n Fix: \(.suggested_fix // "n/a")\n"' "$f" 2>/dev/null
|
|
66
67
|
done
|
|
67
68
|
```
|
|
68
69
|
|
|
@@ -86,7 +87,10 @@ fi
|
|
|
86
87
|
|
|
87
88
|
runs=$(ls "$CARTOGRAPHER_DIR/runs/" 2>/dev/null | wc -l | tr -d ' ')
|
|
88
89
|
total=$(ls "$CARTOGRAPHER_DIR/findings/" 2>/dev/null | wc -l | tr -d ' ')
|
|
89
|
-
|
|
90
|
+
open=$(cat "$CARTOGRAPHER_DIR/findings/"*.json 2>/dev/null \
|
|
91
|
+
| jq -s '[.[] | select(.resolved != true)] | length' 2>/dev/null || echo 0)
|
|
92
|
+
echo "Open findings: $open"
|
|
93
|
+
echo "Total findings on disk: $total (includes resolved)"
|
|
90
94
|
echo "Audit runs recorded: $runs"
|
|
91
95
|
```
|
|
92
96
|
|
|
@@ -107,18 +111,37 @@ fi
|
|
|
107
111
|
# Then proceed as a normal full audit
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
### `/cartographer --
|
|
114
|
+
### `/cartographer --type=<type>` — single finding type
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
|
|
116
|
+
Narrows the audit to one finding type: `contradiction`, `dead_rule`, `stale_ref`, `scope_collision`, or `undocumented_entity`. Export `CARTOGRAPHER_TYPE_FILTER` before running.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
export CARTOGRAPHER_TYPE_FILTER="stale_ref"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Analyzers that cannot produce the requested type are skipped entirely, so this is cheaper than a full audit — each skipped analyzer is an LLM call not made. Useful for re-checking one class of drift after fixing it without paying for the rest.
|
|
123
|
+
|
|
124
|
+
One caveat, because the saving is not uniform: `contradiction` and `dead_rule` come out of a single LLM pass, so asking for either runs that pass and discards the other type from the results. Requesting one of those two costs the same as running both; the other three types are genuinely skippable.
|
|
125
|
+
|
|
126
|
+
An unrecognized type is an error rather than a filter that matches nothing — an empty audit looks exactly like a clean repo, so a typo would read as good news.
|
|
114
127
|
|
|
115
128
|
### `/cartographer --scope=<path>` — scoped audit
|
|
116
129
|
|
|
117
|
-
Limits
|
|
130
|
+
Limits analysis to instruction files under `<path>`, relative to the repo root or absolute. Export `CARTOGRAPHER_SCOPE_PATH` before running.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
export CARTOGRAPHER_SCOPE_PATH="plugins/tribunal"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The scope narrows which files are analyzed; it does not become the repo root. `stale_ref` resolves path-like tokens against the real root, and re-rooting would report every reference outside the scope as broken.
|
|
137
|
+
|
|
138
|
+
Both flags compose, and both apply to manual runs only — the SessionStart and PostToolUse hooks always run unfiltered.
|
|
118
139
|
|
|
119
140
|
## Rendering Findings
|
|
120
141
|
|
|
121
|
-
After a manual audit completes, render findings grouped by severity
|
|
142
|
+
After a manual audit completes, render findings grouped by severity. Resolved
|
|
143
|
+
findings are skipped — a finding whose drift the user already fixed must stop
|
|
144
|
+
appearing here, or the advice to fix it never stops being given:
|
|
122
145
|
|
|
123
146
|
```bash
|
|
124
147
|
echo "## Cartographer Findings"
|
|
@@ -129,6 +152,7 @@ for severity in error warning informational; do
|
|
|
129
152
|
output=""
|
|
130
153
|
for f in "$CARTOGRAPHER_DIR/findings/"*.json; do
|
|
131
154
|
[[ -f "$f" ]] || continue
|
|
155
|
+
jq -e '.resolved != true' "$f" >/dev/null 2>&1 || continue
|
|
132
156
|
fsev=$(jq -r '.severity // "warning"' "$f" 2>/dev/null)
|
|
133
157
|
[[ "$fsev" != "$severity" ]] && continue
|
|
134
158
|
(( count++ )) || true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "curator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Maintenance layer for the user's typed auto-memory store. At every SessionStart, runs four cheap heuristic checks (date_decayed, path_broken, broken_index, orphaned_memory) against the memories at ~/.claude/projects/<encoded>/memory/ inside a wall-clock budget. Surfaces findings as a one-line pointer to /curator review; never edits the memory store directly. Builds on the Onlooker ecosystem plugin.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Onlooker Community",
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.3](https://github.com/onlooker-community/ecosystem/compare/curator-v0.2.2...curator-v0.2.3) (2026-08-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* make the local bats suite tell the truth :mag: ([#135](https://github.com/onlooker-community/ecosystem/issues/135)) ([f0763e0](https://github.com/onlooker-community/ecosystem/commit/f0763e09f3caf2d39c89f28befd12567af0af845))
|
|
9
|
+
|
|
3
10
|
## [0.2.2](https://github.com/onlooker-community/ecosystem/compare/curator-v0.2.1...curator-v0.2.2) (2026-08-02)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -39,7 +39,8 @@ _CURATOR_EVENT_JS="${_CURATOR_EVENT_JS:-$(_curator_resolve_event_js)}"
|
|
|
39
39
|
curator_emit() {
|
|
40
40
|
local event_type="${1:-}"
|
|
41
41
|
local session_id="${2:-}"
|
|
42
|
-
local payload="${3:-
|
|
42
|
+
local payload="${3:-}"
|
|
43
|
+
[ -z "$payload" ] && payload='{}'
|
|
43
44
|
|
|
44
45
|
[[ -z "$event_type" || -z "$session_id" ]] && return 0
|
|
45
46
|
[[ -z "$_CURATOR_EVENT_JS" || ! -f "$_CURATOR_EVENT_JS" ]] && return 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "historian",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Episodic memory layer. At SessionEnd, chunks and sanitizes the session transcript and stores chunks under $ONLOOKER_DIR/historian/<project-key>/sessions/ (default $HOME/.onlooker). On UserPromptSubmit, embeds the prompt and performs similarity retrieval over stored chunks to surface relevant past context. Builds on the Onlooker ecosystem plugin.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Onlooker Community",
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.3](https://github.com/onlooker-community/ecosystem/compare/historian-v0.3.2...historian-v0.3.3) (2026-08-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* make the local bats suite tell the truth :mag: ([#135](https://github.com/onlooker-community/ecosystem/issues/135)) ([f0763e0](https://github.com/onlooker-community/ecosystem/commit/f0763e09f3caf2d39c89f28befd12567af0af845))
|
|
9
|
+
|
|
3
10
|
## [0.3.2](https://github.com/onlooker-community/ecosystem/compare/historian-v0.3.1...historian-v0.3.2) (2026-08-01)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -39,7 +39,8 @@ _HISTORIAN_EVENT_JS="${_HISTORIAN_EVENT_JS:-$(_historian_resolve_event_js)}"
|
|
|
39
39
|
historian_emit() {
|
|
40
40
|
local event_type="${1:-}"
|
|
41
41
|
local session_id="${2:-}"
|
|
42
|
-
local payload="${3:-
|
|
42
|
+
local payload="${3:-}"
|
|
43
|
+
[ -z "$payload" ] && payload='{}'
|
|
43
44
|
|
|
44
45
|
[[ -z "$event_type" || -z "$session_id" ]] && return 0
|
|
45
46
|
[[ -z "$_HISTORIAN_EVENT_JS" || ! -f "$_HISTORIAN_EVENT_JS" ]] && return 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "librarian",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"description": "Consolidation layer between archivist's per-session artifacts and the user's durable typed memory store. Detects which session decisions, dead-ends, and open questions deserve to live across sessions, classifies them into the user/feedback/project/reference types, and queues them as proposals for explicit confirmation. Auto-promotion is opt-in. Builds on the Onlooker ecosystem plugin.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Onlooker Community",
|
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.13.2](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.13.1...librarian-v0.13.2) (2026-08-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **librarian:** put budget skips on the bus at last :satellite: ([#180](https://github.com/onlooker-community/ecosystem/issues/180)) ([bc91749](https://github.com/onlooker-community/ecosystem/commit/bc917495dab053cba825ea28ccdd1993fdacb3e9))
|
|
9
|
+
* **librarian:** stop stage 5 from holding a session open :hourglass: ([#178](https://github.com/onlooker-community/ecosystem/issues/178)) ([4f12a1e](https://github.com/onlooker-community/ecosystem/commit/4f12a1ee25300e0dc372542814e0922df5dbd335))
|
|
10
|
+
|
|
11
|
+
## [0.13.1](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.13.0...librarian-v0.13.1) (2026-08-15)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **librarian:** make proposals the sole dedup source, and say so :broom: ([#156](https://github.com/onlooker-community/ecosystem/issues/156)) ([729d9ea](https://github.com/onlooker-community/ecosystem/commit/729d9ea3e328c2734712cfc5adf556c6798334aa))
|
|
17
|
+
|
|
18
|
+
## [0.13.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.12.1...librarian-v0.13.0) (2026-08-15)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* **tribunal,librarian:** make rubric weights and min_pass floors real :straight_ruler: ([#150](https://github.com/onlooker-community/ecosystem/issues/150)) ([f8f8e28](https://github.com/onlooker-community/ecosystem/commit/f8f8e28b60f6d13d5a2a54e26ca284137b77f99a))
|
|
24
|
+
|
|
25
|
+
## [0.12.1](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.12.0...librarian-v0.12.1) (2026-08-14)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* **librarian:** close four follow-up defects from the promotion epic :broom: ([#148](https://github.com/onlooker-community/ecosystem/issues/148)) ([895df74](https://github.com/onlooker-community/ecosystem/commit/895df7430fa8ba75976d38964d061c21bff785d3))
|
|
31
|
+
|
|
32
|
+
## [0.12.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.11.0...librarian-v0.12.0) (2026-08-13)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
|
|
37
|
+
* **librarian:** land judged lessons in the approved pool :package: ([#146](https://github.com/onlooker-community/ecosystem/issues/146)) ([f7adc72](https://github.com/onlooker-community/ecosystem/commit/f7adc7263a985d4cb7695b46d6f9dbab77dce587))
|
|
38
|
+
|
|
39
|
+
## [0.11.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.10.0...librarian-v0.11.0) (2026-08-13)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### Features
|
|
43
|
+
|
|
44
|
+
* **librarian:** derive an author identity that cannot be linked across scopes :closed_lock_with_key: ([#144](https://github.com/onlooker-community/ecosystem/issues/144)) ([29b3042](https://github.com/onlooker-community/ecosystem/commit/29b3042ef6cd6e7582206cfea6fcf37ebe661021))
|
|
45
|
+
|
|
46
|
+
## [0.10.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.9.0...librarian-v0.10.0) (2026-08-12)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Features
|
|
50
|
+
|
|
51
|
+
* **librarian:** judge lessons before they leave the machine :balance_scale: ([#142](https://github.com/onlooker-community/ecosystem/issues/142)) ([a877527](https://github.com/onlooker-community/ecosystem/commit/a87752714961613b6b2bb768239c666f78d950f8))
|
|
52
|
+
|
|
53
|
+
## [0.9.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.8.0...librarian-v0.9.0) (2026-08-11)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
### Features
|
|
57
|
+
|
|
58
|
+
* **librarian:** let a human take back a lesson confirmation :leftwards_arrow_with_hook: ([#139](https://github.com/onlooker-community/ecosystem/issues/139)) ([a4c4ac8](https://github.com/onlooker-community/ecosystem/commit/a4c4ac8ed5b79f5596f4e22abd1d23a8b0d3e834))
|
|
59
|
+
|
|
60
|
+
## [0.8.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.7.1...librarian-v0.8.0) (2026-08-11)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
### Features
|
|
64
|
+
|
|
65
|
+
* **librarian:** let a human decide what leaves the machine :raised_hand: ([#137](https://github.com/onlooker-community/ecosystem/issues/137)) ([16d7673](https://github.com/onlooker-community/ecosystem/commit/16d76734833aee9c8818f84c1c1f704be4cdfc33))
|
|
66
|
+
|
|
67
|
+
## [0.7.1](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.7.0...librarian-v0.7.1) (2026-08-10)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
### Bug Fixes
|
|
71
|
+
|
|
72
|
+
* make the local bats suite tell the truth :mag: ([#135](https://github.com/onlooker-community/ecosystem/issues/135)) ([f0763e0](https://github.com/onlooker-community/ecosystem/commit/f0763e09f3caf2d39c89f28befd12567af0af845))
|
|
73
|
+
|
|
3
74
|
## [0.7.0](https://github.com/onlooker-community/ecosystem/compare/librarian-v0.6.1...librarian-v0.7.0) (2026-08-09)
|
|
4
75
|
|
|
5
76
|
|
|
@@ -18,7 +18,36 @@
|
|
|
18
18
|
},
|
|
19
19
|
"lesson_transform": {
|
|
20
20
|
"model": "claude-haiku-4-5-20251001",
|
|
21
|
-
"timeout_seconds": 20
|
|
21
|
+
"timeout_seconds": 20,
|
|
22
|
+
"total_budget_ms": 8000
|
|
23
|
+
},
|
|
24
|
+
"lesson_judging": {
|
|
25
|
+
"min_criterion_coverage": 0.6,
|
|
26
|
+
"rubrics": [
|
|
27
|
+
{
|
|
28
|
+
"id": "lesson-promotion",
|
|
29
|
+
"criteria": [
|
|
30
|
+
{ "name": "grounding", "weight": 0.45, "min_pass": 0.7 },
|
|
31
|
+
{ "name": "scope_accuracy", "weight": 0.35, "min_pass": 0.7 },
|
|
32
|
+
{ "name": "generality", "weight": 0.2, "min_pass": 0.6 }
|
|
33
|
+
],
|
|
34
|
+
"score_threshold": 0.75,
|
|
35
|
+
"judge_types": ["standard", "adversarial"],
|
|
36
|
+
"gate_policy": "majority"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "lesson-promotion-public",
|
|
40
|
+
"criteria": [
|
|
41
|
+
{ "name": "grounding", "weight": 0.32, "min_pass": 0.7 },
|
|
42
|
+
{ "name": "scope_accuracy", "weight": 0.24, "min_pass": 0.7 },
|
|
43
|
+
{ "name": "generality", "weight": 0.14, "min_pass": 0.6 },
|
|
44
|
+
{ "name": "disclosure", "weight": 0.3, "min_pass": 0.9 }
|
|
45
|
+
],
|
|
46
|
+
"score_threshold": 0.75,
|
|
47
|
+
"judge_types": ["standard", "adversarial"],
|
|
48
|
+
"gate_policy": "majority"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
22
51
|
},
|
|
23
52
|
"durability_filter": {
|
|
24
53
|
"marker_phrases": [
|