haiodo.blog
57% fewer long comments. My hook had almost nothing to do with it

57% fewer long comments. My hook had almost nothing to do with it

A week ago I found that my comment rules never reached Claude's subagents, and fixed it. I re-measured on 11 thousand edits from Claude transcripts: the 57% drop came from a model change, the rules at startup changed almost nothing. What works is the other hook - the one that shows a finding right after the edit.

What hum1izer is


hum1izer is a CLI in Go. It looks for officialese, clichés and AI traces in prose, code comments and commit messages, in Russian and English. It counts only what grep and arithmetic can count: rules live in YAML, plus rhythm, nominalization and a cleanliness score from 0 to 100. It doesn’t write text. It shows a finding with a line number and says what to do about it, and a human decides.

This is the third post in a series about slop. In the first one, “The em dash doesn’t give away a neural network. I checked on a corpus”, I measured the usual signs of machine text on a corpus and found that the tool catches officialese, not authorship. In the second, “Chasing AI-slop code” (in Russian), I looked for slop in the code itself: found no detector, found one stable quantity - nesting depth. Now the question is practical: does hum1izer help an agent write cleaner if it’s wired straight into the agent’s work?

hum1izer post.md              # prose: report with suggestions
hum1izer --code ./src         # code comments
hum1izer --code .             # same plus the last 20 commits
hum1izer tui ./src            # go through findings by hand, three panes

Most of my code is now written by Claude and its subagents, and they comment in their own way: three to five lines retelling what the function does, edit history right in the code, ===== banners, a ponytail: tag from a plugin at the start of a line.

One command handles that:

hum1izer install --claude --hooks

It installs the skill and adds three hooks to ~/.claude/settings.json:

EventWhat it does
SessionStartputs the rules into context: a comment is 1-2 lines, answers “why”, not “what”, no banners or tool tags
SubagentStartsame for every subagent
PostToolUse on Edit and Writechecks the changed lines of the file and returns findings to the agent

The last one fires right after an edit. No findings - it stays silent. Otherwise the agent gets this:

hum1izer found the following in .../Session.swift. Address the findings:
fix the block or mark `fix --block <id> --keep` if the rule fired for nothing.

## .../Session.swift:190-192
block 5185d5332088 | `swift` | comment | weight 4

- **Tool tag in a comment** (hard, line 192) - Drop the prefix, keep the thought
- **Long comment** (line 190) - Fit in 2 lines: keep the reasoning, move the description to docs

// Same stand and same workspace - continue the previous session: logging in
// again wipes the last chat, and screen restore can't be checked.
// ponytail: not checking the account, there's only one in the run.

Then the agent either rewrites the block or marks it --keep if the rule fired for nothing.


Where the task came from

The hooks have been on since September 17. On September 29 I decided to check whether they help and went through the transcripts in ~/.claude/projects. That gave 9937 code edits. The metric is simple: blocks with findings per 100 added comment lines. All groups are run through the same hum1izer version.

GroupComment linesLong per 100 lines
main session, before hooks18333.82
main session, with hooks6251.76
subagents, before hooks69089.25
subagents, with hooks137827.89

Subagents made 95% of the edits. They wrote long comments 2.5-4.5 times as often as the main session, and the hooks cut that by only 15%.

There were two reasons.

SessionStart doesn’t fire for subagents. It’s in the Claude Code docs, in the section on subagent hooks. The rules showed up in the transcript of one subagent out of 1020. The rest learned about a rule only from a finding, that is, after they had already broken it.

The cache of shown blocks was keyed by session_id. The hook doesn’t repeat a block it has already shown, otherwise the context grows with every edit. But a subagent comes with its parent’s session_id and its own agent_id. A block the parent had seen was silently never shown to the subagent.

An engineer with a clipboard outside a door, the rules hang outside, robots work inside

The fix: a SubagentStart hook that hands out the same rules, and session_id plus agent_id as the cache key. It costs about 1.5 KB of context per subagent.

I wrote the criterion down in advance, in the task: re-measure in a week. If subagents’ long comments drop by less than 15%, the startup rules didn’t help, and I need another way.


A week later: minus 57%

