Merge pull request 'policy: enforce PR-only merges to main + fix broken repl tests' (#116) from policy/pr-only-main into main

This commit is contained in:
rockachopa
2026-03-14 21:15:00 -04:00
3 changed files with 54 additions and 15 deletions

View File

@@ -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
---

View File

@@ -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

16
tox.ini
View File

@@ -52,14 +52,14 @@ commands =
pytest tests/ -q --tb=short \
--ignore=tests/e2e \
--ignore=tests/functional \
-m "not ollama and not docker and not selenium and not external_api and not skip_ci" \
-m "not ollama and not docker and not selenium and not external_api and not skip_ci and not slow" \
-n auto --dist worksteal
[testenv:integration]
description = Integration tests (marked with @pytest.mark.integration)
commands =
pytest tests/ -q --tb=short \
-m "integration and not ollama and not docker and not selenium and not external_api" \
-m "integration and not ollama and not docker and not selenium and not external_api and not slow" \
-n auto --dist worksteal
[testenv:functional]
@@ -79,7 +79,7 @@ commands =
pytest tests/ -q --tb=short \
--ignore=tests/e2e \
--ignore=tests/functional \
-m "not ollama and not docker and not selenium and not external_api" \
-m "not ollama and not docker and not selenium and not external_api and not slow" \
-n auto --dist worksteal
[testenv:ollama]
@@ -100,7 +100,7 @@ commands =
--cov-fail-under=73 \
--junitxml=reports/junit.xml \
-p no:xdist \
-m "not ollama and not docker and not selenium and not external_api and not skip_ci"
-m "not ollama and not docker and not selenium and not external_api and not skip_ci and not slow"
[testenv:coverage]
description = Full coverage report (terminal + XML)
@@ -111,7 +111,7 @@ commands =
--cov-report=xml \
--cov-fail-under=73 \
-p no:xdist \
-m "not ollama and not docker and not selenium and not external_api"
-m "not ollama and not docker and not selenium and not external_api and not slow"
[testenv:coverage-html]
description = Coverage with HTML report
@@ -122,7 +122,7 @@ commands =
--cov-report=html \
--cov-fail-under=73 \
-p no:xdist \
-m "not ollama and not docker and not selenium and not external_api"
-m "not ollama and not docker and not selenium and not external_api and not slow"
# ── Pre-push (mirrors CI exactly) ────────────────────────────────────────────
@@ -142,7 +142,7 @@ commands =
--cov-fail-under=73 \
--junitxml=reports/junit.xml \
-p no:xdist \
-m "not ollama and not docker and not selenium and not external_api and not skip_ci"
-m "not ollama and not docker and not selenium and not external_api and not skip_ci and not slow"
# ── Pre-commit (fast local gate) ────────────────────────────────────────────
@@ -156,7 +156,7 @@ commands =
pytest tests/ -q --tb=short \
--ignore=tests/e2e \
--ignore=tests/functional \
-m "not ollama and not docker and not selenium and not external_api and not skip_ci" \
-m "not ollama and not docker and not selenium and not external_api and not skip_ci and not slow" \
-n auto --dist worksteal
# ── Dev Server ───────────────────────────────────────────────────────────────