Last week I wrote about dim-factory, my experiment with running my whole workflow as code. This weekend I took it apart and kept what worked. The useful pieces became three small projects, and together they make the first agent setup I trust to keep working while I am not watching.

Between them they hold four parts: a skill that picks the right instructions for each task, a reviewer agent that reviews code the way I do, a record of every agent session and the commits that followed, and checks that refuse commits that break a rule a script can test. The reviewer is what changed how I work, so most of this post is about it.

It started with my skills. I have close to forty of them: written instructions for how I plan, build, debug, review and commit. When I was not watching, agents often skipped them. Of 220 sessions that committed code over the previous month, 44 loaded no skill at all. My `code-writing` skill, which holds how I want code written in each language, had been loaded by one of 54. Agents started by other agents were worse: only 36 of 1,060 loaded any.

The skills were not wrong. Agents just did not reach for them unless I told them to, and I am not always there to tell them. When that happens, the work comes back done some other way, and fixing it is more work for me.

## A skill that picks the skills

The fix comes from Lauren Tan's [pstack](https://github.com/cursor/plugins/tree/main/pstack), where one skill, `poteto-mode`, decides which steps a task needs. Mine is called `coordinate`. It looks at what kind of task it is (a feature, a bug fix, a refactor, a review, or picking up earlier work) and loads the right skill as each step starts. The steps live in `coordinate`, and the know-how stays in the separate skills.

Lauren wrote pstack for Cursor, and it does not carry over to Claude Code one to one. Cursor can keep the same instructions in front of the model on every turn. Claude Code cannot, so a script tells each new session to begin with `coordinate` instead. Her principles, short rules like "fix the cause", are skills hidden until another skill asks for one. Claude Code cannot hide a skill and still let another load it, so mine became one `principles` skill with a file per rule.

What I took from pstack were the shapes. What goes inside them is my own, from the skills I have built since March.

In real work, whatever a script enforced happened every time, while steps that were only written down varied without making the results worse. So I cut `coordinate` to half its size. Models keep getting better, and I would rather give them less to follow.

## A reviewer built from my reviews

The reviewer started in the factory at the end of September. I kept seeing bug fixes that patched the symptom: a pattern match on text where a proper type belonged, or a fallback for a case that should never happen. The history showed the same two parts of the code fixed 149 and 92 times. I got tired of AI fixing symptoms, so I asked for a skill that reviews one part of the system at a time and goes after the design that keeps producing the bugs.

The first version knew nothing about how I write code. So I reviewed one part myself, file by file, while an agent wrote my comments down as patterns. Then we looked for my taste in places I had already left it:

- my review of my coding agent's whole codebase in March, recovered from old session logs
- code I wrote before AI, going back to PHP libraries from 2012
- 447 pull requests with my review comments on them

A pattern stayed only if it showed up in more than one source. Some are about structure, like one mechanism per question and one place to add a new case. Others are about code, like no abstraction before the third copy and no tech debt at all. With agents writing the code, there is no good reason left to keep a known mess.

