fix(opencode): reject Rust src/tests/ paths as a wrong task spec

A workflow run on a Bevy/Rust project produced the test-file path
`src/tests/test_<feature>.rs`, which @test correctly flagged as
contradictory: it isn't a valid Rust test location (would require
declaring `mod tests;` in production source, which @test cannot do)
yet the file-gate glob `**/tests/**/*.rs` accidentally matched it.

Phase 5 now gives language-aware Test File guidance: Python uses
colocated or top-level `tests/`, Rust uses crate-level `tests/<feature>.rs`,
and Rust unit-only tasks are routed to NOT_TESTABLE for @make to
handle inline. Phase 6's file gate gains an explicit anti-pattern
clause discarding any new file under `src/` even when the glob matches.

@test's own File Constraint mirrors the anti-pattern so the agent
rejects the bad path with BLOCKED before the orchestrator's gate
even runs — defense in depth on both sides of the dispatch boundary.
This commit is contained in:
Harald Hoyer 2026-05-06 18:31:14 +02:00
parent e2e35acdae
commit d5d90d8b9f
2 changed files with 22 additions and 3 deletions

View file

@ -119,6 +119,9 @@ Rust (integration tests only — see "Rust unit tests" below):
- `**/test_data/**`
- `**/test_fixtures/**`
**Anti-patterns — refuse the path even if the glob above matches:**
- Anything under `src/` (e.g. `src/tests/foo.rs`, `src/**/tests/...`). `src/tests/` is a regular module under `src/`; it would require declaring `mod tests;` in production code (`lib.rs` / `main.rs`) and creating `mod.rs`, which you cannot do. If the caller asks for such a path, treat it as a wrong task spec: return `BLOCKED` with a note that the path is not a valid Rust test location, suggesting `tests/<feature>.rs` (or `NOT_TESTABLE: Rust unit-only` if the test really needs to be in-source).
**You may NOT modify production/source code under any circumstances.**
### Rust unit tests

View file

@ -101,7 +101,20 @@ Break the approved plan into discrete tasks for `@make`. Each task needs:
| **Acceptance Criteria** | Specific, testable criteria (checkbox format) |
| **Code Context** | Actual code snippets from the codebase, not just file paths |
| **Files to Modify** | Explicit list, mark new files with "(create)" |
| **Test File** | Path for test file (colocated pattern), e.g., `<module>/tests/test_<feature>.py (create)` |
| **Test File** | Path for test file. **Pick the pattern that matches the project's language** — see "Test File Path by Language" below. |
### Test File Path by Language
The test file path must follow the language's actual test layout. **Do not invent paths that look colocated but aren't valid for the language** (e.g. `src/tests/test_<feature>.rs` is *not* a Rust test location — it's a regular `src/` submodule).
- **Python**
- Colocated: `<module>/tests/test_<feature>.py (create)`
- Top-level: `tests/test_<feature>.py (create)`
- **Rust**
- Crate-level integration tests: `tests/<feature>.rs (create)` (or, in a workspace, `<crate>/tests/<feature>.rs`)
- **Unit-test-only tasks (in-source `#[cfg(test)] mod tests`):** mark the task as `NOT_TESTABLE` with reason `Rust unit-only``@test` cannot write inside production source. `@make` writes those inline as part of its production change.
- **Polyglot Nix flake**
- Match the host language of the code under change (Python or Rust rules above), wrapping commands in `nix develop -c …` per the agents' devshell rule.
Include **Integration Contracts** when a task adds/changes function signatures, APIs, config keys, or has dependencies on other tasks.
@ -131,9 +144,12 @@ git diff --name-only | comm -23 - /tmp/pre_test_baseline.txt > /tmp/test_new_fil
```
All new files must match the project's test patterns:
- Python: `**/test_*.py`, `**/*_test.py`, `**/conftest.py` (new only), `**/test_data/**`, `**/test_fixtures/**`
- Rust: `tests/**/*.rs`, `**/tests/**/*.rs`, `**/test_data/**`, `**/test_fixtures/**`
- Rust: `tests/**/*.rs`, `**/tests/**/*.rs` (workspace-style `<crate>/tests/...`), `**/test_data/**`, `**/test_fixtures/**`
If any non-matching file appears: discard `@test` output, report violation.
**Anti-patterns — discard the output even if the glob matches:**
- Anything under `src/` for Rust (e.g. `src/tests/foo.rs`, `src/**/tests/...`). `src/tests/` is a regular module path under `src/`, not a Rust test location, and `@test` cannot wire it up via `mod` declarations in production source. Such paths indicate the task spec gave a wrong test path — escalate, don't accept the file.
If any non-matching file appears, or any anti-pattern matches: discard `@test` output, report violation.
**Decision table — handling `@test` results:**