this skill
Stage 1. Turn a loose target — a repository, a set of them, a book — into a pinned, inventoried source list a later stage can cite against.
Read references/citation.md for how to pin, and
references/stage-gates.md for this stage's exit condition.
User arguments: $ARGUMENTS
The invariant
GATHER NEVER WRITES INTO THE CORPUS IT READS
Studying a project must not dirty its checkout. Output lands under --out,
defaulting to notes/ontology/<subject>/ in the current repository. The corpus
is recorded by URL and ref, not by local path.
Context
Current repository — run this command and read the output:
git remote get-url origin 2>/dev/null || echo "(not a git repository)"
Existing studies — run this command and read the output:
ls notes/ontology/ 2>/dev/null || echo "(none)"
Procedure
1. Resolve each target to a ref
For a git repository, prefer a release tag; fall back to a 7-character commit reachable from trunk. Never a branch name — it moves, and every citation built on it rots silently.
Newest tag of a repository you have not cloned:
$ gh release list --repo OWNER/REPO --limit 1 --exclude-drafts --exclude-pre-releases --json tagName --jq '.[0].tagName'
A repository can have thousands of tags and no releases, in which case that prints nothing. Fall back to the tags:
$ git ls-remote --tags --refs --sort=-v:refname https://github.com/OWNER/REPO
For a local checkout, record both the remote and the exact ref:
$ git rev-parse --short HEAD
For prose, the ref is the edition — publisher, year, and printing where the locator scheme depends on it. A page number without an edition is not a locator.
2. Inventory without reading everything
Establish the shape of the corpus cheaply. For code, the module or package list and the size of each. For prose, the table of contents. For a fleet, the repository list and what each one is for.
The inventory is what makes the coverage decision in step 3 an informed one rather than a guess.
3. Decide scope with the user
Present the inventory via ask-user-choice and settle what will be read. Most
studies should not read everything; the point is to decide deliberately rather
than to run out of attention silently.
4. Write sources.jsonl
One row per source, including every source deliberately not read:
{"source": "https://github.com/OWNER/REPO", "ref": "v2.40.0", "scope": "src/parser/", "read": true, "why": ""}
{"source": "https://github.com/OWNER/REPO", "ref": "v2.40.0", "scope": "test/", "read": false, "why": "tests name fixtures, not domain concepts"}
A study that does not state its coverage boundary is not honest about what it
does not know. contest's completeness pass reads exactly this file.
Rules
- Never write into the corpus. Never
git checkout,git clean, or edit inside a target. - Every source has a resolved ref before this stage hands off.
- Every skipped source has a reason in
why. "Out of scope" is not a reason; say what it contains and why that does not bear on the vocabulary. - Where a corpus has no stable anchors, record that the locator is a search
string rather than a position, so
annotateandextractknow.
Output
Open with a one-line hero (✓ <subject>: <n> sources pinned, <n> skipped or
⚠ Blocked: <reason>), then exactly these sections:
## Pinned— each source read, its ref, and how the ref was resolved.## Skipped— each source not read, and why.## Shape— the inventory that informed the scope decision.
End with an ask-user-choice panel offering next steps (for example: run
extract, widen scope, stop here) — skip the panel only in plan mode.
Portability notes
ask-user-choice— present the listed options and wait for the user to pick one. Hosts with a structured multiple-choice tool (Claude Code'sAskUserQuestion) should use it; otherwise print a numbered list and wait for a numbered reply. Never proceed on an assumed answer.$ARGUMENTS— the text the user passed when invoking this skill. If your host does not substitute it, read it as the user's request in the current turn, and ask when there is none.- Bundled files — every relative path in this skill points at a file shipped inside this skill directory. Read them from here, not from the host's plugin tree.