A workshop for coding agents that runs on your machine. You hand in a request, agents build it with you, an independent principal engineer reviews it, and fixers repair what it finds. Only work that earns its hallmark leaves for GitHub. Every handoff and message is filed, so you can always see what happened and why.
The word comes from Goldsmiths' Hall in London, where silver was tested and stamped before it could be sold.
Picture Hallmark as a building seen from above. Requests come in at the Front Desk, get built in the Workshop and tested in the Assay Office. If the assayer finds problems, they go to the Repair Shop and back. Approved work is stamped and leaves from the Dispatch Dock for GitHub. Every handoff passes through the Post Room, which files a copy in Records. When an item closes, the Library keeps what it taught, and briefs the principal engineer before each review (the gold slips). The boxes are items and the figures are agents. Select a room to see what happens there.
Hallmark ships as the hallmark package and installs one command, hall. Setup is once per machine plus once per repository.
Any of these puts hall on your PATH, on macOS, Linux or Windows.
cargo install hallmark
brew install hallmark
nix profile install github:<you>/hallmark
Installs the Hallmark plugin at user level: the skills, the reviewer and fixer agents, and the hooks. Every repo and every session gets them.
hall setup claude
Run in the repo. It detects the gates, writes .hallmark.toml and a stub .hallmark/review.md for local review notes, adds a short Hallmark section to CLAUDE.md or AGENTS.md, and registers the repo.
hall init
git add .hallmark.toml .hallmark/ CLAUDE.md
git commit -m "Adopt Hallmark"
Runs the review loop for every registered repo. --at-login registers it with launchd, systemd or Task Scheduler.
hall daemon start --at-login
Each failed check names the command that fixes it.
$ hall doctor
✓ git 2.51 · gh (logged in) · claude
✓ Claude Code plugin hallmark (user)
✓ hooks: SessionStart UserPromptSubmit PreToolUse
PostToolUse Stop
✓ rindahq/platform: gates rig test, rig run build
✗ daemon not running → hall daemon start
You do three things: start work, send it to review, and check status and history. Agents do everything else. You can do all three by talking to Claude Code, or from the terminal.
# open the app: board, journals, history
hall
# start work and open Claude in its worktree
hall new "Retry failed webhook deliveries" --open
hall new --issue 212 --open
# see where things are
hall status
hall log H-0042
hall history --rejected --since 7d
# decide
hall ready H-0042
hall pull-back H-0042
hall drop H-0041 --reason "superseded by H-0044"
On a terminal these print readable text. When an agent runs them, they print JSON.
Anything that needs you lands in Your Office as an inbox entry. It shows the item, what's being asked, the options and how long it has waited. Hallmark then tells you wherever you are, and keeps telling you until you answer. Only the item that's waiting stops; the rest of the building carries on.
| Waiting for you | Example | What waits | Tells you by |
|---|---|---|---|
| Question | "The issue says usage: billed usage or raw events?" | That item. The agent's session is saved and picks up with your answer | desktop, phone |
| Escalated | "Rejected 3 times for the same missing tenant check" | That item | desktop, phone |
| Session waiting | Your Workshop session wants a permission or a reply | That session | desktop |
| Ready? | "H-0042 looks done: retries with jitter, 14 tests" | Nothing; it stays in development | desktop |
| Budget | "H-0039 reached its 800k token cap" | That item | desktop |
| Stuck | The watchman saw no event for 30 minutes, or the daemon stopped | Whatever was running | desktop, phone |
| Learnings | 4 proposed learnings to approve | Nothing | daily digest |
hall inbox.Your Workshop sessions are covered too. Claude Code fires a Notification hook when a session needs a permission or has waited for you, and Hallmark files it under the session's item.
# the Claude Code status line
rindahq/platform · H-0041 development · ⚑ 2 need you
hall ask "billed usage or raw events?" --options billed,raw --default billed --wait 4h. Its run ends with the status waiting, and Hallmark saves the session ID.hall answer Q-12 a, press a in the terminal app, or tap the button on your phone.claude -p --resume <session> with your answer, so the agent keeps its context. Answered within the hour, most of that context is still cached.If a question has a default and a wait, and nobody answers in time, the agent goes ahead with the default and the journal says so. Escalations never use a default.
# .hallmark.toml (or your user config)
[notify]
desktop = true
phone = "ntfy://ntfy.sh/hallmark-7f3k2q"
quiet_hours = "22:00-07:30" # phone waits, desktop still shows
remind = "2h"
digest = "08:30"
[notify.route]
question = ["desktop", "phone"]
escalated = ["desktop", "phone"]
stuck = ["desktop", "phone"]
ready = ["desktop"]
budget = ["desktop"]
learnings = ["digest"]
Anyone who knows the topic name can read a topic on the public ntfy.sh. Hallmark sends only the item, the question and the options, never code, and generates a random topic. You can also host ntfy yourself.
You don't need to mention Hallmark. Four things work together, from most explicit to the one that can't be skipped.
/hallmark <request>A slash command from the plugin's skill. Always starts a new item.
A SessionStart hook tells Claude whether this worktree belongs to an item, what state it's in, and how to start one. hall init adds the same rule to CLAUDE.md.
The hallmark skill's description covers starting work, picking up issues and asking about status, so "pick up #212" or "what's in review?" loads it.
A PreToolUse hook denies the first file edit or commit outside an item's worktree. The denial says exactly what to run, so Claude creates the item and carries on.
# .hallmark.toml
[capture]
mode = "gate" # gate: edits need an item
# ask: Claude asks you first
# off: only /hallmark starts items
# added to CLAUDE.md by hall init
## Hallmark
Changes in this repo are tracked with Hallmark.
Start each change with `hall new`, or `hall adopt`
for an existing branch. Never push: work reaches
GitHub only through review. `hall help --json`
lists every command.
Existing worktrees, such as the ones you already have, join with hall adopt, which turns the current branch into an item without moving it.
The same journey as states, with the room each one happens in. An item starts in your Claude Code session and ends when its PR opens. You own the states on the left; the review loop on the right runs on its own. Select a state to see who acts, how it moves on and what the journal records.
This is one item, H-0042, from first prompt to PR, as the terminal app would show it. Step through it, or select a point on the bar. Use the arrow keys when the controls are focused. The data is an example.
Every model run is measured and recorded in the journal. Each step has its own limits, and you can see totals by item, room, role, model or day. Only four places use a model: the Workshop, the Assay Office's review, the Repair Shop, and a little at the Front Desk. Everything else is a script.
claude -p --output-format json reports input, output and cache tokens, turns, duration and a cost per model. Hallmark writes all of it to the journal.Stop hook reads the new usage from the session transcript, so development counts too.[budget]
daily = "4M" # whole review loop
per_item = "800k" # then it goes to you
[budget.review]
model = "opus"; effort = "high"
max_usd = 3.00 # per run
daily = "2M"
[budget.fix]
model = "sonnet"; effort = "medium"
max_usd = 1.50
[budget.intake]
model = "haiku"
.hallmark/review.md get the deepest.| Limit | Set by | When it's reached |
|---|---|---|
| Per run | max_usd, passed as --max-budget-usd, plus a time limit | The run stops, and the stop is recorded. The item is retried once, then goes to you. |
| Per item | per_item | The item goes to Your Office marked "budget", with its spend so far. |
| Per room, per day | [budget.review] daily | That room closes for the day. Items wait in the Post Room, in order. |
| Daily total | daily | The daemon pauses. Your Workshop session isn't blocked; a hook adds a note that the budget is spent. |
| Claude plan limit | Your plan | When Claude reports a rate limit, the daemon pauses until it resets and records the pause. |
A one-word reply from Haiku through claude -p still loaded 29.7k tokens of context: 21.2k written to cache and 8.5k read from it. That's the fixed cost of any run before it does any work.
This design conversation, on Opus, has made 114 model calls: 18.1M tokens read from cache, 525k written to cache, and 399k output tokens. Long interactive sessions are where most tokens go, which is why the Workshop is metered too.
Costs are API list-price equivalents. They're a stable unit for comparison, not a bill. Claude plans count usage against limits rather than charging per token, and Hallmark can't read how much of your limit is left, so it enforces its own caps.
Yes, it's something you define, once, in the plugin. The role is split into a Claude Code agent (who it is and which tools it may use), a skill (how it reviews), and a schema (the exact shape of its verdict). Each repo can add local knowledge without changing the role.
hallmark plugin
├─ agents/
│ ├─ principal-engineer.md role, read-only tools, model
│ └─ fixer.md edits code, can't approve
├─ skills/
│ ├─ hallmark/ your session: new, ready, status
│ ├─ principal-review/ lenses, severity, evidence rules
│ └─ hallmark-fix/ address findings, dispute with reason
├─ schemas/verdict.json enforced output shape
└─ hooks/hooks.json every hook calls: hall hook <event>
your repo
└─ .hallmark/review.md local knowledge, read from base
---
name: principal-engineer
description: Independent reviewer for Hallmark
items. Reviews one frozen commit against its
base branch and never edits code.
tools: Read, Grep, Glob, Bash(git diff:*),
Bash(git log:*), Bash(git show:*), Bash(hall log:*),
Bash(hall library search:*)
model: opus
---
You are a principal engineer reviewing a change
you did not write. Use the principal-review skill.
Report only what you can show breaks, with the
scenario. Approve when nothing blocks.
The daemon runs it headless with a fresh context. --json-schema makes Claude Code return a verdict that always matches the schema, so the daemon never has to parse prose.
claude -p --plugin-dir "$HALLMARK_PLUGIN" --agent principal-engineer \
--json-schema "$HALLMARK_PLUGIN/schemas/verdict.json" --output-format json \
"Review H-0042 round 2: commit b81f3e4 against main"
This is a sketch; the exact flags will be checked when it's built. Everything the reviewer reads comes from the base branch, so a change can't edit its own review rules.
Each review starts with a fresh context on purpose. A long-lived reviewer would carry the biases of earlier reviews, and its growing context would be re-read on every call; this conversation alone has read 18M tokens from cache. Instead, the reviewer starts clean and gets a short briefing from the Library, a growing body of what reviews of this repository have taught. Fresh eyes, long memory.
hall library review. They live in a local store next to the journal, never in the repository, so no branch can change what its review is told.---
id: L-0017
kind: rule # rule · false-positive · map · decision
scope: ["apps/api/src/webhooks/**"]
lens: correctness
source: [H-0042/F1]
status: accepted
used: 4 # reviews it informed
last_used: 2026-10-09
---
A retry must check that the endpoint still exists
before each attempt. A deleted endpoint keeps its
queued deliveries until the job notices.
| Records (the journal) | Library (learnings) | |
|---|---|---|
| Holds | What happened | What it taught |
| Source of truth | Append-only JSONL in .git/hallmark/. Local and private, since it includes your prompts; never committed | Markdown in .hallmark/knowledge/. Committed, reviewed, and read from the base branch |
| Index | SQLite, rebuilt from the files at any time with hall reindex | Built-in SQLite full-text search, or qmd for hybrid semantic search when it's installed |
| Questions | Exact: by item, state, lens, room or date | Fuzzy: "what do we know about retries in webhooks?" |
| Per repository | One journal per repo | One knowledge folder per repo, and one qmd collection per repo, hallmark-<repo> |
| Vectors? | No. Every question has an exact answer | Helpful once wording drifts from the code. Path scopes and keyword search cover a young library |
qmd is a local search engine for Markdown, built for agents. It combines keyword search, vector search and reranking, with local models stored in SQLite. It has a JSON command line and an MCP server, and it's MIT-licensed. The Library is a folder of Markdown files, so qmd fits it without changes. It also runs locally, so searching the Library costs no Claude tokens.
It brings a Node 22 or Bun runtime, about 2 GB of local models, and on macOS a Homebrew SQLite. That's a lot to require of every install, so Hallmark makes it an option. Built-in search works everywhere; hall init offers qmd when it finds it. Hallmark always queries a single collection, because qmd ranks results from several collections in one shared pool.
# .hallmark.toml
[library]
search = "qmd" # or "builtin"
collection = "hallmark-platform"
briefing_limit = "6k" # tokens sent to each review
auto_accept = ["map"] # kinds that skip your approval
retire_after = "90d" # unused learnings
$ hall library search "webhook retries" --json
{"results": [{"id": "L-0017", "kind": "rule",
"scope": ["apps/api/src/webhooks/**"],
"score": 0.91, "used": 4}]}
CLAUDE.md stays for rules that are always true, because Claude reads it in full on every run. Situational knowledge belongs in the Library, where only the relevant entries are sent.
Agents are the main users of hall, so the command line is an API. These rules hold for every command.
--json forces it; readable text appears only on a terminal.--reason.hall ready twice returns the same review, not an error.next list of commands that make sense now.next, plus a documented exit code.hall help --json lists commands; hall schema <command> gives the output shape.hall next --role fixer claims work with a lease, so two agents never take the same item.$ hall ready
{"error": {
"code": "ITEM_IN_REVIEW",
"message": "H-0042 is in review, round 2.",
"next": ["hall pull-back H-0042",
"hall log H-0042 --kind review"]}}
$ echo $?
4
| Exit | Meaning |
|---|---|
| 0 | Done |
| 2 | Bad arguments |
| 3 | No such item |
| 4 | Not allowed in the item's current state |
| 5 | Denied by the gate |
| 6 | Checks failed |
| 7 | Claimed by another agent |
| Command | Used by | Does |
|---|---|---|
| Start and decide | ||
| hall | you | Opens the terminal app |
| hall new "<request>" | you, your session | Creates an item and its worktree. --issue 212 starts from a GitHub issue; --open opens Claude in the worktree |
| hall adopt | you, your session | Turns the current branch and worktree into an item |
| hall ready | you, your session | Freezes the commit and sends the item to review |
| hall pull-back | you | Stops review and returns the item to development |
| hall drop --reason | you | Abandons the item and keeps its journal |
| hall inbox | you, your session | Everything waiting for you, oldest first |
| hall answer <q> | you, your session | Answers a question; the asking agent resumes its session |
| See | ||
| hall status | you, any agent | Items by state, or one item in detail |
| hall log <id> | you, any agent | The item's journal. --kind review filters it |
| hall history | you | Closed and dropped items. --rejected, --lens, --since |
| hall usage | you, any agent | Tokens and cost. --by room|item|role|model|day, --since |
| hall budget | you | Shows or sets the caps in [budget] |
| hall library search | you, any agent | Searches the repo's learnings. Built-in search, or qmd when configured |
| hall library review | you | Approves or rejects proposed learnings |
| hall library retire <id> | you, librarian | Retires a learning, keeping its history |
| hall export <id> | you, shipper | The whole record as Markdown |
| Agents | ||
| hall propose-ready | dev agent | Says the work looks done, with a summary. Moves the item to Awaiting you |
| hall ask | any agent | Asks you a question with options, an optional default and a wait. Parks the item |
| hall next --role | assayer, fixer | Claims the next piece of work for that role |
| hall findings | fixer | Open findings and failing checks for the claimed item |
| hall fixed <ids> | fixer | Records which findings a commit addresses, or disputes one with --dispute |
| hall verdict | assayer | Records a schema-checked verdict |
| Machinery and setup | ||
| hall hook <event> | Claude Code hooks | Journal entries, session context and the gate |
| hall daemon | you, once | start, stop, status. Runs the checks, review, fixer, shipper and watchman |
| hall setup claude | you, once | Installs the Claude Code plugin |
| hall init | you, per repo | Writes .hallmark.toml from what it detects |
| hall notify test | you, once | Sends a test notification to every channel you configured |
| hall doctor | you, agents | Checks the install and names the fix for each problem |
| hall help --json · hall schema | agents | Command list and output shapes |
One Rust binary, hall, does everything. Run it with no arguments and it opens the terminal app, the human view of everything above. The subcommands are what agents, hooks and the daemon call. Because the hooks call the binary rather than shell scripts, it runs the same on macOS, Linux and Windows.
The app only reads the journal. Closing it never stops work; the loops run in hall daemon. Opening it on another terminal, or after a restart, shows the same state.
/hallmark ready)
ppull back to development
xdrop, with a reason
wopen the item's worktree
oopen the PR
hhistory view
/filter
uusage view
tabnext repository
Each item has an append-only file of events. Nothing is edited or deleted, so the history you read is the history that happened. Hooks write your session's events and the daemon writes the loop's. Every write goes through hall, which holds a file lock.
.git/hallmark/ shared by every worktree
├─ items/H-0042/
│ ├─ item.json title, branch, state
│ ├─ events.jsonl the journal
│ ├─ reviews/r1.md full review reports
│ └─ checks/r1.log failing check output
└─ queue/ tickets waiting for the assayer
A SQLite index, rebuilt from these files at any time, answers usage and history questions quickly. The journal lives in each repo's git directory, so it outlives worktrees and is never committed. A small registry in your user state directory lists which repos use Hallmark, so the terminal app can show them all. hall export H-0042 writes the whole record as Markdown, and the PR body links to it.
{"t":"10:04:12","actor":"you","event":"ready",
"sha":"7c2d018","note":"cap at 6 attempts done"}
{"t":"10:24:31","actor":"assayer","event":"review",
"round":2,"sha":"b81f3e4","verdict":"changes",
"findings":["F1","F2"],"report":"reviews/r2.md"}
{"t":"10:38:05","actor":"fixer","event":"commit",
"sha":"2a9e771","fixes":["F1","F2"]}
| Piece | What it does | Defined in |
|---|---|---|
| hallmark skill | Your session's side: /hallmark, starting from an issue, ready, pull-back, status and "why was it rejected". | skills/hallmark/ |
| Session hooks | Tie the session to its item, add item context at start, record prompts, turns and commits, and gate edits and pushes. | hooks/hooks.json → hall hook |
| Assayer | Runs the gates at the frozen commit, then the principal-engineer agent with the principal-review skill. | agents/principal-engineer.md |
| Fixer | Addresses open findings or failing checks in the item's worktree, commits and resubmits. It can dispute a finding, with a reason, but can't approve. | agents/fixer.md |
| Librarian | Distils closed items into learnings, proposes them for your approval, and briefs each review from the Library. | hall daemon → claude -p --model haiku |
| Watchman | Flags items with no new event for 30 minutes, and a daemon that stopped. The idea comes from Gas Town's Witness. | hall daemon |
| Shipper | Checks the hallmark, runs the ship command, writes the review summary and minor findings into the PR, links the issue, and closes the item. | hall daemon |
| Terminal app | Board, journal, findings, history, live and repos views. | hall |
Every review covers the same seven lenses. Each finding names its lens, a file and line, a severity, and the concrete case that breaks.
| Lens | Asks | Typical blocker |
|---|---|---|
| First principles | Is this the simplest change that solves the task? Does it belong in this layer? | Patches a symptom; duplicates an existing mechanism |
| Correctness | Edge cases, error paths, concurrency, idempotency, retries | A retry double-charges; two writers race |
| Security & tenancy | Authorization on every path, tenant isolation, secrets, injection | A query bypasses RLS; a secret in a log line |
| Data & migrations | Is the migration reversible and safe on live data? | A locking ALTER on a hot table |
| Evidence | Do the tests prove the behaviour? Did the test count drop? | A test asserts its own mock; a suite stopped compiling |
| Operability | Can someone on call see it fail and recover? | A swallowed error on a billing path |
| Repo rules | The repo's CLAUDE.md or AGENTS.md and any rule files it lists | Breaks a convention the repo wrote down |
The assayer loads its skill and lenses from the base branch, never from the branch under review, so a branch can't soften its own review.
The hallmark is a git note on one SHA. Any later commit has none, so the gate blocks it until it is reviewed again.
Loops end when the hallmark exists or the round limit is reached, never because an agent printed "done".
Several tools cover parts of this. None follows one item from its first prompt to its PR with an independent reviewer and a complete record. Here is what Hallmark borrows from each and where it goes further.
| Tool | What it is | Borrow | Hallmark goes further |
|---|---|---|---|
| Claude SquadGo · AGPL | Terminal app that runs many agents, each in its own worktree and tmux session, with a review before you push | A session list with a live preview pane; sessions that survive closing the terminal | An independent reviewer, fixed checks and a journal. Runs on Windows; Claude Squad runs on macOS and Linux. |
| Vibe Kanbanweb · community-maintained since April 2026 | Kanban board for agent tasks with a worktree per task and inline diff comments sent back to the agent | Columns by state; feedback goes straight to the agent that will act on it | Terminal-native. The reviewer is an agent, and your decision sits at "ready", not on every diff. |
| Gas TownSteve Yegge | Orchestrator for 20–30 agents. A Refinery runs a merge queue with verification gates, Beads keeps state in git, and a Witness spots stuck agents | State in git; items that outlive the sessions working on them; a watch for stuck work | Far smaller: a review line for work you start yourself, sized for one Claude plan rather than heavy API spend. |
| Ralph OrchestratorRust · MIT | Loop runner with "hats" (personas, including review) on an event bus, gates such as tests and lint, and an event history | Assayer and fixer as hats; checks before the model; the event history | It stops when the agent prints a completion signal; Hallmark stops on a hallmark check. Adds your ready step. |
| CodeRabbit CLIpaid credits | Local pre-PR review with JSON output meant for agents | Findings as structured data an agent can act on | Uses your Claude plan and your repo's rules, and binds approval to a commit. |
| githuman | A review checkpoint at the staging area, with a handoff from you back to the agent | The explicit human handoff | The handoff is one step in a recorded lifecycle rather than the whole tool. |
.git/hallmark/ on your machine and is exported only when you ask.Hallmark assumes only git and Claude Code. Everything specific to a project lives in one optional file at the repo root, and hall init writes a first version of it by looking at the repo.
The binary, the /hallmark and principal-review skills and the hooks ship as one Claude Code plugin. Hooks are installed at user level, so every repo and every session gets them.
hall initDetects the stack and proposes the gates: package.json scripts, cargo test and clippy, go test and go vet, pytest, a Makefile, or a justfile. It also finds CLAUDE.md or AGENTS.md for the repo-rules lens.
With no config, worktrees use git worktree add, shipping uses git push and gh pr create, and the review uses all seven lenses. A repo on GitLab sets the ship command to glab mr create.
# .hallmark.toml
base = "main"
worktree = "git worktree add ../{repo}-{id} -b {branch}"
ship = "git push -u origin {branch} && gh pr create --fill"
gates = [] # hall init fills these
reviewer = "claude -p"
max_rejects = 3
stuck_after = "30m"
lenses = "all"
rules = ["CLAUDE.md", "AGENTS.md"]
[budget] # no caps unless you set them
review_model = "opus"
fix_model = "sonnet"
# .hallmark.toml
worktree = "rig wt new {branch}"
ship = "rig submit --pr"
gates = ["rig test", "rig run build"]
rules = ["CLAUDE.md",
"docs/plan/README.md#leave-alone"]
Every command is a setting, so a repo can use its own tooling without Hallmark knowing about it. The reviewer and fixer commands are settings too. Claude Code comes first because its hooks and skills make the session part of the record.
The hall binary with the agent contract, setup claude, init, doctor, items, the journal, the hallmark skill, the session hooks and the gate. Everything you do is recorded from day one.
The daemon, the principal-engineer and fixer agents with their skills and verdict schema, hallmarks, pull-back and issue linking, then the Library with built-in search and the librarian.
The board, journal, findings, history, usage and live views, qmd as an optional Library search, then packaging as a plugin with cargo-dist releases for macOS, Linux and Windows.