ckb/.agents/modules/software/skills/ckb-code-map/SKILL.md
Michał Kopeć 474630e2bb Adopt seven ideas from trailhq/Graft; schema 1.4 -> 1.5
Graft keeps a derived, disposable code graph in sync with a content hash
rather than a calendar, and keeps a protected block on every regenerated
node. This KB is the opposite kind of store — durable, curated, built from
material that cannot be regenerated — but several of Graft's mechanisms
port cleanly, and two of them close real gaps here.

Schema 1.5 is additive: every 1.4 page remains valid.

1. `## Crux` — verbatim source excerpts alongside the synthesis. A summary
   can drift silently; a quote either still matches its source or it does
   not. Lets `ckb-retrieve` ground an answer without a round-trip to the
   archive, and makes drift mechanically detectable.
2. `## Notes` — human-authored and protected everywhere. Closes a real
   gap: `ckb-index-external` regenerates connector pages wholesale, so an
   annotation written there was previously destroyed on the next refresh.
3. `source_fingerprint`/`source_checked` — a digest of the material a page
   was built from. Freshness by date says a page has aged; a fingerprint
   says whether its evidence moved. Most valuable for connector-backed
   libs, where documents change with no notice.
4. `lint_report.py --quick` — a deterministic one-line session-start
   signal, wired into Rule E next to the existing `git status` check.
5. In-degree as a rank-fusion signal in `ckb-retrieve`, weighted below 1.0:
   centrality is a prior, not evidence.
6. Blast radius — a new `ckb-ingest` step walking the graph backwards from
   touched entities to find what the incoming material contradicts, before
   writing anything. Ingest was additive-first, which is how a wiki
   accumulates two pages that quietly disagree.
7. Edge vocabulary in `wiki/graph/index.md` rewritten as a question per
   verb, and completed: `part_of` was written by `ckb-code-map` but never
   declared. Added `produces`, `configures`, `validates`, `implements`.

Lint gains checks 12 (fingerprint drift), 13 (crux verbatimness) and 14
(the protected-Notes rule), verified against a synthetic fixture covering
stale digests, missing sources, fabricated quotes and paraphrased evidence.

Not adopted: the gitignored regenerable store, the MCP server and CLI
daemon, tree-sitter parsing, statusline hooks, telemetry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 22:44:26 +02:00

188 lines
7.7 KiB
Markdown

