karasu weekly update: a working npm serve, release 0.9.0, and zero edges through cards

  • #karasu
  • #ai
  • #claude-code
  • #architecture

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 second weekly update. It covers changes merged from October 10 to 17.

This week in numbers: 88 PRs merged (16 of them Dependabot) and 12 decisions recorded as ADRs. On October 15 I cut a release of four packages: karasu 0.9.0, @karasu-tools/core 0.4.0, karasu-vscode 0.4.0, and karasu-skills 0.2.0. The .krs language v2.0 from last week’s post reached npm and the Visual Studio Code Marketplace with this release.

Highlight 1: karasu serve from npm didn’t work

In the 0.8.0 npm package, karasu serve answered every request with “App not built”. The tarball held only 5 files and no preview app. serve looked for the app at a path relative to the monorepo, so it worked inside the repository and failed when installed from npm. In other words, the README’s npx --yes karasu@latest serve . had never worked outside the monorepo. I noticed while trying the reverse-architecture skill on an outside repository.

The fix ships a serve-only build of the app in dist/app/. A new CI step packs the CLI with pnpm pack, unpacks the tarball, and starts serve from it. I confirmed that this test fails when dist/app/ is dropped from the package’s file list. Only a test that runs the packaged artifact can catch this kind of bug.

The bundled app was 16MB (97 files), and most of it was the Monaco editor. serve only shows the preview and never opens the editor. So the serve build now has its own entry point, and the build fails if any Monaco module ends up in the bundle. I checked the guard by adding the Monaco import back on purpose: the build failed and named 1,233 modules. The serve app is now 1.7MB (4 files). The released 0.9.0 package is 3.3MB unpacked (12 files).

0.9.0 shipped on the third release run. Two npm Trusted Publisher pitfalls stopped karasu-skills from publishing. Trusted Publisher is the npm setting that lets GitHub Actions publish without a token. karasu 0.9.0 depends on karasu-skills 0.2.0, so it stopped too.

  • E404: A newly registered configuration is “not yet validated”, and it expires unless a publish through it succeeds within 2 days. I registered the karasu-skills configuration right after publishing 0.1.0 by hand. The release uses changeset publish, which skips versions already on npm, so 0.1.0 could never validate the configuration. It expired before 0.2.0 went out.
  • E403: A configuration created after September 3, 2026 allows only npm stage publish by default. The release workflow runs npm publish, so “Allow npm publish” had to be checked.

The release docs now say to register the configuration right before merging the release PR that bumps the package.

Highlight 2: no more edges through cards

Edge routing got more work this week. The target was edges drawn straight across cards they should go around.

  • Edges between cards in the same row. These used to cut straight through any card standing between them. Now they go around through the channel above or below the row. The side with fewer crossings against existing edges wins.
  • Edges into a side column. In the Dify model, three edges to and from Gateway crossed 7 cards in each of two views. Now they run down the gap beside the column.

Three smaller fixes went in as well. Lanes in a channel are now ordered by whether each edge turns up or down. Routes that folded back over the same channel are collapsed into one run. In a drill-down view, lanes and labels stay inside the focused frame.

Each fix was measured on real models reverse-engineered from open-source projects. The main set is Umami, Dify, Twenty, and WordPress, 1,542 views in total. Two numbers are tracked for every fix: how many problem spots it removes, and how many views it changes as a side effect.

Fix Problem spots What else changed
Same-row edges Edges through cards: 19 → 0 5 views; +131 crossings (all drawn with hops)
Side-column edges Edges through cards on Dify: 14 → 0 Crossings 38,147 → 38,176 (+29)
Channels bounded by the frame Runs outside the frame in a 5-card fixture: 14 → 0 5 of 7,659 edges, in 3 of 1,542 views

For the side-column fix there was also an option that cleared a wider corridor. It would have changed 13 views, so I went with the smaller change.

Smaller things worth mentioning

