# From requests to mechanisms: a week of risk-tiered review and a gh wrapper for agent PRs (karasu weekly)

> Source: <https://dev.to/kompiro/from-requests-to-mechanisms-a-week-of-risk-tiered-review-and-a-gh-wrapper-for-agent-prs-karasu-36d0>
> Published: 2026-11-01 00:13:52+00:00

[karasu](https://github.com/kompiro/karasu) is a text modeling language for system architecture, inspired by the C4 model.

You describe your system in `.krs` files and karasu draws the views: system, deployment, and team structure.

I haven't written its code by hand; everything goes through Claude Code.

This is the fourth weekly update.

It covers changes merged from October 25 to 31.

This time I start with what I learned building with agents, then go through what shipped.

**This week in numbers:** 70 PRs merged (12 of them Dependabot) and 18 decisions recorded as ADRs.

15 of the ADRs came in ADR-only PRs, and 3 were added inside implementation PRs.

On October 25 the monthly release train left for the first time, shipping karasu 0.10.0 and the Visual Studio Code extension 0.5.0.

**Risk tiers that set how deep a review goes are now running.**

Last week I wrote that the design was merged and the implementation was still to come; it landed on October 27.

Each PR falls into one of four tiers by the paths it touches.

Every tier except High puts a local review record in the PR body.

It's a table of who reviewed and, for each finding, whether it was fixed or declined.

A required check confirms the record is for the PR's latest commit.

This week 76 PRs were closed and 70 of them merged (a 92% merge rate).

Of the 50 merged with a tier label, 18 were High, 10 Design, 12 Medium, and 10 Low.

I merged the 10 Low PRs without reading the diff, so the auto-merge rate is 20%.

9 of the 10 were ADR PRs, and the other was the monthly review of test perspectives.

9 were merged within 3 minutes of leaving draft.

The 22 Medium and Low PRs were merged without going through CodeRabbit.

High is common because much of this week's work touched the review and process machinery itself.

Changes to that machinery are set to land in High.

It'll take a few more weeks of counting to see how the tiers split in everyday development.

While counting, I found 4 High PRs that CodeRabbit never reviewed.

I haven't checked yet whether they slipped through or were left out for a reason.

I'll look into it before the next update.

**Over the past week I came across several talks and posts on not reading code** (all in Japanese).

I never kept reading all the code of the systems I build at work.

Even in reviews, I don't read much of the surrounding code.

Since I started using AI, I have it check the surrounding situation while I read the parts that concern me in depth.

The parts that don't concern me, I now skim.

I think I can do that because I trust AI about as much as a capable contractor.

The trust I mean is close to Minodriven's "using it without caring about its insides."

But what I want to get right is less the code itself than the ground the code is written on.

Even a capable contractor, not knowing the team's agreements and not following its steps, writes code full of places that concern me.

Agents are the same.

If the material to work without hesitation arrives before writing starts, and the steps are followed while writing, I should be able to read less.

In karasu, I set up those two like this.

**Before writing: leave the agent less room to get lost.**

In karasu, a large change merges a design document first and is implemented after.

This week's risk tiers (#3358 → #3365) and the `docs/process.md` split described in this section (#3394 → #3402) both went in that order.

Past decisions are recorded as ADRs, and a step to check them before designing is in the skills.

On top of that, I cut down how much has to be read.

`docs/process.md` is the development guide every session reads in full first.

It had grown to 46 KB.

When all of it is read every time, the step needed right now gets buried in the others.

So I kept only the flow that's always needed, at 14,876 bytes, and moved the rest into topic files under `docs/process/`.

The guide has an index that says when to read each file.

For example, the review tiers and how to write the record are read in the step just before a PR leaves draft.

I measured how far this read-it-when-needed approach (Progressive Disclosure) reaches current models in [a September post](https://fukurou.kompiro.dev/en/progressive-disclosure/).

Then, path-scoped rules that load when a file is opened almost never fired, while steps written into skills got through.

This split doesn't rely on loading on file open either.

The always-read guide names which file to read at which step.

**While writing: move "requests" into mechanisms.**

I had shrunk `docs/process.md` to 12.7 KB once before.

It had grown back to 46 KB.

Tidying by hand was undone as steps were added.

This time I added `pnpm lint:process-index`, which checks the index-to-file mapping, the links, and the guide's size in lefthook and CI.

Last week I wrote that a check refusing `gh pr ready` before the local review was in a PR. That's merged too.

The first version was a Claude Code hook that inspected the text of the command about to run.

But each CodeRabbit review found another way around it: `gh` after a heredoc, `gh` behind `env -u` or `timeout -s`, `gh` through `eval` or `xargs`.

The command text can't tell you what will finally run.

After 20 CodeRabbit reviews, it was rebuilt as a `gh` wrapper placed first on the session's PATH, deciding from the arguments `gh` actually receives.

What wasn't followed when it was written in the guide, I'm moving into lint, the `gh` wrapper, and required checks.

That's Watanabe's move from "request" to mechanical check.

Low's auto-merge sits on top of these two.

I'll keep counting the merge rate and the auto-merge rate in the coming weeks.

I also plan to track whether bugs later turn up in PRs merged without reading.

I'll write about "ground that lets me read less" in a separate post.

The two demo videos in last week's update were recorded by an agent.

This week added the skill behind them (`demo-video`) and `pnpm demo-video:record <scenario> --lang en|ja`.

It works like this:

`karasu serve`), or Visual Studio Code with the extension, on an Xvfb virtual display.
Captions and title cards are kept per language, so one scenario produces both an English and a Japanese video.

Visual Studio Code is driven over CDP (Chrome DevTools Protocol).

ExTester, a UI testing tool for Visual Studio Code extensions, isn't used, because its ChromeDriver doesn't run in the arm64 devcontainer.

**Once the video is made, the agent looks at it.**

The agent can open images with Read, but not videos.

So the recording is cut into a frame every 3 seconds and tiled into one image (a contact sheet), which the agent opens with Read.

The skill's end state says: don't report "done" without having looked at what's in the video.

Below is last week's `serve-import` video (71 seconds), cut every 3 seconds.

The cards are blue up to the first frame of the second row and green in the second frame.

The code panel at the top right of that same frame shows the diff that changed the colors in `theme.krs.style`.

A single frame with the green cards only tells you the cards are green.

Laid side by side, the frames show the change: the style sheet was saved and the view was redrawn.

The caption text, and the flow from the Share dialog to the public app opening, can also be read from this one image.

The skill also has a table of pitfalls hit while recording.

It has rows like "a 'Do you want to save your changes?' prompt shows at the end of the video", "right-clicking an edge opens another edge's menu", and "a translation bar appears".

None of these show up in an exit code or a log; they show up only on screen.

The save prompt appears when Visual Studio Code closes.

A screenshot taken just before closing wouldn't catch it.

**I brought the same practice to fukurou.**

In the repository where I write this update, I added ffmpeg to the devcontainer and rewrote the weekly-update steps.

Until then, the session couldn't see inside the videos, so I checked that the text matched them myself.

Now the session builds the contact sheet, opens it, and checks for itself.

The image above was made with those steps.

Looking back, the only reason it couldn't check before was that ffmpeg wasn't installed.

I've been writing "the agent looks at the video," but what it actually opens is one still image.

"Looking" here means reading time folded into a single image.

Even so, it can read changes over the course of a video that a single screenshot can't show.

Reading a screen recording of a bug the same way might help reproduce it or guess at the cause.

But I don't yet have a karasu bug that was found or fixed from a video.

The next time a screen-related bug comes in, I'll start by handing the recording to the agent.

On October 25 the monthly release train left for the first time.

On the last Sunday of each month, it ships whatever is unreleased.

That morning, a branch with the version bumps and a tracking Issue open automatically.

All I do is open the PR from the Issue's link, read the CHANGELOG, and merge.

| Package | From | To | 
|---|---|---|
| `karasu` (CLI) | 0.9.0 | 0.10.0 | 
| `@karasu-tools/core` | 0.4.0 | 0.4.1 | 
| `karasu-vscode` (Visual Studio Code extension) | 0.4.0 | 0.5.0 | 
| `karasu-skills` | 0.2.0 | 0.2.1 | 

It ships 16 pending changesets.

The first train ran without stopping, all the way to npm and the Visual Studio Code Marketplace.

It was the first time the Marketplace publish rode the train.

Both highlights from last week (import resolution in `karasu serve`, and the Visual Studio Code preview running the app's viewer) arrived on this train.

The `.krs` language is unchanged.

One change in `.krs.style`, though: a stray `-` or non-ASCII character that isn't part of any name used to be dropped silently.

Now it's an error.

Lines you thought were taking effect but weren't will now show up.

**The Visual Studio Code extension also gets a weekly prerelease.**

With only a monthly train, a change merged to main can take up to a month to reach the extension.

So every Wednesday the extension is built from the tip of main and published to the Marketplace prerelease channel.

The version carries the date: `<stable major>.<minor>.<YYYYMMDDNN>`.

A week whose contents match the previous version is skipped.

"The contents match" means every file in the `.vsix` is the same, except for the version number in `package.json`.

I checked beforehand that this can be decided.

I compared the Marketplace 0.5.0 `.vsix` with one built locally from the same commit, and every file matched except `THIRD_PARTY_NOTICES.md`.

That one file differs only in the platform of a bundled binary (linux-arm64 locally, linux-x64 on CI).

Two CI builds should match.

To use it, switch to the prerelease version on the extension's page in the Extensions view.

The first one is due on Wednesday, November 4.

**Edge labels no longer sit on frame borders.**

Drill-down and group borders are now obstacles for labels.

Across four models and `examples/en` (5,061 cases), labels crossed by a border went from 132 to 0, and labels pushed to the far side of a frame went from 57 to 0.

In exchange, labels omitted under `auto` went up from 1,745 to 1,784.

**Edges no longer cut through cards when a route exists.**

These are edges between cards in the same row.

The cause wasn't the route: all five candidate spots where an edge leaves the card were taken.

As a last resort, it now searches the whole side of the card for a spot.

Edges cutting through a card went from 4 to 1.

**Style diagnostics and rendering agree now.**

The valid ranges for `.krs.style` values were written in three places, and they disagreed.

A diagnostic would say a value was ignored while the value got clamped or drawn anyway.

The ranges now live only in the schema, and a declaration a diagnostic rejects isn't used for rendering either.

So rendering changes too.

An out-of-range `label-position` is dropped instead of clamped.

A non-zero length without a unit, a wrong unit, a bad hex color, or a color name with the wrong case (`White`) isn't drawn.

An empty value like `color: ;` makes `karasu render` fail.

This ships on the next train (November 29).

`karasu serve` returned what a symlink pointed to.

Say the served directory held a symlink pointing outside it (like `notes.krs -> ~/.ssh/id_rsa`).

Then `/api/file/:name` returned the contents of the link's target.

This is in the released 0.10.0.

Paths are now resolved to the real file, and nothing outside the served directory is returned.

`/api/watch` also stopped sending `Access-Control-Allow-Origin: *`.

The fix ships on the next train.

Until then, it's safer not to run `karasu serve` in a directory with symlinks that point outside it.

**Smaller changes.**

`analyze()` wrote a cycle flag into the model it was given, so the same model rendered differently before and after `analyze()`. Cycles are now computed when the model is built, and `analyze()` only reads.

Package sources now have a 600-line limit per file. The 33 files over it are pinned at their current length, and the pin goes down as they shrink. Splitting `svg-renderer.ts` (2,965 lines), `layout.ts` (1,719 lines), and `view-extract.ts` (2,042 lines) has started.

GitHub Actions versions are now pinned with `actions.lock` (GA on October 28). It also pins actions that workflows use indirectly.

`A -> B` and `B -> A`) as one double-headed arrow is merged. In the prototype, one view of the Umami model went from 41 edges to 27 and from 157 crossings to 61. The implementation PR is in review.`Host` header.
