diff --git a/AGENTS.md b/AGENTS.md index 93c7451..543ca2f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,12 +21,51 @@ Read [`CLAUDE.md`](CLAUDE.md) for architecture patterns and conventions. ## Non-Negotiable Rules -1. **Tests must stay green.** Run `make test` before committing. -2. **No cloud dependencies.** All AI computation runs on localhost. -3. **No new top-level files without purpose.** Don't litter the root directory. -4. **Follow existing patterns** — singletons, graceful degradation, pydantic-settings. -5. **Security defaults:** Never hard-code secrets. -6. **XSS prevention:** Never use `innerHTML` with untrusted content. +1. **Tests must stay green.** Run `python3 -m pytest tests/ -x -q` before committing. +2. **No direct pushes to main.** Branch protection is enforced on Gitea. All changes + reach main through a Pull Request — no exceptions. Push your feature branch, + open a PR, verify tests pass, then merge. Direct `git push origin main` will be + rejected by the server. +3. **No cloud dependencies.** All AI computation runs on localhost. +4. **No new top-level files without purpose.** Don't litter the root directory. +5. **Follow existing patterns** — singletons, graceful degradation, pydantic-settings. +6. **Security defaults:** Never hard-code secrets. +7. **XSS prevention:** Never use `innerHTML` with untrusted content. + +--- + +## Merge Policy (PR-Only) + +**Gitea branch protection is active on `main`.** This is not a suggestion. + +### The Rule +Every commit to `main` must arrive via a merged Pull Request. No agent, no human, +no orchestrator pushes directly to main. + +### The Workflow +``` +1. Create a feature branch: git checkout -b fix/my-thing +2. Make changes, commit locally +3. Run tests: python3 -m pytest tests/ -x -q +4. Push the branch: git push --no-verify origin fix/my-thing +5. Create PR via Gitea API or UI +6. Verify tests pass (orchestrator checks this) +7. Merge PR via API or UI +``` + +### Why This Exists +On 2026-03-14, Kimi Agent pushed `bbbbdcd` directly to main — a commit titled +"fix: remove unused variable in repl test" that removed `result =` from 7 test +functions while leaving `assert result.exit_code` on the next line. Every test +broke with `NameError`. No PR, no test run, no review. The breakage propagated +to all active worktrees. + +### Orchestrator Responsibilities +The Hermes loop orchestrator must: +- Run `pytest -x -q` in each worktree BEFORE committing +- Never push to main directly — always push a feature branch + PR +- Verify test results before merging any PR +- If tests fail, fix or reject — never merge red --- diff --git a/tests/timmy/test_cli.py b/tests/timmy/test_cli.py index 83c5240..f069048 100644 --- a/tests/timmy/test_cli.py +++ b/tests/timmy/test_cli.py @@ -282,7 +282,7 @@ def test_repl_skips_empty_input(): patch("timmy.session.chat") as mock_chat, ): mock_chat.return_value = "Response" - runner.invoke(app, ["repl"]) + result = runner.invoke(app, ["repl"]) # chat should only be called once (for "hello"), empty lines are skipped, exit breaks assert mock_chat.call_count == 1