Learn an unfamiliar repo the way a senior engineer does: follow where the code
actually churns, find what that churn touches that the rest of the system leans
on, read the load-bearing code for real, then write down BOTH what you learned
(knowledge) and what should hold (policies) — as inert estate proposals a human
reviews, never as asserted fact.
Three BOUNDED phases, one wicked-crew unit each: (1) CHURN — sampled git churn →
ranked active areas (top-N cap); (2) HOTSPOTS — hotspots + blast...
9 stars
0 votes
0 copies
1 view
Added October 18, 2026
ai-agentspythongoshellnodetestinggitsecurity
Works with
cli
mcp
Security analysis
A92/100
mediumInstalls packages at runtime which could introduce malicious dependencies
Installs into .claude/skills of the current project.
Are you the author of Repo Learn?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mikeparcewski-repo-learn)
---
name: wicked-garden-repo-learn
description: |
Learn an unfamiliar repo the way a senior engineer does: follow where the code
actually churns, find what that churn touches that the rest of the system leans
on, read the load-bearing code for real, then write down BOTH what you learned
(knowledge) and what should hold (policies) — as inert estate proposals a human
reviews, never as asserted fact.
Three BOUNDED phases, one wicked-crew unit each: (1) CHURN — sampled git churn →
ranked active areas (top-N cap); (2) HOTSPOTS — hotspots + blast-radius via
wicked-garden-search cross-referenced with churn, then READ the intersection
through the estate graph (FetchContent, RetrieveEntity, TraverseGraph, Path);
(3) CAPTURE — derive memories AND policies and submit them through the estate
shim's `propose` (inert queue, safe under --readonly).
Use when: "learn this repo", "onboard me to this codebase", "what should I know
before working here", "derive memories / policies from this repo". For a
read-only narrative with no capture, use wicked-garden-search `narrate`.
metadata:
role: router
phases: "*"
archetypes: "*"
---
# wicked-garden-repo-learn — churn-led repo learning that captures back
A repo teaches you where to look if you follow two independent signals at once:
**temporal** (where the code churns) and **structural** (what the rest of the system
depends on). This skill walks the intersection, reads it for real, and **captures the
learning back** as estate proposals — knowledge (what is true, read from the code) and
policies (what should hold); a memory only for what you witnessed or were told. Garden ORCHESTRATES; estate GROUNDS (graph + read) and RECEIVES (the proposal
queue); a human APPROVES — every capture is an inert proposal the studio Memories /
Policies surfaces review, never an asserted fact.
**Why churn is a separate step:** estate's `RankHotspots` is *structural* PageRank, not
temporal — it never sees git history. Churn is the gap this skill fills, with a
**bounded, sampled** git-log method; the two signals meet in § Cross-reference.
## Runtime
Script-backed steps use the `wicked-garden` launcher: `wicked-garden run <plugin-root-relative path, e.g. scripts/…> [args]`. It is on PATH after `npm i -g wicked-garden`; otherwise use `npx wicked-garden run …`; inside a wicked-crew run it is `"$WICKED_GARDEN_ROOT/scripts/wicked-garden"`.
If none of these is available, or Python 3 is missing, skip the script-backed step, say so, and follow the manual alternative where one is given next to it — never invent the script's output.
Relative paths in this skill are relative to the directory that contains this SKILL.md.
## The three phases (= wicked-crew's three units)
| # | Phase (crew unit) | Produces | Where |
|---|-------------------|----------|-------|
| 1 | **churn** | ranked active paths/areas over a window, top-N capped | [refs/churn-sampling.md](refs/churn-sampling.md) |
| 2 | **hotspots** (hotspots + blast-radius, then READ) | most-central symbols + blast-radius of the churn set, and real understanding of the load-bearing, volatile code | `wicked-garden-search` (reused — do NOT reimplement) + § Read the intersection |
| 3 | **capture** | memories AND policies as inert `proposal.submit` entries | [refs/capture-proposals.md](refs/capture-proposals.md) |
Run them in order — each phase narrows the next. A partial run still helps (churn alone
orients; without capture it is a briefing), but the contract is **captured proposals**, not prose.
**In a wicked-crew run each row is ONE unit** (`Phase n/3` in your directive — the
`capture-learnings` workflow's `churn` / `hotspots` / `capture` units): do only that row's
work, hand the next unit what it needs in your output, and submit proposals ONLY in the
capture unit.
## Phase 1 — churn (the bounded, sampled step)
Rank the paths that changed most over a window. The method is fully specified in
[refs/churn-sampling.md](refs/churn-sampling.md); the non-negotiables:
- **Size the window first** (`git rev-list --count`) — full-scan vs sample by commit count.
- **Explicit top-N cap** (default **40** paths) — always `head -N`; the raw `--name-only`
stream stays inside the shell pipe (sort / uniq), never in your context.
- **Sampling rule for large histories** — above the commit cap (default **2000**) switch to
a recency-capped or even-stride sample; never `git log` the full history unbounded (worker
output is capped ~25K chars — a full-history dump starves the phases that matter).
- **Roll up to directories** for wide monorepos; **filter generated/vendored noise**
(lockfiles, `dist/`, `target/`, `node_modules/`).
Output of phase 1: a ranked list of ≤N `count path` (or `count dir/`) rows.
## Phase 2 — hotspots + blast-radius (reuse wicked-garden-search)
Do NOT reimplement centrality. Use the **wicked-garden-search** skill:
- `hotspots` — most-central symbols by PageRank (`RankHotspots`). Read its
the `wicked-garden-search` skill's `refs/hotspots.md`; pass `--limit` near your top-N.
- `blast-radius <path>` — for each top churn path, the dependents estate sees
(including injected bus/dispatch/capability/archetype edges grep can't). This
is what tells you a churny file is load-bearing vs peripheral.
Freshness is a precondition. In a human session run the search skill's `index`
action first (`wicked-estate index <path>`) and heed any `STALENESS` marker.
**In a governed run never run `wicked-estate index`** or any other write subcommand —
the run hands you an indexed store; read its `STALENESS` marker and report it
(§ In a governed run).
## Cross-reference — the 2×2 that picks read targets
Churn and centrality answer different questions; the intersection ranks targets.
| | **High churn** | **Low churn** |
|-----------------------|----------------|---------------|
| **High centrality** | load-bearing + volatile → **read deeply**; likely yields BOTH knowledge (how it works / why it moves, read from the code) AND a policy (what must hold as it changes) | stable core → **read for invariants**; yields policies (severity `error`/`critical`) more than knowledge |
| **Low centrality** | churny periphery (config, fixtures, generated) → **mostly noise**; skip unless it's a hot integration seam | quiet leaf → **skip** |
The prime targets are the top-left cell: paths that appear in BOTH the phase-1
churn list and the phase-2 hotspot/blast-radius set. Read those first.
## Phase 2, continued — read the intersection
For each cross-referenced target, build **genuine** understanding — not a file
listing — via the estate tools (all read-only; in a governed run through the shim's
`call` action, § In a governed run):
1. `SearchEntity` `{"name": "<symbol-or-path>"}` → resolve to node ids/kinds.
2. `RetrieveEntity` → the symbol's structured record; `FetchContent` → the actual
source slice (read the code, don't infer from names).
3. `TraverseGraph` (bounded — it carries `max_depth`/`max_nodes`) or `Lineage` →
how the target connects: what it calls, what calls it, which events/commands
inject edges into it. For "how does A reach B" ask `Path` (`{"from": "<A>",
"to": "<B>"}`) — the ordered hops, each with edge kind + confidence — instead of
hand-assembling a route from `TraverseGraph` edge lists; `found: false` with
`depth_bounded` or `node_bounded` set is a bounded search, not proof there is no
route (widen `depth` / `max_nodes` or say "no route within the bound").
4. Recall what's already known before writing anything new: `wicked-garden-mem`
`recall` (`scope_prefix: ""`, the explicit everything opt-in) so you extend the record rather than duplicate it.
Stop when you can state, per target, **what it does, why it changes, what it
depends on, and what would break** — that is the input to phase 3.
## Phase 3 — capture: memories AND policies as proposals
Turn understanding into reviewable records (knowledge, memory, policy) via the estate
**`proposal.submit`** tool (inert queue; a PERMITTED safe write even under
`--readonly`) — in a governed run through the shim's `propose` action (§ In a governed
run), the ONLY submission path: there is no deliverable file and no other fallback. Full contract, payloads, facets,
and derivation heuristics: [refs/capture-proposals.md](refs/capture-proposals.md). The essentials — use the
enum values EXACTLY as written (an out-of-enum `tier` or `severity` is rejected
when the proposal is approved, so the learning is lost):
- **Knowledge** — how the code works, read from the code (*acquired*, so it is
knowledge, not memory: DES-memory-vs-knowledge). `kind_type: "knowledge"`; payload
`{"content": "<1–3 sentences>", "class": "concept", "source": "<repo>:<path>",
"source_rev": "<commit sha>"}`. Most repo-learn findings are this kind. Submit it
like any proposal (estate 0.29.2+ approves it into the knowledge store); count it in
the `wicked-capture-report` like the rest. Never propose a graph metric (PageRank rank,
churn count, blast-radius size): estate's tools re-derive those on demand.
- **Memory** — only something *witnessed* while studying (a gotcha you hit, with
why) or *told* by the operator. Split a mixed finding: "we chose fail-closed
because of incident X" is memory, "the implementation fails closed" is
knowledge. `kind_type: "memory"`; payload
`{"content": "<1–3 sentences>", "tier": "<value>"}` where `tier` is EXACTLY one
of `working` / `episodic` / `semantic` / `procedural` / `archival` — for a repo
learning use `semantic` (a decision or gotcha) or `procedural` (a how-to you were
taught); NEVER invent a tier like `durable`. A convention or rule the team must
follow ("our convention is always X") is a **policy**, never a memory. Facets `{"repo": "<name>", "project": "<name>"}`.
- **Policy** — what *should hold*. `kind_type: "policy:<type>"` where `<type>` ∈
architecture · development · security · testing · operations · compliance ·
design-ux; payload `{"rule": "<imperative>", "severity": "<value>", "reach": "project"}`
(a rule learned from this repo applies to its project) where
`severity` is EXACTLY one of `info` / `warn` / `error` / `critical` — the middle
band is `warn`, NOT `warning`. Facets `{"repo": "<name>", "project": "<name>", "language": "<lang>"}`.
- **Never pass `provenance`** — the server stamps it from `WICKED_RUN_*`. Passing
it is at best ignored; treat provenance as not yours to set.
- Derive **both** per target where the 2×2 warrants it: the knowledge record
holds the *is*, the policy holds the *ought*. A stable high-centrality
invariant is a policy; a "this is how X actually wires up" discovery was read
from the code, so it is knowledge, never a memory.
## In a governed run
You are in a governed run when you were dispatched as a unit of a wicked-crew run — a phase
directive and/or the `wicked-garden-governed-worker` skill was handed to you. Both carriers
stamp `WICKED_RUN_ID` / `WICKED_RUN_UNIT` / `WICKED_RUN_AGENT` on your environment
(wicked-core ≥ 0.7.26) — the primary cue; the handed context confirms it. **When unsure,
treat the session as governed** — the shim in `--readonly` is also correct in a human session.
Follow `wicked-garden-governed-worker` (A2, A5) and ground through the **estate shim in
read-only mode** — the ONE grounding transport in a run: wicked-core registers no estate MCP
on any seat any more (an organization MCP allowlist used to drop one silently), so there is
no `mcp__wicked-estate__*` tool to call and nothing to fall back to. The store is pinned from
the worker environment (`WICKED_ESTATE_DB` / `WICKED_HOME` / `WICKED_MEMORY_DB`) or
`--db <path>`; `WICKED_ESTATE_READONLY=1` rides every worker so the shim spawns read-only by
default; the shim refuses an unpinned store — report that, never guess one.
1. **Grounding — one rung** (phase 2): the shim, `--readonly` literal — every estate tool
named above is reachable as
`wicked-garden run scripts/_estate_client.py --readonly call '{"tool":"FetchContent","arguments":{…}}'`.
If it answers `{"ok": false, …}` or the fence denies the call, write
`estate: not available (<reason>)` in your output and continue **UNGROUNDED** — for
phase 1 git suffices; for phase 2 report the gap rather than a grep approximation
presented as the graph answer. Never `wicked-estate index`, `scip`, `tfstate`,
`import-telemetry`, `compact`, `watch`, `clusters --annotate`; never retry a denied
call, no variants, no wrappers (governed-worker A2/A5). The read-only CLI is still
allowed by the fence but is not a documented rung.
2. **Capture** (phase 3) submits each proposal through the shim —
`wicked-garden run scripts/_estate_client.py --readonly propose '{"kind_type":"knowledge","payload":{…},"facets":{…}}'`
(`proposal.submit`; provenance is server-stamped from the `WICKED_RUN_*` markers on your
environment, never passed). **There is no deliverable file and no file fallback**: write
nothing into the repository (governed-worker E1) and nothing elsewhere either. If the shim
answers `{"ok": false, …}` or the fence denies the call, emit the exact
`{kind_type, payload, facets}` objects as ONE fenced `json` block in your output, write
`estate: not available (<reason>)`, and continue — that block is the ONLY record
(governed-worker A4); a human can submit from it.
3. **Report** honest counts: derived N / submitted M / failed K (governed-worker A4), and
whether the shim answered. **End the capture unit's output with the machine-readable
report line** (wicked-core#535 — the engine's capture-report floor reads it; a capture
phase whose output carries no such line is DENIED into the human gate, because 0
proposals cannot otherwise be told apart from a skill that never ran):
```text
wicked-capture-report {"derived": 7, "submitted": 7, "failed": 0}
```
Emit it **always** — including a degrade and a legitimate 0 (`{"derived": 0,
"submitted": 0, "failed": 0}` is an honest report and PASSES). `submitted` below
`derived`, or any `failed`, is a loss the gate stops, so the numbers must be the ones
you actually observed: count a proposal as `submitted` only when the shim answered
`{"ok": true, …}` for it.
## Degrade + honesty
- **Estate unreachable** (the shim answers `ok: false`, or the fence denies the call):
phase 1 still runs (pure git); for phase 2 write `estate: not available (<reason>)`
in your output and report the gap — never a grep approximation presented as the graph
answer, never a retry of a denied call. In a human session the search skill's
human ladder (estate tools → CLI → grep) applies instead.
- **Every degrade still reports.** Whatever happened — estate unreachable, a denied call,
nothing worth capturing — the last line of the capture unit's output is the
`wicked-capture-report {"derived": N, "submitted": M, "failed": K}` line with the real
counts (a degrade is typically `derived N / submitted 0 / failed N`, which the engine
denies into the human gate ON PURPOSE: the learnings are in your fenced `json` block and
a human must act on them).
- **`proposal.submit` fails (a JSON-RPC error, e.g. -32602 / -32603)**: do NOT silently drop the learning.
Emit the derived memories and policies as one fenced `json` block of exact
`{kind_type, payload, facets}` objects in your report so a human can
submit them, and name the degrade. That block is the only record — a governed run
writes no file.
- Capture is a **proposal**, not an assertion — report it as "proposed N
memories, M policies (pending review)", never "recorded".
## Depth
- [refs/churn-sampling.md](refs/churn-sampling.md) — the bounded/sampled git-log method:
window sizing, the sample rule, the top-N cap, directory rollup, noise filters, counting fallback.
- [refs/capture-proposals.md](refs/capture-proposals.md) — the `proposal.submit`
contract for memories and policies, the derivation heuristics (2×2 → what to
propose), worked examples, the idempotency/dedup discipline, and the shim `propose`
command — the only submission path in a governed run.