Crossing marks now dim with their edges. Hovering a card or an edge dims the unrelated edges. The small arcs drawn where edges cross (“hops”) and the count chips stayed bright, though, and kept tracing the shape of the dimmed edges. Now the marks dim along with their edges. To make that possible, the hops that last week merged into one <path> are split again, one <path> per edge. On the Dify model, output grew by 4.29% and hop <path> elements went from 757 to 2,585. The largest view still renders in about the same time: 33.3 ms before, 33.5 ms after. Edges that ignored the mouse, such as those in the deployment view, now respond to hover too.

Nodes with the same name are no longer mixed up. Api inside Shop (Shop.Api) and a top-level service "Shop.Api" used to share one internal key. Every node and edge identity key now comes from one helper that cannot produce collisions. When two systems declare a node with the same ID, each internal map used to pick a different winner; they now follow one rule.

karasu diff compares multiple systems correctly. Compare mode diffed only the first system. Adding or removing the first system made it compare two different systems, so unchanged cards showed up as removed and added. Systems are now paired by ID, not by position.

Small fixes. In a style sheet, label-offset: -20px lost its sign and behaved like 20px. karasu render <directory> used to fail with “File not found”; it now reads the directory’s index.krs.

The e2e tests found two real bugs. This week added a lot of Playwright end-to-end tests, and no test is left behind test.skip. Along the way they found two bugs that jsdom unit tests could not see. Neither is fixed yet. The first: shortcuts such as Ctrl/Cmd+B fire while you type in the editor. Monaco’s input target changed to an element that is neither contenteditable nor a textarea, so the “is the user typing?” check misses it. The second: reloading while the deployment view or the matrix is open drops you back on the system view.

What building with agents looked like this week

Dependabot triage runs on a hane skill. karasu doesn’t merge Dependabot PRs as they come. Triage uses the dependabot skill from hane, a skill collection I develop separately. hane is a Claude Code plugin that bundles skills which grew out of karasu’s development.

Running /hane:dependabot makes the agent go through these steps:

  1. Collect every open Dependabot PR.
  2. Trace each PR upstream: release notes, the code diff between tags, changes in publisher or repository ownership, install-time scripts, the lockfile’s dependency edges, known advisories, and how long ago the version was published.
  3. Write the results into a design doc, with a recommendation for each PR: merge, hold, or reject.
  4. Once I decide, merge and close the PRs, then promote the design doc to an ADR.

The skill doesn’t skip the investigation for patch releases. A tampered version dressed up as a patch is the easiest way for a supply-chain attack to get through. It never auto-merges either; a human makes every call. Because the design doc and the ADR stay in the repository, I can trace when and why any version came in.

The skill holds only the steps that apply to any repository. Rules specific to karasu live in a rules file on the karasu side (.claude/rules/dependabot.md). Examples are the 7-day cooldown and the conditions for bringing an update in through a replacement PR when Dependabot’s own diff can’t be correct. I ran three triage rounds this week with that combination.

Running them taught me two things.

The first is about the limit on open PRs. This week 24 npm updates cleared the cooldown, and a limit of 8 delivered them in three waves. Each wave needed its own triage, so I raised the limit to 25. The total number of PRs stays the same; only how many are open at once changes.

The second is that rebuilding a lockfile can move a dependency off the version you reviewed. When a replacement PR rebuilt the lock, knip resolved to 6.41.0, which had just cleared the cooldown, instead of the reviewed 6.40.0. I pinned it back to 6.40.0 and recorded the lesson in an ADR.

I classified 261 CodeRabbit findings. CodeRabbit reviews karasu’s PRs. I classified all 261 threads it left on Markdown files from August 20 to October 10. About half turned out to be real defects. So documentation stays in review.

Among the rest, two false positives kept coming back. Both came from how the rule text that CodeRabbit reads was worded. For example, a rule saying “don’t put a list of milestones in a design doc” was applied to the implementation steps that the design doc template asks for. The rule was only ever meant for the slice-plan table. So I fixed the rule text, not the CodeRabbit config, by stating its scope. That also makes the sentence harder to misread for a human who meets it out of context.

Coming next

  • A design for shipping the Visual Studio Code extension as a weekly prerelease is merged. Implementation comes next.
  • An Issue is open to move npm publishing to staged publishing under Trusted Publisher.
  • An epic is open to make the public gallery available beyond its operator, me.

Feedback and questions are welcome in the comments on dev.to.