---
name: ckb-code-map
description: Read a source repository under src/ and write what it IS into wiki/entities/ — a `type: repository` page (remote, default branch, language, build and test commands, owner) plus `type: component` pages for its significant modules/services, cross-linked into the graph and stamped with the commit they were generated from so staleness is detectable. Use when the user says "map the code", "map src/<repo>", "refresh the code map", "index the repo", "add this repo to the wiki", or after cloning a new repo into src/. Distinct from `ckb-ingest` (which processes raw/inbox/ material and never reads code) and from reading code to answer a question (that is `ckb-retrieve` using src/ as evidence). Part of the opt-in `software` module.
---
# Code map skill
## Purpose
`src/` clones are gitignored: they are not in this KB's history and may be
absent entirely from a fresh checkout. So the *knowledge* about a repository
has to live in `wiki/`, or it doesn't survive.
This skill produces that knowledge. It is deliberately **not** a code-to-prose
dump — a wiki page restating what any reader could get by opening the file is
pure liability, because it goes stale silently. What this skill records is what
you cannot get by reading one file: how to obtain the repo, how to build and
test it, what its pieces are, who owns it, and how those pieces connect to
entities the wiki already knows about.
## Trigger phrases
- "map the code" / "map `src/<repo>`" / "index the repo"
- "refresh the code map" / "the code map is stale"
- "add this repo to the wiki" / "I cloned a new repo into src"
## Scope rule
Reads `src/`. Writes only `wiki/`. Never writes, commits, or pushes anything
inside `src/` — those are independent repos with their own remotes.
## How to run this skill
### Step 0 — Confirm the module is installed and the repo exists
Check `ckb.yaml` lists the `software` module. If `src/` is empty or the named
repo isn't there, say so and stop — don't guess which repo was meant when
several are present, ask.
### Step 1 — Gather the facts that don't come from reading code
From inside the repo, cheaply:
```bash
git -C src/<repo> remote -v
git -C src/<repo> rev-parse --abbrev-ref HEAD
git -C src/<repo> rev-parse --short HEAD
git -C src/<repo> log -1 --format=%cI
git -C src/<repo> status --short
```
Then read the manifest and entry docs — `README`, `package.json`, `pyproject.toml`,
`go.mod`, `Cargo.toml`, `Makefile`, CI config. Build and test commands come from
here, not from inference.
**Do not run the build, the tests, or any script from the repo** to find out what
it does. Mapping is a read-only activity.
### Step 2 — Identify components, and be ruthless about what counts
A component is a part of the system a person would name in conversation: a
service, a CLI, a published package, a long-lived subsystem. A directory is not
automatically a component. **Ten honest component pages beat a hundred mirroring
the folder tree** — the second kind makes the wiki look thorough while making it
useless to search.
If you cannot write a one-sentence `tldr` for a candidate that says what it *does*
(not where it lives), it isn't a component. Leave it out.
### Step 3 — Consult the cascade before creating anything
Per the cascade rule, check whether pages already exist for this repo or its
components — in `wiki/` first, then `linked/`, then `libs/`. Refreshing an
existing page is the normal case, not the exception: update it, keep its history,
and don't renumber or re-slug it just because a directory was renamed.
### Step 4 — Write the repository page
`wiki/entities/<repo-slug>.md`:
```markdown
---
type: repository
tldr: One sentence on what this software does — not "the repo for X".
resource: https://git.example.com/me/thing
repo: thing
commit: a1b2c3d
confidence: 0.9
quality: 0.8
last_updated: YYYY-MM-DD
freshness_window_days: 90
retention: high
---
# thing
**Remote:** `git@git.example.com:me/thing.git` · **Default branch:** `main`
· **Local path:** `src/thing` · **Mapped at:** `a1b2c3d` (YYYY-MM-DD)
## What it is
Two or three sentences. What problem it solves and for whom.
## Getting it
git clone git@git.example.com:me/thing.git src/thing
## Build and test
<the actual commands, from the manifest not invented>
## Components
* [[thing-api]] / [thing-api](/wiki/entities/thing-api.md) — <tldr>
## Specs
KB-root specs this repo implements, and the repo-level specs that implement them.
Written by `ckb-spec`; leave the section here even when empty.
## Sources
`src/thing` at `a1b2c3d`, mapped YYYY-MM-DD. README, `pyproject.toml`, CI config.
## Notes
<!-- Yours. Never rewritten by any skill. -->
```
The `commit` field and the **Mapped at** line are what make this page auditable —
they let a reader and `ckb-lint` tell how far the page has drifted from the code.
Never write them from memory; take them from Step 1. They are this page's
fingerprint, serving the same role `source_fingerprint` serves elsewhere: a
commit either still matches `HEAD` or it doesn't, which beats guessing from a
date.
`## Notes` is **protected** (page schema, `CLAUDE.md`/`AGENTS.md`). A refresh
regenerates everything above it and carries it across byte-for-byte. This
matters more here than almost anywhere else: a code map is re-run often, and
the things worth knowing about a repo that the repo doesn't say about itself —
which build target is abandoned, which service is being decommissioned, who to
ask — have nowhere else to live.
### Step 5 — Write component pages
Same shape, `type: component`, with `repo: <repo-slug>` pointing home. Keep them
thin and link upward to the repository page and sideways to whatever the wiki
already knows — a component that talks to a system with an existing entity page
should link to it rather than re-describing it.
### Step 6 — Graph, indexes, log
- `wiki/graph/edges.json``part_of` (component → repository), `depends_on`
(repo/component → an external library or a system that has a page), `owns`
(person → repo, **only** on stated ownership, never inferred from commit counts).
- `wiki/entities/index.md` — add or refresh a row per page.
- `wiki/log.md` — one entry per run, per Rule B, naming the repo and commit.
### Step 7 — Report
State the repo and commit mapped, pages created vs. refreshed, components
deliberately skipped and why, anything you couldn't determine (no build command
in the manifest — say so rather than inventing one), and whether the working tree
was dirty at map time (a map taken from uncommitted work is fine, but should be
labelled as such). Close with the standard reminder: on disk, not committed.
## Edge cases
- **Dirty working tree** — map it, but record the commit *plus* a note that
uncommitted changes were present. Don't refuse, and don't silently pretend the
tree was clean.
- **Repo with no remote** (local-only) — record `resource:` as absent and say
plainly in **Getting it** that this repo exists only locally. That's a real
finding: a gitignored, remote-less repo is one disk failure from gone.
- **Monorepo** — one repository page, components per package. Don't create a
repository page per package.
- **Several repos, user said "map the code"** — ask which, or offer to do all;
don't pick one.
- **Repo that's mostly vendored/generated code** — map what's authored here. Note
the vendored portion once, on the repository page.
- **A component page already exists as `type: concept`** from an earlier ingest —
don't create a duplicate. Update the existing page and change its type, noting
the change in the log entry.
---
*Licensed under the Apache License, Version 2.0 — see [LICENSE](../../../../LICENSE)
at the repository root, or <http://www.apache.org/licenses/LICENSE-2.0>.*