microsoft/shadow-frog-meditate
>- Clean and consolidate the shadow knowledge base. Scans all shadow files for duplicate discoveries (same claim, different wording), near-duplicates (one extends another), and conflicting entries (contradicting claims). Merges duplicates, resolves conflicts by investigating the code, and asks the user only when resolution is unclear. Invoke periodically to keep the shadow focused and free of noise.
npx skills add https://github.com/microsoft/ShadowFrog --skill shadow-frog-meditate
Shadow hygiene — deduplicate, merge, and resolve conflicts across the
entire .shadow/ knowledge base. Prerequisite: .shadow/ exists with
discoveries.
Over time, shadows accumulate noise:
or one was wrong)
_cross/ entrysaying the same thing
This noise confuses downstream agents and dilutes signal. Meditate cleans
it up.
Use parallel subagents to scan the shadow. Each subagent handles a batch
of shadow files.
Not every file needs scanning. To reduce cost:
_meta/state.jsonlast_update_at against file modification times
For the first meditate after a large dream run, most files will need
scanning. For incremental meditation after small updates, this can
reduce scope by 80%+.
For each per-file shadow (e.g., src/auth.py.md):
## symbol headingSubagents must output findings as one JSON object per line so the
orchestrator can auto-apply resolutions. This is critical for automation —
prose recommendations require manual interpretation.
{"action": "merge", "file": "src/auth.py.md", "symbol": "authenticate_user", "keep": "- silently returns None on expired tokens...", "remove": "- returns None when token expires...", "merged": "- authenticate_user() silently returns None on expired tokens instead of raising. 3 of 7 callers don't check.\n _(verified, source: exploration)_", "reason": "duplicate: same claim, different wording"}
{"action": "merge", "file": "src/db.py.md", "symbol": "connect", "keep": "- connection pool exhaustion...", "remove": "- pool runs out...", "merged": "...", "reason": "near-duplicate: first extends second"}
{"action": "conflict", "file": "src/auth.py.md", "symbol": "validate_token", "entry_a": "- raises ValueError...", "entry_b": "- returns False...", "resolution": "verified_a", "reason": "code inspection: line 42 raises ValueError"}
{"action": "conflict", "file": "src/cache.py.md", "symbol": "invalidate", "entry_a": "...", "entry_b": "...", "resolution": "escalate", "reason": "both claims have evidence, needs user input"}
{"action": "move_to_cross", "file": "src/auth.py.md", "symbol": "validate_token", "entry": "- all validators share...", "cross_slug": "shared-validation-pattern", "reason": "cross-scope: involves 4 files"}
Fields:
action: merge | conflict | move_to_cross | move_from_crossfile: shadow file path relative to .shadow/symbol: the ##/### heading the discovery lives underkeep: the discovery text to keep (for merge)remove: the discovery text to delete (for merge)merged: the final merged text (for merge)resolution: verified_a | verified_b | escalate (for conflict)reason: human-readable explanationThe orchestrator collects all lines, applies merge and conflict
actions automatically, and presents escalate items to the user.
After per-file scanning:
_cross/*.md discovery, check if any per-file discoverymakes the same or overlapping claim
_prefs.md preference, check if any per-file discoveryor _cross/ entry duplicates it
expired tokens" and "Silently returns None when token expires" are
duplicates.
behaviors are distinct, not duplicates. E.g., "returns None on
expired tokens" vs "uses constant-time comparison" — these are
unrelated observations about the same function.
Also involves: — two discoveries with overlappingAlso involves: refs are more likely related.
Process each finding by type.
Combine into a single discovery:
source: user > source: interaction > source: explorationverified > uncertain > refutedAlso involves: refs (union of both)Example:
BEFORE (two entries under same symbol):
- authenticate_user() returns None on expired tokens.
_(verified, source: exploration)_
- When the token is expired, authenticate_user silently returns None
instead of raising. 3 of 7 callers don't check.
_(verified, source: exploration)_
AFTER (merged):
- authenticate_user() silently returns None on expired tokens instead
of raising. 3 of 7 callers don't check the return value.
_(verified, source: exploration)_
The broader discovery absorbs the narrower one:
When two discoveries contradict each other:
file::symbol locationverified, the incorrect one refutedto reflect the current behavior and mark it verified
If investigation takes more than a few minutes without resolution:
(conflict unresolved — needs user input)When the same discovery exists in both a per-file shadow and _cross/:
_cross/, remove from per-file_cross/When a per-file discovery duplicates a _prefs.md entry:
in _prefs.md, remove from per-file
both (they serve different purposes)
After all resolutions, print a summary:
Meditate Summary
================
Files scanned: 42
Duplicates merged: 7
Near-dupes absorbed: 3
Conflicts resolved: 2
Conflicts escalated: 1
Cross-scope fixed: 2
Total entries removed: 12
Escalated (needs your input):
src/auth.py::validate_token
- "raises ValueError on invalid format" vs "returns False on invalid format"
Both claims have evidence. Which behavior is correct?
For large shadows (20+ files), use parallel subagents:
merge actions: use edit tool with remove as old_str,replace keep with merged
conflict actions where resolution is verified_a orverified_b — mark the loser refuted
escalate items for user reviewFor smaller shadows, run everything in a single pass — the scan output
format is still useful for traceability.
_dreams/)Meditate performs consistency checks and field repair on _dreams/. It
does NOT delete or rewrite report prose (those are historical records),
but it DOES fix missing/wrong metadata in the index.
_dreams/ has a row in_dreams/_index.md, and every row in the index has a matching folder.
Fix mismatches (add missing rows, remove orphaned rows).
_dreams/<id>/ should contain at minimuma report.md. Flag any empty directories.
base_commit in a report's frontmatter is morethan 100 commits behind current HEAD, add a note to the index:
⚠️ patch may not apply cleanly. Check with:
git rev-list <base_commit>..HEAD --count 2>/dev/null
Dream report: _dreams/<id>/ reference, verify that dream folder
exists. Remove dangling references.
Dream subagents sometimes write incomplete index rows (e.g., unknown
category/verdict, generic titles). meditate-repair.py (below)
auto-resolves these by reading each experiment's report.md frontmatter,
manifest.json, and verdict-section signals.
Do NOT hand-edit 50+ rows. Use the script. The agent's job is to
surface what the script CAN'T auto-fix:
report.md's dream_id (frontmatter orbody) doesn't match the folder name, the report was copy-pasted from
another experiment. The script prints these to stderr and skips them.
Do NOT auto-fix corruption — the content is wrong, not just the ID.
Log them in the meditate summary for user review.
manifest.json exists for anexperiment, the script falls back to slug heuristics (-extend,
-fix, -deeper, -improve, -integration, -cleanup, -metrics,
-remaining strongly suggest compounding from a sibling). If the
heuristic match is not high-confidence, flag for user review rather
than guessing.
SKILL_DIR=""
for DIR in .github/skills/shadow-frog-meditate \
.claude/skills/shadow-frog-meditate; do
[ -d "$DIR" ] && SKILL_DIR="$DIR" && break
done
if [ -n "$SKILL_DIR" ] && [ -x "$SKILL_DIR/meditate-repair.py" ]; then
python3 "$SKILL_DIR/meditate-repair.py"
else
echo "meditate-repair.py not found; falling back to manual scan." >&2
fi
What it does:
.shadow/_dreams/_index.md to .bak firstdream_id != folder name) andprints them to stderr — these rows are skipped, you resolve manually
unknown/empty category/verdict/title,resolves the canonical value from the report's frontmatter, manifest,
or verdict section signals
"Dream t##: <slug>") with the first # H1 or ## Summary line
Verdict detection order is `manifest > VERDICT_SECTION signals >
whole-body signals`. Dead-end signals are checked BEFORE useful signals
so not useful doesn't match useful.
Idempotent — safe to rerun until output reports 0 repaired.
Do NOT delete dream reports during meditate — only the user decides
what to keep or discard (via Phase 7 review or manual cleanup).
source: user discovery without asking — userknowledge is the highest trust. If it conflicts with `source:
exploration`, investigate thoroughly before concluding the user was wrong.
Also involves: refs — when merging, take the union._index.md after removing entries (discovery counts change)._meta/state.json — set last_update_type: "meditate"._prefs.md placement unless a pref is clearlyduplicated verbatim in a per-file shadow.
**Every merged or rewritten discovery must exactly follow the canonical
format in /shadow-frog** (Discovery Format, Cross-Cutting, Preferences).
Re-read both the original entries and the spec before writing — a
malformed discovery is worse than a duplicate; it breaks the viewer
parser and downstream agents.
Meditate-specific rules:
labels: [...] from both entries.Also involves: file::symbol from bothentries (union, not intersection).
Dream report: _dreams/<id>/ references must survive the merge —re-attach to the merged entry if either original had one.
verified/uncertain/refuted) is taken from the strongersource: user ≻ interaction ≻ verified exploration ≻ uncertain.
more behavioral wording when entries differ in style.
Take microsoft/shadow-frog-meditate from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.