Skip to content

Releases: kapadias/nonna

Release list

v2.0.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 01:06
d77314f

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. full adds the STATUS gate and the whole harness.
  • /nonna shows 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/nonna and
    /plugin install nonna@nonna.
  • Environment variables are now NONNA_* (for example NONNA_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 full in a
    repository, git config --global nonna.mode full for all of them, or set the plugin's mode
    option to full.
  • The STATUS gate runs only in full mode, and only where docs/STATUS.md exists.
  • install.sh installs 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 in git config nonna.testCmd. Change it with
    /nonna test '<command>', turn the gate off there with /nonna test off, or turn the run_tests
    option off before Nonna meets your repositories.
  • The git hooks now read git config alone. NONNA_TEST_CMD and NONNA_MODE still steer Claude
    Code's hooks, but no longer the git hooks: for your own pushes, set git 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). /nonna shows what she enforces in the repository
    and where each setting comes from; /nonna lite|full|off and /nonna test '<command>' change it;
    /nonna setup records the test command she finds, wires the git hooks and offers the rest;
    /nonna uninstall takes 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>' sets git 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. /nonna lists them, and /nonna uninstall removes every nonna subsection, which
    it used to leave behind while saying it had removed them. NONNA_TEST_CMD still 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's log.showSignature no 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 .gitmodules or diff.ignoreSubmodules says 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, lite and full, one switch per repository (ADR-0011). Every hook reads, in
    order: NONNA_MODE (Claude Code's hooks only), your nonna.mode (repository, then global), the
    plugin's mode option, nonna.defaultMode, and last what the repository carries (the hooks and
    the rules: full; otherwise lite). A value nobody meant fails closed to full; off enforces
    nothing and says nothing, but for her settings (/nonna, above). Nonna never writes nonna.mode herself: what she records goes in
    nonna.defaultMode, below it, so git config --global nonna.mode off reaches every repository
    you have not set. The git hooks take nothing from the environment, a git -c flag 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 in hosts/lite/.
  • The plugin's test gate works out of the box, with consent. The run_tests option (on) is the
    consent: the first session in a repository records the detected command in nonna.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 rspec or bundle exec rake test, vendor/bin/pest or
    vendor/bin/phpunit, ./gradlew test, ./mvnw test or mvn test, dotnet test and mix test.
    A command is named only when its runner is there (a missing one would read as a red suite and
    block every push); the gradlew, mvnw and vendor/bin scripts 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 that package.json, go.mod or Cargo.toml gated before is gated still (only
    pytest's row keeps claiming its repository). The back ends come before package.json, which in a
    Rails, Laravel or Phoenix app usually serves the front end; pytest stays first, and go.mod and
    Cargo.toml keep their places. Detection runs only while no nonna.testCmd is 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: set nonna.testCmd to 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, through git push, subtree push or git's own git-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
    nested sh -c do 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 the settings.json deny-list.
  • Plugin git hooks survive updates, and plugin installs get pre-commit too. 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|off decides 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.sh holds 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...
Read more