Releases: kapadias/nonna
Releases · kapadias/nonna
Release list
v2.0.0
Your AI agent says "done"; Nonna makes it prove it.
- Install it as a plugin:
/plugin marketplace add kapadias/nonna, then
/plugin install nonna@nonna. - The test gate runs out of the box: the first session records your test command, and the agent
cannot end its turn or push on a red suite. - Lite is the default: the test gate, "where's the test?", the branch and secret guards, and six
house rules.fulladds the STATUS gate and the whole harness. /nonnashows what she enforces here and switches her lite, full or off.- Measured on the plugin, one prompt for every arm (benchmark round 3, Claude Sonnet and Haiku):
with lite, 1 of 64 trap runs cut a corner, against 24 of 64 for the bare agent, for about 3 cents
more per change.
Upgrading from 1.x (Keel)
- Reinstall:
/plugin uninstall keel@keel, then/plugin marketplace add kapadias/nonnaand
/plugin install nonna@nonna. - Environment variables are now
NONNA_*(for exampleNONNA_CRITICAL_PATHS). - The plugin now starts in lite mode: the test gate, the branch and secret guards and six house
rules. For 1.x's behaviour (the constitution and the STATUS gate), run/nonna fullin a
repository,git config --global nonna.mode fullfor all of them, or set the plugin'smode
option to full. - The STATUS gate runs only in full mode, and only where
docs/STATUS.mdexists. install.shinstalls lite unless you pass--mode full. Running it again keeps the mode a
repository already has, so a 1.x copy-in install stays full.- The plugin runs your tests before the agent can say done and before a push. The first session in a
repository records the command it detects ingit config nonna.testCmd. Change it with
/nonna test '<command>', turn the gate off there with/nonna test off, or turn therun_tests
option off before Nonna meets your repositories. - The git hooks now read git config alone.
NONNA_TEST_CMDandNONNA_MODEstill steer Claude
Code's hooks, but no longer the git hooks: for your own pushes, setgit config nonna.testCmd. - The plugin no longer formats the files the agent edits. A copy-in install still does.
Added
/nonna, the user's switch (ADR-0011 §12)./nonnashows what she enforces in the repository
and where each setting comes from;/nonna lite|full|offand/nonna test '<command>'change it;
/nonna setuprecords the test command she finds, wires the git hooks and offers the rest;
/nonna uninstalltakes back only what is hers, in every worktree, and names each value. It is
the user's alone: the agent cannot invoke it, and the branch guard refuses the agent running its
scripts. While she is off, the guard still keeps her settings, and nothing else.- Monorepos: a test command per directory (ADR-0014, #29).
/nonna test --dir packages/api '<command>'setsgit config nonna.packages/api.testCmd. At the
end of a turn the Stop hook runs only the commands of the directories this session changed (new
files included), each once and inside the repository in its directory, then the repository's
command for a file in none, and every command when it cannot list the changes. They share the 240
seconds, the first red blocks with its directory named, and a directory is not run again until it,
or a shared file outside every package, changes. The pre-push hook chooses the same way over the
pushed range./nonnalists them, and/nonna uninstallremoves everynonnasubsection, which
it used to leave behind while saying it had removed them.NONNA_TEST_CMDstill replaces them
all. For every repository, a pushed merge is now tested for what it takes from each side (a clean
merge used to push with no tests), and a signer'slog.showSignatureno longer puts the
verifier's lines into what the pre-push hook reads, where they could hide a STATUS update. Nor
does a submodule bump go unread because.gitmodulesordiff.ignoreSubmodulessays to ignore
it, nor a submodule checked out behind the pushed one pass for a clean working tree, and a replace
ref no longer makes the hook read a look-alike instead of what is pushed. - Modes:
off,liteandfull, one switch per repository (ADR-0011). Every hook reads, in
order:NONNA_MODE(Claude Code's hooks only), yournonna.mode(repository, then global), the
plugin'smodeoption,nonna.defaultMode, and last what the repository carries (the hooks and
the rules: full; otherwise lite). A value nobody meant fails closed to full;offenforces
nothing and says nothing, but for her settings (/nonna, above). Nonna never writesnonna.modeherself: what she records goes in
nonna.defaultMode, below it, sogit config --global nonna.mode offreaches every repository
you have not set. The git hooks take nothing from the environment, agit -cflag or a file the
config includes, so a command cannot switch them off for itself. - Lite, the plugin's default: the test gate, "where's the test?", the branch and secret guards,
the git hooks, and six house rules (hooks/lib/lite.md, linted to 150 words and to cover the
never-list) in place of the constitution.install.sh --mode lite|full, lite by default as round 3 decided (an
install already there keeps its mode), with lite rules for the other hosts inhosts/lite/. - The plugin's test gate works out of the box, with consent. The
run_testsoption (on) is the
consent: the first session in a repository records the detected command innonna.testCmd, where
the Stop and pre-push hooks read it, and never overwrites one, an empty one included. The first
session also tells you, once, what Nonna did there: the mode, the test command and the git hooks
she added. - The test gate finds Ruby, PHP, Java and Kotlin, .NET and Elixir suites, as well as pytest, npm,
go and cargo:bundle exec rspecorbundle exec rake test,vendor/bin/pestor
vendor/bin/phpunit,./gradlew test,./mvnw testormvn test,dotnet testandmix test.
A command is named only when its runner is there (a missing one would read as a red suite and
block every push); thegradlew,mvnwandvendor/binscripts need a JVM or php too; detection
runs nothing. A row whose runner is missing is skipped and detection goes on to the rows below it,
so a repository thatpackage.json,go.modorCargo.tomlgated before is gated still (only
pytest's row keeps claiming its repository). The back ends come beforepackage.json, which in a
Rails, Laravel or Phoenix app usually serves the front end; pytest stays first, andgo.modand
Cargo.tomlkeep their places. Detection runs only while nononna.testCmdis recorded, so a
repository that has one is unaffected. A copy-in install, which detects on each run, now names the
back end's command, where its runner is there, in a repository that has both a back end and a
package.json: setnonna.testCmdto keep the old one. - "Where's the test?" When source changed and no test file did, Stop sends the agent back for a
test that fails without the change, or a plain reason why none is needed. It asks once for a set of
changes in a session. - Committed work cannot dodge the Stop gate. SessionStart records where the session began, and
Stop checks everything changed since, committed or not. - The Stop block shows what failed: the failing lines from pytest, jest, go, cargo or TAP output,
quoted as the repository's words (a test cannot speak in her voice), with any line that looks like
a secret hidden, then what to do. - Guards that travel with the plugin. The branch guard refuses force pushes (
--force,-f,
--force-with-lease, abbreviated or in a cluster), a push of every branch (:, a wildcard),
--no-verify, hook-path overrides, push config, and an agent's changes to Nonna's own settings or
git hooks, throughgit push,subtree pushor git's owngit-push, and git's plumbing pushes,
which run no hook (send-pack). It reads each
command the way the shell will run it, so quotes, escapes, brace lists, globs, capitals and a
nestedsh -cdo not hide a flag; a value computed when the command runs can. It is a speed bump:
branch protection on the server is the wall. The secret
guard refuses reads and searches (Read, Grep) of secret files, by any name that leads to one,
linted against thesettings.jsondeny-list. - Plugin git hooks survive updates, and plugin installs get
pre-committoo. The links go
through the plugin's data directory, re-pointed at the running version each session. They lead to
Nonna's own scripts, never scripts a repository ships. A dangling link of Nonna's is repaired; a
foreign hook, a hook manager and a hook that points at nothing are reported, never overwritten. - In full mode, when another enabled plugin already states the "reuse before you write" ladder, the
constitution's copy is left out (NONNA_LADDER=on|offdecides it yourself). - "Done" means the suite passes. In rounds 1–2 of the benchmark, agents said "done" on a broken
suite in 16 of 16 bare runs and most harnessed ones: nothing deterministic ran the tests. Now the Stop hook and
the pre-push hook run the project's own test command (pytest, npm, go or cargo, detected; or
NONNA_TEST_CMD) whenever code changed, and refuse on red.hooks/lib/tests.shholds it.
Detection runs at every turn end only in a copy-in install; the plugin records what it detects
once per repository, with consent (above). A green tree is not re-tested at every turn end, a Stop-time timeout does not
block, and the pre-push gate reads the pushed range from git, so a branch's first push is gated.
The push scan covers every commit the remote lacks, one diff per commit, so a key in a local-only
base commit, or one added and removed inside the push, is caught; colour, external-diff config and
non-ASCI...