Same script, same hum1izer version for all groups. Over the week 505 subagents got the rules, made 1073 edits and added 4739 comment lines.

SubagentsComment linesLong per 100 lines
before hooks69089.25
post-edit check only138997.94
plus rules at startup47393.40

Minus 57% from the week before. The criterion is met with room to spare, the task can be closed.


Except the model changed too

The same week subagents in one project, project A from here on, moved from sonnet-5 to opus-5-5 and sonnet-5-5. The other one, project B, stayed on sonnet-5. When groups differ by more than the hook, you can’t compare them as a whole. Split by model:

SubagentsModelLinesLong per 100 lines
post-edit check onlysonnet-5137457.99
plus rules at startupsonnet-513877.43
plus rules at startupopus-5-531151.67

On the same model the startup rules gave minus 7%. That’s below my own threshold. In project B on sonnet-5 it even got worse: 6.31 before, 7.04 after.

In project A, where the model changed, it went from 10.23 to 1.69. That’s where the 57% came from.

An old and a new robot at drafting tables: the old one has a long scroll, the new one a short note

Rules at the start of a session barely change the first draft of a comment. The model does. Had I not split the data by model, I’d have booked someone else’s result as my win.


Where the hook does work

The first measurement looks at the draft - the comment at the moment of the edit, before the agent sees any finding. But the post-edit check exists exactly to fix the draft. So I counted something else: what the agent did with the block after the finding, in the same transcript. Each block is counted once.

Subagents, sonnet-5BlocksFixedMarked --keepLeft as is
before September 28233642%2%54%
the week after18993%0%5%

Same model, and it fixes more than twice as often. It’s not a change in project mix: inside project B it went from 35% to 91%, inside project A from 50% to 95%.

Whose contribution it is, I can’t say. Three changes landed between the two measurements: the hook started returning only new findings instead of the whole file (the 90th percentile of its output fell from 6745 to 3469 characters), subagents started getting the rules, and each subagent got its own cache. This data can’t separate them.

With the new models my “fixed” heuristic falls short: it looks for an edit whose old text contains the first line of the block. By it, opus-5-5 fixed 27% of blocks and sonnet-5-5 6%. So I ran hum1izer over the files as they are now. Of 322 blocks shown to subagents during the week, 285 are clean now, 22 still have a finding, and 15 files no longer exist. For opus-5-5, all 79 remaining blocks are clean. Who cleaned them - the subagent itself, the main session or me by hand - the transcript doesn’t say.


What made it into git

A transcript shows the draft, git shows what I committed. I took only my own commits: in project A half of the week’s commits are a colleague’s, and I don’t have their sessions. Then ran the added lines.

ProjectPeriodComment linesLong per 100Other per 100
ASep 17-2830590.920.39
ASep 28 - Oct 553980.520.52
BSep 17-2826992.561.19
BSep 28 - Oct 515062.462.46

Committed code has 3-11 times fewer long comments than the drafts. Something removes them between the edit and the commit: the hook, the main session, or me. Git can’t tell which.

But the “other” column grew. In project B it doubled. I looked at why.

A robot clerk stretches a sheet sideways so it fits between two strips of tape

Long comment line - a comment line longer than 100 characters. In project B there were 3 of them the week before and 21 this week. In project A they’re 26 of the 28 “other” findings. The agent got the rule “no more than two lines” and follows it to the letter: three lines of 80 characters become two lines of 120. Same thought, same length, check passes.

This has happened before. In the first post the agent glued three lines into one of 140 characters, and I added a line length limit. The limit works: it catches such lines. But the agent starts from them instead of ending there, and every such line has to be caught after the edit.

In the post about code shape (in Russian) I wrote that an agent learns to ignore a threshold that gets broken too often. Here it doesn’t ignore the rule, it routes around it: follows the letter and leaves alone what the rule was written for.

There’s a separate rule for this, and the hook shows it. But in opus-5-5 drafts long lines run at 0.77 per 100 comment lines against 0.36 for sonnet-5 a week earlier: it writes fewer blocks over two lines, but wider lines.


Summary

The “hum1izer findings” metric is scored by hum1izer itself. The next step in the task is a blind sample: 30-50 comments from each group, rated by me without knowing where they came from.