Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/ISSUE_TEMPLATE/break-the-ratchet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Break the Ratchet submission
description: Submit a diff you believe genuinely weakens a gate without the ratchet flagging it
labels: ["break-the-ratchet", "community"]
body:
- type: markdown
attributes:
value: |
Read [BREAK-THE-RATCHET.md](../../BREAK-THE-RATCHET.md) before filing. The judge never
executes your diff; it runs the ratchet against it as text, and a maintainer reviews the
claim by hand.

Run `npx modonome break-the-ratchet path/to/your-submission/` locally first and paste its
output below.
- type: input
id: title
attributes:
label: One-line description
placeholder: "Expected-value drift in a currency-conversion assertion"
validations:
required: true
- type: dropdown
id: category
attributes:
label: Category
options:
- assertion-removal
- skip-injection
- vacuous-assertion
- coverage-lowering
- type-escape
- assertion-strength-downgrade
- homoglyph-disguise
- expected-value-drift
- cross-file-migration
- novel
validations:
required: true
- type: textarea
id: claim
attributes:
label: What does the diff do, mechanically?
validations:
required: true
- type: textarea
id: why
attributes:
label: Why is this a real weakening?
description: What does the test stop proving? Why would a maintainer merging this be worse off?
validations:
required: true
- type: textarea
id: patch
attributes:
label: The diff (submission.patch content)
description: Paste the unified diff. This is read as text only, never applied or executed.
render: diff
validations:
required: true
- type: textarea
id: judge-output
attributes:
label: Local judge output
description: Output of `npx modonome break-the-ratchet path/to/your-submission/`
render: text
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
options:
- label: I ran the judge locally and it reported candidate-break or false-positive-candidate
required: true
- label: This represents a realistic attack an autonomous agent could plausibly attempt, not a contrived edge case
required: true
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ __pycache__/
CLAUDE.md
runs/

