feat(opencode): mandate stub-first @make pre-pass for Rust integration TDD
Rust integration tests live in a separate test crate that imports from lib.rs, so any test referencing not-yet-existing public API can only RED at build time. The build error masks assertion diagnostics and makes the RED state opaque — no stack trace, no left/right values. For Rust tasks whose @test step writes an integration test against public API that does not yet exist, the orchestrator now dispatches a stub-first @make pass before @test runs: 1. @make adds the planned public API as todo!()-bodied stubs in lib.rs and any new src/<module>.rs. Signatures lifted verbatim from the Phase 5 task spec. Acceptance criterion is cargo check only — no test command runs. 2. @test writes the integration test, which now compiles and panics at todo!() with a stack trace — a clean MISSING_BEHAVIOR RED. 3. Phase 7 dispatches @make again to replace the todo!() bodies with real implementations. Two atomic commits per task: scaffold then implement. Phase 5's Rust test-path guidance now flags the two-dispatch requirement up front. test.md's Rust failure-classification hints recognize todo!() / unimplemented!() panics as MISSING_BEHAVIOR with a pointer to the workflow's stub-first section.
This commit is contained in:
parent
832306c817
commit
5b5c59aa84
2 changed files with 24 additions and 1 deletions
|
|
@ -200,6 +200,7 @@ After running tests, classify each failure:
|
|||
**Mapping hints (Rust):**
|
||||
- `error[E0432]: unresolved import` / `error[E0425]: cannot find function/value` for the symbol under test → `MISSING_BEHAVIOR`
|
||||
- `error[E0599]: no method named ...` on a real but incomplete type → `MISSING_BEHAVIOR`
|
||||
- Test panics with `not yet implemented` / `not implemented: …` (from `todo!()` or `unimplemented!()` in a stub body) → `MISSING_BEHAVIOR` (this is the expected RED state for stub-first integration TDD; see workflow Phase 6 "Rust integration TDD: stub-first")
|
||||
- Test panics with `assertion failed: ... left: ..., right: ...` → `ASSERTION_MISMATCH`
|
||||
- Test file fails to compile due to its own bug (typo, wrong type, unused-import-as-error) → `TEST_BROKEN`
|
||||
- `linker not found`, missing system library, missing feature flag → `ENV_BROKEN`
|
||||
|
|
|
|||
|
|
@ -111,7 +111,8 @@ The test file path must follow the language's actual test layout. **Do not inven
|
|||
- 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`)
|
||||
- Crate-level integration tests: `tests/<feature>.rs (create)` (or, in a workspace, `<crate>/tests/<feature>.rs`).
|
||||
- **If the test references not-yet-existing public API**, the task automatically requires a **stub-first `@make` pre-pass** before `@test` runs (see Phase 6 → "Rust integration TDD: stub-first"). Plan for two `@make` dispatches per such task: stub pass, then body pass.
|
||||
- **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.
|
||||
|
|
@ -213,6 +214,27 @@ When `@test` returns `NOT_TESTABLE: Rust unit-only` (the implementation needs in
|
|||
|
||||
This keeps the agents in their lanes: `@test` never writes inside `src/`, `@make` writes both the tests and the production code in a single coherent change, and the orchestrator sees explicit test pass evidence.
|
||||
|
||||
### Rust integration TDD: stub-first (mandatory)
|
||||
|
||||
Rust integration tests live in a separate test crate (`tests/<feature>.rs`) that imports from `lib.rs`. Any test referencing not-yet-existing public API can only RED at *build* time, which masks assertion diagnostics. To avoid this, **for every Rust task whose `@test` step writes an integration test against public API that does not yet exist**, dispatch a stub-first `@make` pass *before* `@test` runs:
|
||||
|
||||
**Stub pass (split from Phase 7's body pass):**
|
||||
|
||||
1. Dispatch `@make` in **standard mode** (no tests exist yet) with this exact scope:
|
||||
- **Goal:** add the planned public API as `todo!()`-bodied stubs so the integration test will compile.
|
||||
- **Files to modify:** `src/lib.rs` (add `pub mod …;` declarations) plus any new `src/<module>.rs` files containing the stub functions/structs.
|
||||
- **Stubs only:** every function body is exactly `todo!()`. Every method body is exactly `todo!()`. Public structs may use `pub struct Foo;` or `pub struct Foo { /* fields TBD */ }` — but no logic.
|
||||
- **Signatures must match the planned final API exactly** (return types, lifetimes, generics) — otherwise the integration test will mismatch later. Lift signatures from the Phase 3 plan / Phase 5 task spec.
|
||||
- **Acceptance criteria:** `cargo check` (wrapped in `nix develop -c …` if the project has a devshell) passes; no test command is run.
|
||||
- **Code Context Anti-patterns still apply:** the stub pass is small and finalized — no draft bodies, no contradictory signatures.
|
||||
2. Verify `cargo check` passed in `@make`'s output. If not, fix and re-dispatch the stub pass before continuing.
|
||||
3. Dispatch `@test` as normal. The integration test now compiles; running it panics on `todo!()` at runtime, which is a clean `MISSING_BEHAVIOR` RED with a stack trace — far better than the build-error-RED form.
|
||||
4. Continue to Phase 7's body pass (`@make` in TDD mode), where the same files are revisited and the `todo!()` bodies are replaced.
|
||||
|
||||
**This routing is mandatory** for new public API in Rust. It is **not** required when the integration test exercises an existing public API (e.g. a behavior fix where the function already exists) — in that case `@test` runs directly and `@make` modifies the body in Phase 7.
|
||||
|
||||
The stub pass and the body pass each produce their own atomic commit (per Phase 9 rules): `feat(<scope>): scaffold <thing> with todo!() stubs` followed by `feat(<scope>): implement <thing>` (or whichever conventional type fits).
|
||||
|
||||
**Parallelism:** Independent tasks can have tests written in parallel.
|
||||
**Constraint:** `@test` must not modify existing conftest.py files (prevents collision during parallel execution).
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue