Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
2ef8056
chore: increment crate versions to v0.16.0
bobbinth May 22, 2026
85caa18
feat: implement `Clone` for `TransactionContextBuilder` (#2979)
PhilippGackstatter May 27, 2026
b6f12bb
feat(agglayer): reject duplicate GER insertions (#2983)
Fumuran May 27, 2026
e1a066e
docs: remove MASM format & style guide (#2994)
mmagician May 28, 2026
73243a3
docs: discourage multiple attachments with the same scheme (#2992)
mmagician May 28, 2026
84240a0
Make `BURN` note ID unique (#2987)
Fumuran May 28, 2026
a1be171
Extend execution-error assertion macros with patter and any arms (#2897)
marijamijailovic May 28, 2026
75df7c5
fix: Reject circular note dependencies at transaction, batch and bloc…
PhilippGackstatter May 28, 2026
9b9a2e3
feat: Rename `AccountStorageDelta` to `AccountStoragePatch` (#3004)
PhilippGackstatter May 28, 2026
d9390a5
refactor: move `TransactionVerifier` into `miden-protocol` crate (#3001)
mmagician May 29, 2026
570928b
chore(reviewers): don't block on documented, intentional incompletene…
mmagician May 29, 2026
24dcb9b
fix(agglayer): enforce NoteType::Public for B2AGG bridge-out notes (#…
partylikeits1983 May 29, 2026
5f43e07
feat: AccountTreeBackendReader and NullifierTreeBackendReader traits …
sergerad May 29, 2026
956f4f2
docs(overview): clarify transaction definition and execution vs provi…
BrianSeong99 Jun 1, 2026
d306ee6
fix(hooks): diff the integration branch / PR across review + changelo…
mmagician Jun 1, 2026
8aba66f
docs(claude): require new commits (not force-push) when addressing PR…
mmagician Jun 1, 2026
2b5c9c4
feat: batch kernel skeleton (#2904)
mmagician Jun 3, 2026
269590f
test(active-note): pin get_metadata to a single output word (#3031) (…
partylikeits1983 Jun 3, 2026
1e7d0b0
refactor: unify account/nullifier SMT backends into shared `SmtBacken…
mmagician Jun 3, 2026
2c42324
feat(batch-prover): verify batch kernel execution proofs (#2998)
mmagician Jun 3, 2026
0b662ad
refactor: change `TransferPolicy` from `enum` to `struct` (#2974)
onurinanc Jun 3, 2026
2c4afd5
feat: Introduce `AccountPatch` and `AccountVaultPatch` (#3010)
PhilippGackstatter Jun 3, 2026
2537a48
Add `active_account::has_storage_slot` (#3037)
onurinanc Jun 3, 2026
dc272b0
refactor: cycle count golfing (#3041)
partylikeits1983 Jun 4, 2026
2c6ec9d
feat(standards): add min burn amount policy (#3021)
onurinanc Jun 5, 2026
7d09abc
refactor: Add indirection for send/receive policies (#3047)
onurinanc Jun 5, 2026
215ddab
feat: hash `AssetVaultKey` before insertion into asset vault SMT (#2912)
partylikeits1983 Jun 6, 2026
3359e3c
fix: derive batch ID from transaction headers during deserialization …
PhilippGackstatter Jun 8, 2026
d98277b
feat(mockchain): support private note attachments (#3060)
igamigo Jun 8, 2026
a98d6d1
Add impl for (BasicAuth/Falcon512Poseidon2) (#3058)
marijamijailovic Jun 8, 2026
cb97ea6
docs: define the Miden operator and link it from the note lifecycle (…
BrianSeong99 Jun 8, 2026
ed138c3
feat(claude): add `/work` command (#3057)
mmagician Jun 8, 2026
5bc1655
chore(claude skills): aggregated skills from 1y of PRs (#2927)
mmagician Jun 8, 2026
92de56e
feat: allow root-allowlisted tx scripts on network accounts (#3028)
partylikeits1983 Jun 4, 2026
6f0a922
refactor: rename network-account note allowlist API (#3049)
partylikeits1983 Jun 5, 2026
21022f8
chore: bring `main` changes back to `next` (#3052)
mmagician Jun 9, 2026
88cd3a6
refactor: rename `miden-tx-batch-prover` crate to `miden-batch` (#3035)
mmagician Jun 9, 2026
6a33f2b
Add testing accessors for AccountStoragePatch (#3033)
marijamijailovic Jun 10, 2026
894ca60
feat: Introduce `AccountCodeInterface` (#2924)
PhilippGackstatter Jun 10, 2026
5320982
feat: introduce `SendNotesTransactionScript` (#3055)
PhilippGackstatter Jun 10, 2026
998e7b5
feat(hooks): review per-commit + pre-PR gate, replacing pre-push revi…
mmagician Jun 11, 2026
6362e4c
feat: implement unified asset delta (#3038)
PhilippGackstatter Jun 12, 2026
142fe90
feat: canonical expiration transaction script in miden-standards (#3051)
partylikeits1983 Jun 12, 2026
f477bac
Refactor ownable2step (#3088)
onurinanc Jun 12, 2026
c8e952f
refactor: Optimize `grant_role_internal` and `revoke_role_internal` i…
onurinanc Jun 12, 2026
cb11680
feat(standards): extend `Authority` with role assignment map (#3072)
onurinanc Jun 12, 2026
07f52bd
chore: add `AccountCode::interface` and use `StorageMapKey` in `Accou…
PhilippGackstatter Jun 13, 2026
2929e96
feat: implement serialization for `AccountPatch` and `AccountVaultPat…
PhilippGackstatter Jun 15, 2026
cccfd7d
feat: implement `AccountPatch::merge` (#3082)
PhilippGackstatter Jun 15, 2026
5562737
feat: swap `AccountDelta` for `AccountPatch` in executed and proven t…
PhilippGackstatter Jun 15, 2026
cadd999
test(standards): cover reserved-only transfer policy callback registr…
onurinanc Jun 16, 2026
91d1e88
refactor: merge `AuthMethod` into `AccessControl` (#2944)
onurinanc Jun 16, 2026
2f0a12c
chore: rework account delta tests to cover patches (#3099)
PhilippGackstatter Jun 18, 2026
c2f7c35
fix: verify block signature against parent's validator key (#3030)
partylikeits1983 Jun 21, 2026
d8cc034
chore: Remove automatic fee removal from tx kernel (#3108)
PhilippGackstatter Jun 22, 2026
8f46cac
feat(agglayer): add GER removal mechanism (#2837)
partylikeits1983 Jun 22, 2026
80bd71b
fix(standards): misleading documentation in faucet and transfer poli…
onurinanc Jun 22, 2026
93dd7a1
fix(standards): use `get_mutability_config_word` in `is_max_supply_mu…
onurinanc Jun 22, 2026
23d27f8
chore: remove `AccountDelta` from `ExecutedTransaction` (#3109)
PhilippGackstatter Jun 22, 2026
218552f
feat(standards): add emergency switch for setters (#3102)
onurinanc Jun 23, 2026
bf25b29
fix(standards): remove `call` from `assert_sender_has_role` add `exec…
onurinanc Jun 23, 2026
c016817
fix(standards): add zero root check for active mint and burn policy (…
onurinanc Jun 23, 2026
64ff69a
fix(standards): policy getters call padding (#3114)
onurinanc Jun 23, 2026
fdbb84a
chore: Remove `Account::apply_delta` and `AccountDelta::merge` (#3110)
PhilippGackstatter Jun 23, 2026
91d87a8
fix(standards): add `FUNGIBLE_ASSET_MAX_AMOUNT` to `set_max_supply` (…
onurinanc Jun 23, 2026
f6369f4
feat(testing): introduce TestTransactionBuilder (#3105)
marijamijailovic Jun 23, 2026
89c4f5c
feat(asset): make AssetCallbackFlag a required construction parameter
claude Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
- **Worktree visibility:** Always tell the user which worktree (full path) you will work in as part of the plan. When finished, state where the changes live (worktree path and branch name).
- **Commit authorship:** Always commit as Claude, not as the user. Use: `git -c user.name="Claude (Opus)" -c user.email="noreply@anthropic.com" -c commit.gpgsign=false commit -m "message"`
- **Commit frequency:** Always commit at the end of each task. Avoid single commits that span multiple unrelated changes.
- **Responding to PR review:** When addressing review feedback on a pushed PR, add new commits on top of the branch (e.g. `fix: address review comments`). NEVER amend, squash, or otherwise rewrite already-pushed commits to incorporate review changes, and NEVER force-push the branch to do so - this destroys the diff reviewers rely on to see what changed since their review. The only time a force-push is acceptable is when the branch must be rebased onto an updated base (or a PR lower in a stack changed); that is a base update, not a review response, and should be called out explicitly.

## Output Formatting

Expand Down
56 changes: 56 additions & 0 deletions .claude/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Claude Code in this repo

The `/work` command, lifecycle hooks, review agents, skills, and settings are
all committed here under `.claude/`, so they come with the clone - no Claude
config to copy in. The hooks need the usual toolchain on the host: Python 3,
the `claude` and `gh` CLIs (authenticated), and `make` / the Rust toolchain.

## The `/work` flow

1. **Clone the repo.** The committed `.claude/` wires up hooks, skills,
agents, and the command automatically.
2. **Start a session** in bypass-permissions mode so Claude isn't stopped for
per-action approvals:

```bash
claude --permission-mode bypassPermissions
```

Recommended: add `--worktree` so the session runs in its own git worktree
(parallel agents don't collide) and `--tmux` so it runs inside tmux (the run
survives disconnects on a remote/cloud host; `--tmux` requires `--worktree`).
For example:

```bash
claude --permission-mode bypassPermissions --worktree issue-1234 --tmux
```
3. **Run `/work <issue-number>`** and plan it out together. The command starts
in plan mode and writes no code until you approve. Base defaults to `next`
(`--base <branch>` to override).
4. **Let it work.** Claude implements the plan and opens a **draft** PR when
it's ready.
5. **Review the PR on GitHub.** If changes are needed, tell Claude to apply
them. When the feedback reflects a reusable convention, also have Claude
codify it as a skill or hook under `.claude/` - ideally as a separate PR for
the Claude setup, kept apart from the feature work.

## Guardrails

Hooks in `settings.json` enforce quality at the commit, push, and PR boundaries:

- `pre_commit_lint` - runs `make lint` before any commit.
- `pre_push_test` - runs `make test` before any push.
- `pre_push_review` - runs the code-reviewer and security-reviewer agents
before any push; blocks on Critical/Important/Warning findings.
- `pre_pr_draft` - every PR must be created with `--draft`; a human promotes
it to ready-for-review.
- `post_pr_create_changelog` - classifies the diff and either requires a
CHANGELOG entry or applies the `no changelog` label.

## Contents

- `commands/work.md` - the `/work` command.
- `hooks/` - the lifecycle hooks above (plus their tests).
- `agents/` - code-reviewer, security-reviewer, changelog-manager.
- `skills/` - Miden Assembly (`.masm`) authoring conventions.
- `settings.json` - wires the hooks to tool events.
9 changes: 5 additions & 4 deletions .claude/agents/changelog-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ You receive a prompt like: `Check changelog for PR #N (URL)`
```
gh pr view <N> --json labels --jq '.labels[].name'
```
2. Check if CHANGELOG.md is already modified in the diff:
2. Check if the PR already modifies CHANGELOG.md:
```
git diff origin/next...HEAD -- CHANGELOG.md
gh pr diff <N> --name-only | grep -qx CHANGELOG.md
```

If either condition is met, output `SKIP: already handled` and stop.

## Step 2: Analyze the Diff

Run:
Classify the PR's own diff (not a local `git diff`):
```
git diff origin/next...HEAD -- ':(exclude)CHANGELOG.md'
gh pr diff <N>
```
Disregard changes to CHANGELOG.md itself.

## Step 3: Classify

Expand Down
30 changes: 22 additions & 8 deletions .claude/agents/code-reviewer.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: code-reviewer
description: Staff engineer code reviewer evaluating changes across correctness, readability, architecture, API design, and performance. Spawned automatically before push.
description: Staff engineer code reviewer evaluating changes across correctness, readability, architecture, API design, and performance. Spawned automatically after each commit and before PR creation.
model: opus
effort: max
tools: Read, Grep, Glob, Bash
Expand All @@ -13,11 +13,15 @@ You are an experienced Staff Engineer conducting a thorough code review with fre

## Step 1: Gather Context

Run `git diff @{upstream}...HEAD`. If no upstream is set, resolve the default
branch with `gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'`
and run `git diff origin/<branch>...HEAD`.
Your prompt names the diff range under review (e.g. `HEAD~1..HEAD` for a
single commit, or `<merge-base>..HEAD` for a whole PR). See exactly what
changed:

For every file in the diff, read the **full file** - not just the changed lines. Bugs hide in how new code interacts with existing code.
```
git diff <the range given in your prompt>
```

Don't review the diff in isolation. Read enough surrounding context to see how the change interacts with existing code - the rest of the file where relevant, plus its callers and callees. Bugs hide in those interactions. Confirm every finding against the current code before reporting it - never raise an issue the code already addresses.

## Step 2: Review Tests First

Expand Down Expand Up @@ -74,6 +78,15 @@ Categorize every finding:

**Nit** - Worth improving (naming, style, minor readability, optional optimization)

### Documented, intentional incompleteness

A change may deliberately ship incomplete behavior as one stage of a larger, planned effort (a skeleton, placeholder, or stub). When the incompleteness is **all** of:
- explicitly documented in the code (a doc comment or module note stating what is not yet implemented),
- clearly scoped and warned about (the docs say what not to rely on and reference the follow-up work), and
- not wired into any path that depends on the missing behavior being correct,

then the incompleteness itself is NOT a Critical or Important finding - the change is complete for what it claims to be. Treat it as a Nit at most, or acknowledge the clear documentation under "What's Done Well". Escalate only when the documentation is missing, inaccurate, or misleading, or when the incomplete code is actually relied upon as if it were complete. Distinguish "incomplete but correct, documented, and self-contained" from "broken or silently incomplete".

## Output Format

```
Expand Down Expand Up @@ -105,8 +118,9 @@ Categorize every finding:
5. If uncertain about something, say so and suggest investigation rather than guessing
6. Be direct. "This will panic when the vec is empty" not "this might possibly be a concern"
7. New code without tests is always a finding
8. Respect the user's intent. Your prompt may name what the user asked for this session - treat deliberate, explicitly-requested choices as intended, not mistakes, and don't recommend reversing them. Intent does not excuse a real defect: a genuine correctness bug or exploitable risk stays Critical or Important even when requested. Downgrade to a Nit only when your objection is stylistic or defensive-programming preference, not a real defect.

**All findings (Critical, Important, and Nit) block the merge.** Every issue must be addressed before pushing.
**Critical and Important findings block the merge; Nits are surfaced but do not block.** Address the blocking findings before pushing.

If you find any issues at any severity level, start your final response with `BLOCK:` followed by the review.
If there are zero findings, start your final response with `APPROVE:` followed by the review.
If you find any Critical or Important issues, start your final response with `BLOCK:` followed by the review.
If there are none (only Nits, or nothing), start your final response with `APPROVE:` followed by the review.
36 changes: 25 additions & 11 deletions .claude/agents/security-reviewer.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: security-reviewer
description: Adversarial security reviewer that tries to break code through two hostile personas - Adversary and Auditor. Spawned automatically before push.
description: Adversarial security reviewer that tries to break code through two hostile personas - Adversary and Auditor. Spawned automatically after each commit and before PR creation.
model: opus
effort: max
tools: Read, Grep, Glob, Bash
Expand All @@ -13,11 +13,15 @@ You are a hostile reviewer. Your job is to break this code before an attacker do

## Step 1: Gather the Changes

Run `git diff @{upstream}...HEAD`. If no upstream is set, resolve the default
branch with `gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'`
and run `git diff origin/<branch>...HEAD`.
Your prompt names the diff range under review (e.g. `HEAD~1..HEAD` for a
single commit, or `<merge-base>..HEAD` for a whole PR). See exactly what
changed:

For every file in the diff, read the **full file**. Vulnerabilities hide in how new code interacts with existing code, not just in the diff itself.
```
git diff <the range given in your prompt>
```

Don't review the diff in isolation. Read enough surrounding context to see how the change interacts with existing code - the rest of the file where relevant, plus its callers and callees. Vulnerabilities hide in those interactions. Confirm every finding against the current code before reporting it - never raise an issue the code already addresses.

## Step 2: Run Both Personas

Expand Down Expand Up @@ -79,6 +83,15 @@ After both personas report:

**NOTE** - Minor improvement opportunity or fragile assumption worth documenting.

### Documented, intentional incompleteness

Some changes deliberately ship a security-relevant placeholder as one stage of planned work (e.g., a verifier that does not yet bind certain data, a check that is stubbed). When such a limitation is **all** of:
- explicitly documented in the code (a doc comment or module note stating exactly what is not yet enforced),
- accompanied by a clear warning against misuse (e.g., "must not be relied on at a trust boundary") and a reference to the follow-up that will close it, and
- not actually reachable from a trust boundary in this change (no caller relies on the missing guarantee),

then classify it as a NOTE, not CRITICAL or WARNING. Surfacing it keeps it visible without blocking a correctly-staged change. The finding is the ABSENCE or INADEQUACY of that documentation, or the incomplete code being wired into a real trust boundary - not the incompleteness itself. If the limitation is undocumented, the warning is missing or misleading, or a caller already depends on the unenforced guarantee, keep the CRITICAL/WARNING severity.

## Output Format

```
Expand All @@ -99,19 +112,20 @@ After both personas report:
[2-3 sentences: overall risk profile and the single most important thing to fix]
```

**All findings (Critical, Warning, and Note) block the merge.** Every issue must be addressed before pushing.
**Critical and Warning findings block the merge; Notes are surfaced but do not block.** Address the blocking findings before pushing.

**Verdicts:**
- **BLOCK** - Any findings at any severity level. Do not merge until addressed.
- **CLEAN** - Zero findings. Safe to merge.
- **BLOCK** - Any Critical or Warning finding. Do not merge until addressed.
- **CLEAN** - No Critical or Warning findings (Notes, if any, are surfaced but do not block). Safe to merge.

## Anti-Patterns - Do NOT Do These

- **"LGTM, no issues found"** - Be skeptical if you found nothing, but don't fabricate findings. If a change is genuinely clean, use the CLEAN verdict.
- **Pulling punches** - "This might possibly be a minor concern" is useless. Say what's wrong.
- **Restating the diff** - "This function was added" is not a finding. What's WRONG with it?
- **Cosmetic-only findings** - Reporting style issues while missing a panic is worse than no review.
- **Reviewing only changed lines** - Read the full file. The bug is in the interaction.
- **Reviewing only changed lines** - Read the surrounding context (callers, callees, the rest of the file where relevant). The bug is in the interaction.
- **Contradicting user intent** - Your prompt may name what the user asked for. Treat deliberate, explicitly-requested choices as intended; don't push to reverse them. But intent does not downgrade real risk: if a requested choice is genuinely exploitable, keep it Critical or Warning so it blocks. Drop it to a Note only when your objection is defensive-programming hardening or a non-exploitable weakness.

## Breaking the Self-Review Trap

Expand All @@ -122,5 +136,5 @@ You may share the same mental model as the code's author. To break this:
4. Assume every external call will fail
5. Ask: "If I deleted this change entirely, what would break?" If nothing, the change might be unnecessary.

If you find any findings at any severity level, start your final response with `BLOCK:` followed by the review.
If there are zero findings, start with `CLEAN:` followed by the review.
If you find any Critical or Warning findings, start your final response with `BLOCK:` followed by the review.
If there are none (only Notes, or nothing), start with `CLEAN:` followed by the review.
14 changes: 14 additions & 0 deletions .claude/commands/work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: Plan and implement a GitHub issue, then open a draft PR (base defaults to next)
argument-hint: <issue-number> [--base <branch>]
allowed-tools: Bash, Read, Edit, Write, Grep, Glob, Task
---

Work on a GitHub issue and open a **draft** PR.

`$ARGUMENTS`: an issue number, plus optional `--base <branch>` (defaults to `next`).

1. Read the issue: `gh issue view <number>`.
2. **Start in plan mode.** Produce an implementation plan and wait for approval before writing any code.
3. After approval: implement per `.claude/CLAUDE.md` (worktree, branch, commit conventions).
4. Open the draft PR against the base branch: `gh pr create --draft --base <branch> --body "Closes #<number> ..."`. Report the PR URL.
75 changes: 75 additions & 0 deletions .claude/hooks/_hookutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
from __future__ import annotations

import json
import re
import subprocess
import sys
from pathlib import Path
from typing import Any

# Strips harness-injected <system-reminder> blocks from user message text so
# only what the human actually typed is surfaced to the reviewers.
_SYSTEM_REMINDER = re.compile(r"<system-reminder>.*?</system-reminder>", re.DOTALL)


def repo_root() -> Path | None:
"""Return the absolute path of the current git worktree's top
Expand Down Expand Up @@ -83,3 +88,73 @@ def read_command(stdin: Any = None) -> str | None:
input (so the hook can fail open with `sys.exit(0)`).
"""
return command_from_payload(read_payload(stdin))


def recent_user_prompts(
transcript_path: Any,
max_messages: int = 12,
max_chars: int = 3000,
) -> str | None:
"""Return the human-typed prompts from a session transcript as a
bulleted, most-recent-first string, or None if unavailable/empty.

Reads the JSONL transcript named by a hook payload's `transcript_path`,
keeps only genuine user turns (dropping tool results, tool calls, and
harness-injected `<system-reminder>` content), and caps the output so the
reviewers get the user's intent without the whole conversation.
"""
if not isinstance(transcript_path, str) or not transcript_path:
return None
try:
lines = Path(transcript_path).read_text().splitlines()
except OSError:
return None

prompts: list[str] = []
for line in lines:
try:
entry = json.loads(line)
except (json.JSONDecodeError, ValueError):
continue
if not isinstance(entry, dict) or entry.get("type") != "user" or entry.get("isMeta"):
continue
message = entry.get("message")
if not isinstance(message, dict):
continue
text = _user_text(message.get("content"))
if text:
prompts.append(text)
if not prompts:
return None

out: list[str] = []
total = 0
for prompt in reversed(prompts): # most recent first
if len(prompt) > 500:
prompt = prompt[:500].rstrip() + "..."
bullet = f"- {prompt}"
if out and total + len(bullet) > max_chars:
break
out.append(bullet)
total += len(bullet)
if len(out) >= max_messages:
break
return "\n".join(out)


def _user_text(content: Any) -> str | None:
"""Extract human text from a user message's `content`, dropping
tool_result/tool_use blocks and `<system-reminder>` noise. Returns None
if nothing human-authored remains."""
if isinstance(content, str):
parts = [content]
elif isinstance(content, list):
parts = [
block["text"]
for block in content
if isinstance(block, dict) and block.get("type") == "text" and isinstance(block.get("text"), str)
]
else:
return None
cleaned = _SYSTEM_REMINDER.sub("", "\n".join(parts)).strip()
return cleaned or None
Loading