# examples/demo-app/runs/ holds intentionally committed, verbatim evidence captures
# (dry-run output, ratchet rejections, Gauntlet scores) that back README and
# WALKTHROUGH.md claims: un-ignore that one path from the bare `runs/` rule above,
# which otherwise silently drops it from `git add -A` with no warning. Do not widen
# this to other `runs/` directories; agent-runtime scratch state stays ignored.
!examples/demo-app/runs/
!examples/demo-app/runs/**

# Tripwires ships a real .claude/settings.json under templates/: un-ignore that one
# tracked template (never the root-level, session-local .claude/ above).
!templates/.claude/
Expand Down
4 changes: 2 additions & 2 deletions .modonome/policy-attestation.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"file": "README.md",
"section": "Development practice",
"found": true,
"sha256": "sha256:f36a5919bf1a05328bef6a0c863a3dff373d695aa54adb8d43ed47ae04d03223"
"sha256": "sha256:a19f2bf634e8ca46d8143daecac418cbccf5d9ba555dee40f448a2ec97e36a34"
},
{
"file": "AGENTS.md",
Expand Down Expand Up @@ -111,5 +111,5 @@
"autonomy_enabled": false,
"dry_run": true
},
"content_digest": "sha256:c98af1b5aeebb3e187cc2969d43020abca147acd139bbd4e11d8884ef4cbe53b"
"content_digest": "sha256:5e2618aaaaafedc776aa1b55bd577e9169edf3c98cc57c5848a9d5342e645487"
}
2 changes: 1 addition & 1 deletion .modonome/snapshot/badge.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"color":"blue","label":"snapshot","message":"972 files, 131084 tok","schemaVersion":1}
{"color":"blue","label":"snapshot","message":"1021 files, 135457 tok","schemaVersion":1}
2 changes: 1 addition & 1 deletion .modonome/snapshot/map.json

Large diffs are not rendered by default.

253 changes: 163 additions & 90 deletions .modonome/snapshot/map.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .modonome/snapshot/signature.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"commands":{"build":"","lint":"","test":"npm test"},"entrypoints":["bin/modonome.mjs"],"generated_at":"2026-07-08T02:22:46.060Z","generated_for":"modonome","governance":{"armed":false,"autonomy_enabled":false,"dry_run":true},"instructions":["AGENTS.md","CODEX.md","CONTRIBUTING.md","README.md"],"language_mix":{"(none)":11,"css":50,"diff":15,"html":9,"js":19,"json":134,"jsonl":7,"md":124,"mjs":338,"patch":37,"png":5,"py":4,"sh":1,"svg":1,"tf":4,"toml":2,"ts":61,"tsx":98,"txt":10,"webmanifest":1,"webp":1,"woff2":9,"xml":1,"yaml":9,"yml":21},"merkle_root":"sha256:5c8b2b06867da2c07e8766db14a5a9901ac6dc781697cc4f2d203c8211cb42e3","preamble":"Modonome snapshot. Read this before reading the repo. Tier 0 (signature.json) is the fingerprint: if merkle_root matches your last read, nothing changed. Tier 1 (map.json / map.md) lists modules, public API signatures, import edges, and attention ranking. Cite anchors (F: for files, S: for symbols); each resolves to a path and line so you can act without re-reading the whole repo.","protected_paths":[".github",".github/CODEOWNERS","package-lock.json"],"schema_version":1,"size":{"bytes":3582195,"files":972},"snapshot_version":24,"stack":{"name":"Node or TypeScript","pm":"npm"},"tier_hashes":{"map":"sha256:cf9184050ba8b6a35bb1f3e9061bc3df7523ac20ad37268cac46732bfecbfd5d","map_md":"sha256:16a8db3f8ce2a1909ee0c5a1373ac796d806585125f0e695ab2df613799489a1"}}
{"commands":{"build":"","lint":"","test":"npm test"},"entrypoints":["bin/modonome.mjs"],"generated_at":"2026-07-08T14:47:54.704Z","generated_for":"modonome","governance":{"armed":false,"autonomy_enabled":false,"dry_run":true},"instructions":["AGENTS.md","CODEX.md","CONTRIBUTING.md","README.md"],"language_mix":{"(none)":11,"css":50,"diff":18,"html":9,"js":19,"json":138,"jsonl":8,"md":139,"mjs":351,"patch":40,"png":5,"py":4,"sh":1,"svg":2,"tf":4,"toml":2,"ts":61,"tsx":98,"txt":18,"webmanifest":1,"webp":1,"woff2":9,"xml":1,"yaml":9,"yml":22},"merkle_root":"sha256:845e8b35b5de64b4c920d5bbdcf431e8b5af0df5190253d4a6c0874f1522c894","preamble":"Modonome snapshot. Read this before reading the repo. Tier 0 (signature.json) is the fingerprint: if merkle_root matches your last read, nothing changed. Tier 1 (map.json / map.md) lists modules, public API signatures, import edges, and attention ranking. Cite anchors (F: for files, S: for symbols); each resolves to a path and line so you can act without re-reading the whole repo.","protected_paths":[".github",".github/CODEOWNERS","package-lock.json"],"schema_version":1,"size":{"bytes":3756020,"files":1021},"snapshot_version":44,"stack":{"name":"Node or TypeScript","pm":"npm"},"tier_hashes":{"map":"sha256:1dd668eba3b8ddf5d04692f32de055aefd59bdff73b00aac37dd9b7cbf01aca2","map_md":"sha256:8229958a1277b8c4a9c1e928e1645231eb3276a70b9354fd7c8f6bedc5c82874"}}
32 changes: 31 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,36 @@ flowchart TB
| Agent harness (agent scope) | The engine loop: adopt, sweep, make, check, gate | Each harness turn while a run is active | The host's ordinary surfaces: branches, pull requests, and `.modonome/` state |
| CI (enforcement scope) | Ratchet, validators, drift and style guards | On every pull request and on push to the default branch | Read-only judgment. It blocks merges and writes no application code |

## Host vs. self: the two governance targets

Modonome governs two different kinds of target with the same mechanism, and the distinction
matters for what evidence exists and what "armed" means in each case.

- **Host mode**: Modonome adopts a repository that is not itself. This is the product: any
team's codebase, with its own CI, CODEOWNERS, and conventions, governed the way
[QUICKSTART.md](QUICKSTART.md) and the [README](README.md) describe. `examples/demo-app/`
is the committed, publicly reproducible instance of this: real dry-run and gate-integrity
evidence, run against a target that is not modonome's own repo.
- **Self-governance**: Modonome applies its own rules to its own repository. `.modonome/` at
the repo root is the tool's own state, `self-govern.yml` runs modonome against itself on a
schedule, and `check-self-application.mjs` verifies the tool actually eats its own dogfood
rather than exempting itself. This is real and continuously exercised, but it is one
instance, not a second host: no public evidence yet demonstrates modonome governing a
third-party repository it does not maintain (see the alpha-limitations table in the
[README](README.md#the-loop-armed-mode)).

The [control panel](apps/control-panel/README.md) makes this duality concrete in its write
model: pointing the panel at a connected host repo and writing to it requires only
`MODONOME_PANEL_WRITE=1`, "evolve it freely." Pointing it at modonome's *own* `.modonome/`
requires that plus the caller's git identity resolving to a CODEOWNERS-listed owner of
`config.yaml`, enforced on the resolved filesystem path so a client cannot relabel a
self-governance write as a host write to dodge the gate. See
[docs/control-panel-modes.md](docs/control-panel-modes.md) for the full model.

Neither mode changes what "armed" means: `MODONOME_ARMED` and `autonomy_enabled` gate the
loop identically whether the target is a host repo or modonome itself, and a config file
alone can never arm either one (see "Why it is safe to run" in the README).

## Prerequisites

Modonome degrades gracefully. The read-only CLI and the CI enforcement need only Node.js.
Expand Down Expand Up @@ -140,7 +170,7 @@ default until an owner arms it.
at Checkpoint 1: it vets a proposal before it becomes a work item, so `queue.mjs` can gate
the backlog the way the pull request gates `main`. The crew itself (the roster, what each
agent is for, and how each is configured on a model, runner, schedule, and trigger) is
documented in [docs/agent-org.md](docs/agent-org.md), with the structure decision in
documented in [docs/agents.md](docs/agents.md), with the structure decision in
[docs/adr/ADR-044-agent-org-structure.md](docs/adr/ADR-044-agent-org-structure.md).
- The snapshot utility (`scripts/snapshot.mjs` plus `scripts/lib/snapshot-*.mjs` and
`scripts/lib/lang-adapters/`). A dependency-free pipeline (walk, Merkle hash, per-file
Expand Down
79 changes: 79 additions & 0 deletions BREAK-THE-RATCHET.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Break the Ratchet

The gate integrity check (`scripts/guard-ratchet.mjs`) is a deterministic, line-based diff
analyzer. The README says plainly what that means it can and cannot see: [What it catches, and
what it cannot](README.md#what-it-catches-and-what-it-cannot). This is the other half of that
honesty: an open invitation to prove the boundary wrong, or to prove it right.

**If you can construct a diff that represents a genuine gate weakening and the ratchet does not
flag it, we want it.** A confirmed break becomes a named, credited entry below and, once fixed, a
new AgentProof scenario with your name on it.

## The rule that keeps this safe to run

**Submissions are never executed.** `npx modonome break-the-ratchet <your-submission-dir>` reads
your `.patch` file as plain text and hands it to the ratchet's own `--diff` mode, which is
itself a pure text analyzer: it parses a diff into added and removed lines and pattern-matches
them. It never runs `git apply`, never writes your diff's content into a working tree, and never
executes anything a submission contains. That is the whole safety model, and it is not
negotiable: a "bring your own agent" style challenge that ran arbitrary submitted code would be a
security hole shaped exactly like the thing this project exists to prevent.

Because the judge never executes your diff, it cannot itself decide whether your claim is true.
It mechanically confirms one fact (did the ratchet flag it, yes or no) and presents your
declared claim for a maintainer to review. See the seeded example below for what a genuine,
already-confirmed candidate looks like.

## How to submit

1. Create a directory: `submission.patch` (a real unified diff, same format as
`fixtures/ratchet-diffs/gaming/*.diff`) plus `declaration.json` describing the claim
(schema: `schemas/break-the-ratchet-submission.schema.json`):

```json
{
"title": "One-line description",
"category": "expected-value-drift",
"claim": "What the diff does, mechanically.",
"whyRealWeakening": "Why a maintainer merging this would be worse off. Be concrete: what does the test stop proving?",
"expectedRatchetVerdict": "should-block"
}
```

`category` is one of the ratchet's known structural categories (`assertion-removal`,
`skip-injection`, `vacuous-assertion`, `coverage-lowering`, `type-escape`,
`assertion-strength-downgrade`, `homoglyph-disguise`) or one of the semantic categories the
README already names as out of scope (`expected-value-drift`, `cross-file-migration`), or
`novel` if it is neither.

2. Run the judge locally and read its verdict:

```bash
npx modonome break-the-ratchet path/to/your-submission/
```

3. Open an issue using the "Break the Ratchet" template with your `declaration.json` and
`submission.patch` attached. A maintainer reviews `candidate-break` verdicts by hand: does the
diff genuinely weaken a gate the way the claim describes, in a way a reasonable reviewer could
miss?

## What happens to a confirmed break

- It is credited here, in the hall of fame, with your name (or handle) and the date.
- If the underlying gap is fixable within the ratchet's structural, false-positive-safe design
(see `docs/adr/ADR-045-scope-focus.md`'s constraint: a fix may never introduce a new false
negative), it gets fixed and the fixture becomes a permanent regression test.
- If it is a genuinely semantic gap the deterministic ratchet cannot close without becoming
something other than a deterministic line-based analyzer (most `expected-value-drift` and
`cross-file-migration` cases), it is documented as a confirmed, permanent limitation, and it
strengthens the case for the independent checker's role rather than the ratchet's.

## Hall of fame

| Date | Submitter | Category | Verdict | Outcome |
|---|---|---|---|---|
| 2026-07-08 | modonome project (seed example) | expected-value-drift | Confirmed | Documented as a permanent limitation; see [challenge/examples/expected-value-drift/](challenge/examples/expected-value-drift/). The ratchet does not, and structurally cannot, see that `assert.equal(result.total, 95)` becoming `assert.equal(result.total, 100)` moved the expected value to match a bug rather than fixing one. This is why the independent checker rubric (`prompts/roles/checker.txt`) explicitly asks the reviewer to check for expected-value drift. |

The seed entry above is not a community submission; it starts the ledger honestly with the
ratchet's own documented, already-known limitation, rather than launching with an empty table
that implies no one has looked yet.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ or CVE identifier where one exists.

## Unreleased

**Highlights since the published `0.1.0-alpha` (npm, 2026-06-27; `next` dist-tag tracks
these):** AgentProof expanded from 16 to 25 normative scenarios plus 10 extended
(HARDENED); the Gauntlet gate-integrity replay with a shareable score line; multi-language
gate coverage hardening; the repo snapshot utility (Merkle-verified, tiered); governed
remediation phases 1-4 (near-miss widener, metadata-only history remediator, policy packs);
the researcher, envisioner, and market-researcher crew roles (configured, off the default
sequence); runtime model fallback chains per role; two governed checkpoints around the
maker/checker loop; deterministic gate-rejection evidence on the demo app; and a
documentation truth pass (claims reconciled to committed evidence, self-graded benchmark
framing, honest scope labels). Full detail below, newest first.

### Wire the researcher role into the agent loop (WI-042)

- Added `prompts/roles/researcher.txt`: reads the repo's own durable state, drafts one
Expand Down
7 changes: 4 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ a change pass; add tests instead.

## How we respond

We triage new issues and pull requests within 5 business days, and label them so you know
where they stand. Security reports follow the faster timeline in [SECURITY.md](SECURITY.md)
(acknowledged within 7 days). If a thread goes quiet, a polite nudge is welcome.
There is no guaranteed response time on issues or pull requests; see [SUPPORT.md](SUPPORT.md)
for the honest version of what to expect and how things get triaged. Security reports follow
the faster, real timeline in [SECURITY.md](SECURITY.md) (acknowledged within 7 days). If a
thread goes quiet, a polite nudge is welcome.

## House style

Expand Down
Loading
Loading