Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .claude/hooks/bca-guidance.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ simpler -- not the number smaller.
names; it is nexits, never exit -- an unknown metric voids the whole
marker). A clear function with an honest suppression beats a
"compliant" tangle.
- Keep the fix where the violation is. The flag is scoped to the
function you just edited. Fix it there, mention anything larger you
noticed, and do not widen the change into a module rewrite to bring
the number down.

This repo also gates at the task boundary: run `make self-scan` (and
`make pre-commit`) before declaring work done. The per-edit hook is an
Expand Down
22 changes: 18 additions & 4 deletions .claude/skills/batch-fix/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ respecting crate conflicts, dependencies, and quick-win priority.
early waves, giving rapid feedback and reducing blast radius.
6. User-specified order is preserved as a tiebreaker within each crate group
(after dependency and quick-win sorting).
7. **Wave size cap**: a wave holds at most six issues. In worktree mode each
agent clones the workspace, and this workspace has twelve crate manifests,
so an uncapped wave can start a dozen concurrent clones and exhaust the
disk. Issues past the sixth roll into the next wave. The cap is harmless in
branch mode, where agents already run one at a time.

### Algorithm

Expand All @@ -266,10 +271,14 @@ for crate in classified:
waves = []
scheduled = set() # issue numbers already assigned to a wave
remaining = copy of classified (dict of crate -> [issue list])
MAX_WAVE = 6 # see scheduling rule 7: bounds concurrent worktree clones

while remaining is not empty:
wave = []
crates_in_wave = set()
for crate in list(remaining.keys()) sorted by most issues first:
if len(wave) >= MAX_WAVE:
break
if crate not in crates_in_wave:
# Find the first issue whose dependencies are all scheduled
candidate = None
Expand Down Expand Up @@ -784,7 +793,9 @@ Follow the `/fix-issue` workflow:
public API, run `find_referencing_symbols` (or workspace-wide `rg`) to
enumerate every call site. If the implementation reveals issues the plan
missed, revise the plan the same way before proceeding.
7. Self-review the implementation:
7. Check the implementation against the project-specific traps this
checklist exists to catch — it is a domain checklist, not a generic
second look:
- Correctness: root cause addressed, not just symptom?
- Performance: appropriate algorithms and data structures? No O(n²) on
hot paths (whole-repo analysis, large source files, deep ASTs)?
Expand All @@ -796,7 +807,9 @@ Follow the `/fix-issue` workflow:
- Conventions: no `unwrap`/`expect`/`panic` outside tests, no `unsafe`,
newtypes for domain invariants, narrow visibility, borrowing over
cloning, no `to_string_lossy()` on identifier paths.
8. Fix any issues found in self-review. If fixes were non-trivial, re-review.
8. Fix anything the checklist turned up. Do not re-run the checklist over
your own fixes — Phase 3 reviews this diff from outside the fix
context, which is where a second look actually pays.
9. **Write tests.** Sufficient testing is mandatory before proceeding. At
minimum:
- **Unit tests**: for all new or changed public functions. Each edge
Expand Down Expand Up @@ -945,8 +958,9 @@ For each finding, classify severity and effort:
- **performance** or **code-smell** (medium+ effort) -> fix if safe
- **trivial code-smell** or **test-gap** -> fix if trivial, note otherwise

Fix all actionable findings. If fixes were non-trivial, re-review the new
diff. Do NOT proceed with known bugs or security issues.
Fix all actionable findings, then proceed to Phase 4 — re-running this
review over your own fixes costs a pass and finds nothing. Do NOT proceed
with known bugs or security issues.

### Phase 4: Validate

Expand Down
5 changes: 4 additions & 1 deletion .claude/skills/cleanup-crate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ approval-required). Re-run without --dry-run to apply."
#### Worktree mode

**CRITICAL**: Every agent MUST be launched with `isolation: "worktree"`.
Launch all removal area agents in parallel.
Launch removal area agents in parallel, at most six at a time — each one
clones the workspace, and a crate with a dozen removal areas will fill
the disk with scratch clones if they all start at once. Queue the rest
and launch each as a slot frees.

#### Branch mode

Expand Down
7 changes: 5 additions & 2 deletions .claude/skills/doc-author/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,11 @@ with a fresh-context critique, scaled to the size of the change:
- **Fallback** (no subagent available): run this skill again in
`review` mode, in a fresh turn, against the saved file.

