/scribe - Accumulate PR / issue / research recurring patterns into the wiki
The patterns worth picking up are procedures that recur as a routine or a convention, and review comments or failures that recur. A one-off circumstance and a design decision itself are not picked up.
Invariants
| Condition |
Content |
| Via PR |
Never commit or push directly to the default branch |
| Progress record |
The cursor is the mergedAt of the last merged scribe PR. For a research file, compare that mergedAt against its last commit time |
| Where the threshold lives |
scripts/triage.py decides whether a pattern becomes a page or a candidate; this skill does not judge it |
| Facts only |
Write only facts stated in PRs / issues and research files, plus facts verified in the current code. No guessing |
| No research paths |
Never write .claude/workspace/research/ file paths under docs/wiki/. The wiki carries the distilled pattern, and a path would send the reader back to the raw report |
| Worktree isolation |
Edit and commit inside an isolated worktree; never touch the user's working tree. The worktree is created in Phase 6, so Phase 6 is the only Phase that writes |
Phase 1: Preconditions and onboarding
- Check for an unmerged scribe PR with
gh pr list --label scribe --state open --limit 1. If one exists, do not overtake it; stop and report
- Prepare the content of ${CLAUDE_SKILL_DIR}/templates/readme.md when
docs/wiki/README.md does not exist
- Prepare the content of ${CLAUDE_SKILL_DIR}/templates/candidates.md when
docs/wiki/_candidates.md does not exist. For this and step 2 alike, the write happens inside Phase 6's worktree
- If the scribe label does not exist, create it with
gh label create scribe --description "scribe による wiki 提案"
Phase 2: Scope
- Get the mergedAt of the last merged scribe PR with
gh pr list --label scribe --state merged --limit 1 --json mergedAt -q '.[0].mergedAt'
- If no mergedAt comes back, this is the first run. Take all of
gh pr list --state merged --search '-label:scribe', gh issue list --state closed, and find .claude/workspace/research -name '*.md' as the scope
- If a mergedAt comes back, take the PRs from
gh pr list --state merged --search "-label:scribe merged:><mergedAt>", the issues from gh issue list --state closed --search "closed:><mergedAt>", and the files from git log --since="<mergedAt>" --name-only --diff-filter=AM --pretty=format: -- '.claude/workspace/research/*.md' | sort -u plus the untracked ones from git ls-files --others --exclude-standard -- '.claude/workspace/research/*.md' as the scope. A report not yet committed has no git log entry, and it is exactly the one a local run is most likely to hold
- Only
*.md counts as a research target; read no other format. Use each file's last commit time as the cursor, not its filesystem mtime or the Generated: line inside it. A checkout resets mtime to the checkout moment regardless of when the content last changed in git, so mtime cannot anchor the comparison. Generated: carries the date the file was produced and stays there through later edits, so it drops updates too
- Even with PRs, issues, and research all empty, go on to Phase 3 when
docs/wiki/_candidates.md holds a line with two or more pieces of evidence. Report "nothing new" and stop only when that line is absent too
Phase 3: Extraction
- Read
docs/wiki/*.md to grasp the existing pages. A page carrying kind: structure is not a pattern, so a coinciding name stays existing: "none"
- Read each in-scope PR/issue including comments via
gh pr view <number> --comments/gh issue view <number> --comments
- Read each in-scope research file in full with Read. Do not narrow by section name
- Add what you read to the array, grouped per pattern. Add only the evidence when the pattern is in the array already. Design decisions and their history belong to
docs/decisions/ and are out of scope
- Read
docs/wiki/_candidates.md. When a pattern in the array points at the same thing as an existing candidate line, use that line's body verbatim as the name
- Pass that array to
python3 ${CLAUDE_SKILL_DIR}/scripts/triage.py '<JSON array of patterns>' docs/wiki/_candidates.md. The script reads the candidate lines from both sections of _candidates.md into the array, then applies the two-evidence threshold and the per-run page cap, splitting the result into pages (create/promote/update), candidates, and deferred (left for a later run). Do not judge the threshold or the cap yourself
- Prepare how
docs/wiki/_candidates.md changes. candidates go under the 単発 section and deferred under the 昇格待ち section, and the line of a pattern that became a page is removed. A line takes the form - <one-line content> <evidence>, with #number and (research) listed space-separated. When the line is already there, add only the evidence. The write happens inside Phase 6's worktree
| Field |
Value |
name |
The key deciding whether two patterns are the same. One from a candidate line carries that line's body verbatim |
evidence |
The array of evidence. #number from a PR/issue, (research) from a research file |
existing |
page when it sits on an existing page, candidate when in _candidates.md, else none |
Phase 4: Cross-check against the latest code
Before creating, promoting, or updating a page, cross-check each pattern against the current code. What this Phase settles is the content; writing to files happens inside Phase 6's worktree, all at once.
- For each item that holds, add the current-code location as reference code, written as
path + symbol name. Write no line numbers
- Settle the globs of the implementation files the rule bears on, and settle its scenes from
find_wiki_rule.py's SCENES constant. The two are independent judgments: globs name implementation files, scenes name situations, and a rule confined to filing or PR practice carries an empty globs array with whatever scenes hold
- Sweep the reference code of every page under
docs/wiki/*.md, existing pages unrelated to this run's scope included. Mechanically verify that the file exists and that the symbol name greps within it
- For each structure page, cross-check the rows its boundary, contract, and requirement sections list against the current code. Run every target page each time, independent of reference-code state. When they drift, settle the wording that matches the current code. Do not drop it
- For a broken reference, reread the current code. The table below settles what to write
- When a dropped item holds a line in
_candidates.md, move that line into the 棄却 section and write the reason it was dropped on the next line, indented. This takes priority over the removal Phase 3 step 7 prepared
| Check |
What to settle when it fails |
| Does the convention/procedure still hold in the current code? |
Drop it. On an existing page, the wording that marks it as no longer holding |
| Does a structure page match the current implementation? |
The wording that matches the current code. Do not drop it |
| Is it already mechanically enforced by lint / hook / CI? |
Drop it |
| Do the referenced paths/commands still exist? |
The relink target among the current paths/commands |
Phase 5: 由来 link judgment
For a page being created, promoted, or updated, write the DR file path in its 由来 section only when the pattern derives from a specific DR decision under docs/decisions/. The gate is the counterfactual test "if that DR were superseded, would this page need rewriting?", and the link is added only on Yes. With three or more links on one page, reapply the counterfactual test to each link and remove those that come back No.
In addition, inspect the 由来 links of every page, including existing pages. Verify the DR file exists and check its status; if superseded, read the successor DR. Settle the relink target as the successor when the pattern still holds, and settle the wording that marks it as no longer holding when it does not. Here too, the write happens inside Phase 6's worktree。
Phase 6: PR creation
Move only the pages in Phase 3's pages, and state deferred in the PR body as what was left. Reference repairs, 由来 repairs, and the structure-page rewrites Phase 4 step 4 settled sit outside the cap, so run them even when pages is empty. Create a PR even for candidate-only additions, and skip the PR only when there is no change at all.
- After
git fetch origin <default branch>, create an isolated worktree and branch scribe/<yyyymmdd-HHMMSS> from origin/<default branch>
- Write what Phase 3-5 settled, inside the worktree. The skeleton follows which write it is: an item in
pages takes the 共通項 skeleton in ${CLAUDE_SKILL_DIR}/templates/page.md, and a structure page Phase 4 step 4 settled a rewrite for takes the structure skeleton in the same file, replacing only the rows that moved. Candidate lines go to _candidates.md in Phase 3 step 7's form, and the reference and 由来 repairs use the relink targets Phase 4-5 settled
- Commit the elements of
commits in order, one at a time. The first commit also git adds the _candidates.md update and the reference/由来 repairs alongside its own pages, and the rest of the elements git add only their own pages. Commit each element separately with the message docs(wiki): <that element's pattern names, ...> を追加/更新
- Run
python3 ${CLAUDE_SKILL_DIR}/scripts/verify_run.py <worktree> <base>, feeding Phase 3's report JSON on stdin. <base> is the origin/<default branch> step 1 branched from. The script reads the 昇格待ち count at <base> and the expected commit count off that report itself, so count neither yourself. Confirm ok comes back true. On false, do not go on to step 5
- Push and run
gh pr create --base <default branch>. Title [scribe] <pattern names, ...> を追加/更新, label scribe. In the body, list the added/promoted/updated pages grouped per commit, then the candidate additions, the reference-repaired/由来-repaired pages, the range of PRs/issues read and the count of research files read, the items dropped by verification, and any leftovers
- Remove the worktree
- When step 4 returns false, leave the worktree in place so the write can be redone. When step 5 or later fails, run
git worktree remove --force <worktree> and git branch -D scribe/<yyyymmdd-HHMMSS> so neither the worktree nor the local branch is left behind
1---2name: scribe3description: Extract recurring patterns from past closed PRs/issues and the research findings in .claude/workspace/research/, verify them against the latest code, and propose them to docs/wiki/ via PR.4---56# /scribe - Accumulate PR / issue / research recurring patterns into the wiki78The patterns worth picking up are procedures that recur as a routine or a convention, and review comments or failures that recur. A one-off circumstance and a design decision itself are not picked up.910## Invariants1112| Condition | Content |13| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |14| Via PR | Never commit or push directly to the default branch |15| Progress record | The cursor is the mergedAt of the last merged scribe PR. For a research file, compare that mergedAt against its last commit time |16| Where the threshold lives | `scripts/triage.py` decides whether a pattern becomes a page or a candidate; this skill does not judge it |17| Facts only | Write only facts stated in PRs / issues and research files, plus facts verified in the current code. No guessing |18| No research paths | Never write `.claude/workspace/research/` file paths under `docs/wiki/`. The wiki carries the distilled pattern, and a path would send the reader back to the raw report |19| Worktree isolation | Edit and commit inside an isolated worktree; never touch the user's working tree. The worktree is created in Phase 6, so Phase 6 is the only Phase that writes |2021## Phase 1: Preconditions and onboarding22231. Check for an unmerged scribe PR with `gh pr list --label scribe --state open --limit 1`. If one exists, do not overtake it; stop and report242. Prepare the content of ${CLAUDE_SKILL_DIR}/templates/readme.md when `docs/wiki/README.md` does not exist253. Prepare the content of ${CLAUDE_SKILL_DIR}/templates/candidates.md when `docs/wiki/_candidates.md` does not exist. For this and step 2 alike, the write happens inside Phase 6's worktree264. If the scribe label does not exist, create it with `gh label create scribe --description "scribe による wiki 提案"`2728## Phase 2: Scope29301. Get the mergedAt of the last merged scribe PR with `gh pr list --label scribe --state merged --limit 1 --json mergedAt -q '.[0].mergedAt'`312. If no mergedAt comes back, this is the first run. Take all of `gh pr list --state merged --search '-label:scribe'`, `gh issue list --state closed`, and `find .claude/workspace/research -name '*.md'` as the scope323. If a mergedAt comes back, take the PRs from `gh pr list --state merged --search "-label:scribe merged:><mergedAt>"`, the issues from `gh issue list --state closed --search "closed:><mergedAt>"`, and the files from `git log --since="<mergedAt>" --name-only --diff-filter=AM --pretty=format: -- '.claude/workspace/research/*.md' | sort -u` plus the untracked ones from `git ls-files --others --exclude-standard -- '.claude/workspace/research/*.md'` as the scope. A report not yet committed has no git log entry, and it is exactly the one a local run is most likely to hold334. Only `*.md` counts as a research target; read no other format. Use each file's last commit time as the cursor, not its filesystem mtime or the `Generated:` line inside it. A checkout resets mtime to the checkout moment regardless of when the content last changed in git, so mtime cannot anchor the comparison. `Generated:` carries the date the file was produced and stays there through later edits, so it drops updates too345. Even with PRs, issues, and research all empty, go on to Phase 3 when `docs/wiki/_candidates.md` holds a line with two or more pieces of evidence. Report "nothing new" and stop only when that line is absent too3536## Phase 3: Extraction37381. Read `docs/wiki/*.md` to grasp the existing pages. A page carrying `kind: structure` is not a pattern, so a coinciding name stays `existing: "none"`392. Read each in-scope PR/issue including comments via `gh pr view <number> --comments`/`gh issue view <number> --comments`403. Read each in-scope research file in full with Read. Do not narrow by section name414. Add what you read to the array, grouped per pattern. Add only the evidence when the pattern is in the array already. Design decisions and their history belong to `docs/decisions/` and are out of scope425. Read `docs/wiki/_candidates.md`. When a pattern in the array points at the same thing as an existing candidate line, use that line's body verbatim as the `name`436. Pass that array to `python3 ${CLAUDE_SKILL_DIR}/scripts/triage.py '<JSON array of patterns>' docs/wiki/_candidates.md`. The script reads the candidate lines from both sections of `_candidates.md` into the array, then applies the two-evidence threshold and the per-run page cap, splitting the result into `pages` (create/promote/update), `candidates`, and `deferred` (left for a later run). Do not judge the threshold or the cap yourself447. Prepare how `docs/wiki/_candidates.md` changes. `candidates` go under the 単発 section and `deferred` under the 昇格待ち section, and the line of a pattern that became a page is removed. A line takes the form `- <one-line content> <evidence>`, with `#number` and `(research)` listed space-separated. When the line is already there, add only the evidence. The write happens inside Phase 6's worktree4546| Field | Value |47| ---------- | --------------------------------------------------------------------------------------------------------------- |48| `name` | The key deciding whether two patterns are the same. One from a candidate line carries that line's body verbatim |49| `evidence` | The array of evidence. `#number` from a PR/issue, `(research)` from a research file |50| `existing` | `page` when it sits on an existing page, `candidate` when in `_candidates.md`, else `none` |5152## Phase 4: Cross-check against the latest code5354Before creating, promoting, or updating a page, cross-check each pattern against the current code. What this Phase settles is the content; writing to files happens inside Phase 6's worktree, all at once.55561. For each item that holds, add the current-code location as reference code, written as `path` + symbol name. Write no line numbers572. Settle the globs of the implementation files the rule bears on, and settle its scenes from `find_wiki_rule.py`'s `SCENES` constant. The two are independent judgments: globs name implementation files, scenes name situations, and a rule confined to filing or PR practice carries an empty globs array with whatever scenes hold583. Sweep the reference code of every page under `docs/wiki/*.md`, existing pages unrelated to this run's scope included. Mechanically verify that the file exists and that the symbol name greps within it594. For each structure page, cross-check the rows its boundary, contract, and requirement sections list against the current code. Run every target page each time, independent of reference-code state. When they drift, settle the wording that matches the current code. Do not drop it605. For a broken reference, reread the current code. The table below settles what to write616. When a dropped item holds a line in `_candidates.md`, move that line into the 棄却 section and write the reason it was dropped on the next line, indented. This takes priority over the removal Phase 3 step 7 prepared6263| Check | What to settle when it fails |64| ------------------------------------------------------------- | ---------------------------------------------------------------------------- |65| Does the convention/procedure still hold in the current code? | Drop it. On an existing page, the wording that marks it as no longer holding |66| Does a structure page match the current implementation? | The wording that matches the current code. Do not drop it |67| Is it already mechanically enforced by lint / hook / CI? | Drop it |68| Do the referenced paths/commands still exist? | The relink target among the current paths/commands |6970## Phase 5: 由来 link judgment7172For a page being created, promoted, or updated, write the DR file path in its 由来 section only when the pattern derives from a specific DR decision under `docs/decisions/`. The gate is the counterfactual test "if that DR were superseded, would this page need rewriting?", and the link is added only on Yes. With three or more links on one page, reapply the counterfactual test to each link and remove those that come back No.7374In addition, inspect the 由来 links of every page, including existing pages. Verify the DR file exists and check its status; if superseded, read the successor DR. Settle the relink target as the successor when the pattern still holds, and settle the wording that marks it as no longer holding when it does not. Here too, the write happens inside Phase 6's worktree。7576## Phase 6: PR creation7778Move only the pages in Phase 3's `pages`, and state `deferred` in the PR body as what was left. Reference repairs, 由来 repairs, and the structure-page rewrites Phase 4 step 4 settled sit outside the cap, so run them even when `pages` is empty. Create a PR even for candidate-only additions, and skip the PR only when there is no change at all.79801. After `git fetch origin <default branch>`, create an isolated worktree and branch `scribe/<yyyymmdd-HHMMSS>` from `origin/<default branch>`812. Write what Phase 3-5 settled, inside the worktree. The skeleton follows which write it is: an item in `pages` takes the 共通項 skeleton in ${CLAUDE_SKILL_DIR}/templates/page.md, and a structure page Phase 4 step 4 settled a rewrite for takes the structure skeleton in the same file, replacing only the rows that moved. Candidate lines go to `_candidates.md` in Phase 3 step 7's form, and the reference and 由来 repairs use the relink targets Phase 4-5 settled823. Commit the elements of `commits` in order, one at a time. The first commit also `git add`s the `_candidates.md` update and the reference/由来 repairs alongside its own pages, and the rest of the elements `git add` only their own pages. Commit each element separately with the message `docs(wiki): <that element's pattern names, ...> を追加/更新`834. Run `python3 ${CLAUDE_SKILL_DIR}/scripts/verify_run.py <worktree> <base>`, feeding Phase 3's report JSON on stdin. `<base>` is the `origin/<default branch>` step 1 branched from. The script reads the 昇格待ち count at `<base>` and the expected commit count off that report itself, so count neither yourself. Confirm `ok` comes back true. On false, do not go on to step 5845. Push and run `gh pr create --base <default branch>`. Title `[scribe] <pattern names, ...> を追加/更新`, label scribe. In the body, list the added/promoted/updated pages grouped per commit, then the candidate additions, the reference-repaired/由来-repaired pages, the range of PRs/issues read and the count of research files read, the items dropped by verification, and any leftovers856. Remove the worktree867. When step 4 returns false, leave the worktree in place so the write can be redone. When step 5 or later fails, run `git worktree remove --force <worktree>` and `git branch -D scribe/<yyyymmdd-HHMMSS>` so neither the worktree nor the local branch is left behind