Skip to content

Enforced mode and the commit gate

In enforced mode, a commit is held until you have answered enough of what this session taught you. Nothing else is blocked — you can still edit, run, test and push.

What “enough” means, exactly

Each session gets a gate with a required number of concepts: how many of the concepts Claude logged while building you have not already mastered, capped at max_questions_per_task. The gate opens once you have passed required × pass_threshold of them, rounded up — at the defaults, three of four. A concept counts as passed only when it was answered at grade 3 or better, and only when this session’s work is what put it on the list: a skip counts as answered but never as passed, and a concept a quiz pulled in as review debt cannot clear a bar the diff set, or the gate would be theatre.

required is frozen when the gate opens and only ever grows, so mastering concepts mid-quiz cannot shrink the obligation you already have.

The two paths a commit can take

Where you commitWhat stops itInstalled by
Inside Claude CodeA PreToolUse hook that inspects every Bash command and denies the ones that are really a git commit.The plugin, automatically.
A terminal, or any other editorA git pre-commit hook in the repository.You, with the script below.

Install both, or enforced mode only gates half the ways to commit.

Installing the git hook

Terminal window
/path/to/eklavya/scripts/install-git-hook.sh # in the repo you want gated
/path/to/eklavya/scripts/install-git-hook.sh --uninstall
  • It chains to any pre-commit hook you already have rather than replacing it — your existing one is moved to pre-commit.local and still runs.
  • --uninstall removes Eklavya’s hook and restores the previous one.
  • It only acts on repositories whose .eklavya.json sets "mode": "enforced", so installing it is safe even if you later switch to ambient.
  • It reads that file and no other — never your global ~/.eklavya/config.json. Enforcing globally gates commits made inside Claude Code, but a bare-terminal commit is only held in repositories that say so themselves. A hook installed once must not start blocking because a global setting changed elsewhere.

What a blocked commit looks like

Terminal window
Eklavya is holding this commit.
You have answered 1 of 4 concepts from this session's work.
Answering is not the same as passing: a skipped or wrong answer counts here
but not towards the gate. /eklavya:gate has the passing count.
Open Claude Code in this repo and run /eklavya:quiz to finish, then commit again.
To see what is outstanding: /eklavya:gate
To turn this off for the repo, set "mode" to "ambient" in .eklavya.json

How to get through it

  1. Run /eklavya:gate

    See how many concepts are outstanding and what score they need.

  2. Run /eklavya:quiz

    Answer honestly. Guessing does not help — the point is the gap, not the gate.

  3. Commit again

    Both paths now succeed.

The safety rules

  • It fails open. A missing jq, a missing sqlite3, a missing database or an unreadable config lets the commit through with a warning. A learning tool that bricks commits gets uninstalled.
  • No gate row, no block. If nothing was taught this session there is nothing to prove, so nothing is held. An unpassable gate teaches nothing.
  • Only opted-in repositories are gated. A repository with no .eklavya.json is never touched by the git hook.
  • Blanking cannot lock you out. A grade of 0 never passes, so a session answered entirely with “I don’t know” would leave the gate permanently unmet. Once everything else is exhausted, Eklavya re-offers the concepts it taught you, one tier lower and as a different question, until the lesson lands. Concepts you declined are not re-offered — the gate holding against a decline is the gate working. The one exception is a decline that came with an explanation anyway: a skip you were taught through was not really a skip, and treating it as one would remove your only way past the gate.