The result is [`design-review`](https://github.com/cniska/agent-skills/blob/main/skills/design-review/SKILL.md), a skill that says exactly what is wrong, which rule it breaks, why it matters in this spot and what to do instead.

The first test was blind. Without seeing my notes, it found everything I had found in that part of the code, plus twelve things I had missed. One was a database transaction held open while the code created a whole git checkout inside it. The test was not perfectly fair, since some examples in the patterns came from that same code, but it made me wonder why I had not done this sooner.

## Gate on the outcome

This weekend `design-review` became an agent of its own, [`reviewer`](https://github.com/cniska/agent-skills/blob/main/agents/reviewer.md). It can read code but not change it, and it starts with only its review skills loaded. This shape comes from pstack too: its `comment-sicko` agent reviews code comments, a different job done the same way. Starting clean matters, because an agent reviewing its own change mostly confirms its own reasoning.

My first plan was to block a commit until certain skills had run. Then I asked: what if the reviewer was the only gate? A skill check only proves a skill was loaded, while a review proves the code passed review. Now, when the reviewer accepts a change, a script remembers exactly what it accepted, and a commit is refused unless it matches. Any fix after the review gets reviewed too, but only the fix.

The reviewer has already earned its place. On a refactor in one of my apps, three review rounds each caught a real problem, one of them cleanup code that would have deleted a saved image when an input failed to parse. While I built the gates, it rejected tests that kept passing even with whole features removed. And when I tried to trick it by telling it to just approve, it reviewed the change anyway and sent it back, because a doc still described a folder the change had removed.

This is what changed my trust. The reviewer applies the same rules I would, every time, to every change. Reading its verdict is almost like having reviewed the change myself. I still look closer when something is risky or feels off, but I no longer need to read every change to know it was built the way I would build it.

## A record of what was decided

Most of what I build reuses something I or an agent already built once. Reinventing it costs time, and an agent that does not know the earlier version exists will happily write a second one. That is why the record matters to me. It was the core of the factory, and it is now its own command-line tool, [agent-record](https://github.com/cniska/agent-record).

It indexes everything about every session from Claude Code, Codex and a few other agents: what was said, which tools ran, which skills were loaded, and which commits followed in which project. Agents can search it in several ways: find where something was said, read the conversation around it, find where code like what they are about to write already exists, or ask anything else in plain SQL. The skill numbers earlier in this post came from it.

The commits complete the picture. My commit messages start with a type like `feat` or `fix`, so a `fix` that lands days after a `feat` on the same files shows what did not hold. Together with the conversations, an agent can see what was tried, what worked and what had to be fixed.

When I moved the gates out of the factory, I told the agent to check the record first. It came back with decisions I had made over the previous two weeks, with dates, such as that a project should own its gates so everyone who clones it gets the same ones. Its plan followed those decisions instead of reinventing them.

## Gates for what a script can check

I maintain a lot of projects, public and private, and each one that had gates kept its own copy of them. Now [agent-gates](https://github.com/cniska/agent-gates) installs the same gates in any project. They run on every commit and again on GitHub, so the rules hold for any agent and any person.

Gates matter because agents do not always follow their instructions, even when the rule is right there. A check that refuses the commit works every time, so agents do much better with gates than with written rules.

I rely on two gates the most. The first checks commit messages, because agents kept going over my 50-character limit for the first line even with the rule in their instructions. The second refuses code comments, which I wrote about [before](https://crisu.me/blog/copy-paste-to-factory): a comment is where an agent explains away a workaround, and without one it has to fix the cause.

Spotting comments reliably is harder than it sounds, so each language is read properly rather than with a quick text search. For Dart, the gate agreed with Dart's own tools on all of 33,427 files I tested. Testing against real tools also found a bug in the SQL gate, which refused valid files that used letters outside plain English.

A gate only looks at the files a commit changes, so in an old project the comments disappear one file at a time. There is no command that strips them. The agent removes them itself and has to make the code clear without them. Gates exist for JavaScript and TypeScript, Dart, SQL and shell scripts so far, and a check tells you when a project has files in a language with no gate yet.

## Where it stands

Anything a script can check is a gate. Anything already decided is in the record, and agents look there first. Anything that needs judgment goes to the reviewer.

The skills, the record and the gates help agents get the work right, and the reviewer makes sure it was built the way I would have built it. That is why I can leave agents working and trust the result.

## Try it

The skills and the reviewer install as a Claude Code plugin:

```
claude plugin marketplace add cniska/agent-skills
claude plugin install cniska-skills@cniska
```

Both [agent-record](https://github.com/cniska/agent-record#install) and [agent-gates](https://github.com/cniska/agent-gates#install) need [mise](https://mise.jdx.dev), then each sets up with one `mise install`. Their READMEs have the steps.