Apply the findings through the Step 5 fix discipline, then re-run the
reviewer once to confirm it comes back clean.
Apply the findings through the Step 5 fix discipline. Do not re-run the
reviewer to confirm your fixes landed — a second pass over prose you
just corrected finds nothing. Re-review only when the findings forced a
structural rewrite, because the rewritten sections are new prose nobody
has read yet.

### Step 7: Verify lint

Expand Down
5 changes: 4 additions & 1 deletion .claude/skills/improve-crate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ to apply."
#### Worktree mode

**CRITICAL**: every agent MUST be launched with `isolation: "worktree"`.
Launch all change area agents in parallel.
Launch change area agents in parallel, at most six at a time — each one
clones the workspace, and a crate with a dozen change areas will fill
the disk with scratch clones if they all start at once. Queue the rest
and launch each as a slot frees.

#### Branch mode

Expand Down
6 changes: 5 additions & 1 deletion .claude/skills/simplify-rust/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ Determine what to review based on `$ARGUMENTS`:

1. Collect the diff for the chosen scope
2. Read each changed file to understand context around the diff hunks
3. Use the Agent tool to spawn three parallel **read-only** review agents (one per dimension below). Agents analyse and return findings — they do NOT edit files.
3. Review against the three dimensions below, scaling the work to the diff.
A diff you can hold in one context — roughly one crate, under ~10
changed files — is faster to review directly than to farm out. Beyond
that, spawn **read-only** review agents, one per dimension and no more
than three. Agents analyse and return findings — they do NOT edit files.
4. Aggregate findings, deduplicate, and classify by priority
5. Apply fixes directly to the code (orchestrator only — not the review agents)
6. Run `make pre-commit` to validate formatting, linting, and tests. Fall
Expand Down
4 changes: 3 additions & 1 deletion .opencode/plugins/bca-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ not the number smaller. Do not extract a meaningless helper or split a
cohesive function to dodge the count — a spurious helper often raises
file-level nom/nargs and helps nothing. If the complexity is essential
and the function is clearest left whole, add a suppression marker with
a one-line reason instead of contorting the code.
a one-line reason instead of contorting the code. Keep the fix in the
function that was flagged rather than widening it into a module
rewrite.
`.trim()

// Resolve the analyzer with the same precedence as the shell hook:
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ smaller.
[Suppression markers](big-code-analysis-book/src/commands/suppression.md)
and the full recipe at
[`recipes/agent-feedback.md`](big-code-analysis-book/src/recipes/agent-feedback.md).
- **Keep the fix where the violation is.** The flag is scoped to the
function you just edited. Fix it there, mention anything larger you
noticed, and do not widen the change into a module rewrite to bring
the number down.

The per-edit hook is an early-warning convenience; the task-boundary
gate (`make self-scan` / `make pre-commit`) is the real check before
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,20 @@ for historical reference.

### Fixed

- `make book-pot` writes `messages.pot` to the book's `po/` directory
again, and now refuses to run against an unsupported mdBook. The
target passed a relative `-d po`, which mdBook 0.4 resolved against
the book root but 0.5 resolves against the working directory, so
under 0.5 the pot silently landed in `./po/` at the repository root
and `make book-po-update` then failed on a missing file. The
destination is now absolute. A version guard was added alongside it:
mdbook-i18n-helpers 0.3.x pairs only with mdBook 0.4.x, and the
mismatched pair that does *not* error — helpers 0.4.x — extracts
fenced code blocks one entry per line instead of one per block, so
the following `msgmerge` marks every code-block entry fuzzy and
rewrites `po/ja.po` against msgids the pinned toolchain never
produces. `docs/development/translations.md` now pins both halves of
the toolchain and describes both failure modes.
- Comment-only rows are no longer counted as physical lines of code in
Tcl, iRules (#1135), and Perl (#1137). Both defects let a token reach
the `_` catch-all that ends `stats.ploc.lines.insert(start)`: in the
Expand Down
53 changes: 53 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,59 @@ destroys other agents' in-progress work:
- For non-code files: use targeted `Edit` tool calls with scoped
`old_string` / `new_string` pairs.

### Communication during a task

- Say in one sentence what you are about to do before the first tool
call. After that, speak up when you find something that changes the
plan or the conclusion — not once per file read.
- Lead the closing message with the outcome: what happened, what
broke, or what you found. Supporting detail goes underneath it, for
the reader who wants it.
- Keep answers short and caveats shorter. When asked to explain a
design decision or a metric, give the high-level answer and expand
only if asked.

### Length of written deliverables

Documents produced here — audit and triage reports, GitHub issue
bodies, changelog entries, lessons-learned drafts, book pages — should
be as long as their substance requires and no longer. No filler
sections, no summary that restates the section above it, no
boilerplate heading carrying nothing. A skill's report template is a
maximum shape, not a quota: drop a section with no findings rather
than padding it. `doc-author` and
[`docs/conventions/documentation.md`](docs/conventions/documentation.md)
own the prose rules; this length rule applies to every other written
artifact too.

### Delegating to subagents

Skills that fan work out set their own agent counts — follow them.
Outside those, the default is to do the work yourself:

- Delegate only for work that is genuinely independent and larger than
a handful of tool calls: a wide multi-file investigation, one fix
per crate. Reading three files and editing one is not worth an
agent.
- One agent that can carry the whole task beats three that split it.
- Do not spawn an agent to check work you just did. The independent
reviewers in `simplify-rust`, `review`, and `doc-author` exist
because a fresh context catches what an authoring context cannot;
that is a different thing from re-verifying your own output, which
costs tokens and finds nothing.
- Keep concurrent `isolation: "worktree"` agents to six or fewer.
Each clones the workspace, and this workspace has twelve crate
manifests, so unbounded fan-out has filled the disk with scratch
clones. Queue the remainder and start each as a slot frees. The
fan-out skills encode this same six: `batch-fix` caps wave size,
`improve-crate` and `cleanup-crate` cap concurrent area agents.
- Match the model to the stage. The Agent tool takes `model`, so a
mechanical pass (collecting a diff, running a lint, filling in a
template) can run on a smaller one while analysis keeps the default.
There is no reasoning-effort knob on the Agent tool — only
`Workflow`'s `agent()` accepts one, and no skill here uses
`Workflow`.

### Skills available under `.claude/skills/`

| Skill | Use when… |
Expand Down
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -952,9 +952,26 @@ book-serve:
# demand and is not tracked. Untranslated or stale (fuzzy) entries
# fall back to English at build time, so doc PRs never block on
# translation. See docs/development/translations.md.
#
# `-d` is absolute on purpose: mdbook 0.4 resolved a relative dest
# against the book root, 0.5 resolves it against the cwd. Under 0.5 the
# former `-d po` silently wrote the pot to ./po/ at the repo root, and
# book-po-update then failed on the missing file.
book-pot:
@command -v mdbook-xgettext >/dev/null 2>&1 || { echo "mdbook-xgettext not found; install with: cargo install mdbook-i18n-helpers --version '=0.3.6' --locked"; exit 1; }
MDBOOK_OUTPUT='{"xgettext": {}}' mdbook build big-code-analysis-book -d po
@command -v mdbook >/dev/null 2>&1 || { echo "mdbook not found; this workflow needs the 0.4.x line (see MDBOOK_VERSION in .github/workflows/pages.yml and docs/development/translations.md)"; exit 1; }
@ver="$$(mdbook --version | awk '{print $$2}' | tr -d v)"; \
case "$$ver" in \
0.4.*) ;; \
*) echo "mdbook $$ver cannot be paired with mdbook-i18n-helpers 0.3.x."; \
echo "CI pins mdbook 0.4.40 + helpers 0.3.6 (MDBOOK_VERSION in"; \
echo ".github/workflows/pages.yml); helpers 0.4.0+ target mdbook 0.5."; \
echo "A mismatched pair still produces a pot, but one that splits"; \
echo "code blocks per line instead of whole — msgmerge then marks"; \
echo "every code-block entry fuzzy and silently shreds po/ja.po."; \
exit 1 ;; \
esac
MDBOOK_OUTPUT='{"xgettext": {}}' mdbook build big-code-analysis-book -d "$(CURDIR)/big-code-analysis-book/po"

book-po-update: book-pot
msgmerge --update --backup=off big-code-analysis-book/po/ja.po big-code-analysis-book/po/messages.pot
Expand Down
Loading