Per-gate expect declarations with red classifier ported from RedAssert #492

Closed
opened 2026-08-23 17:48:14 +00:00 by jared · 2 comments
Owner

Context

GateVerdict judges every gate by exit_status.zero? (command_result.rb:6-7). The test-writer node needs the inverse — red for the right reason — which today is unenforced: maps/poodr-implementation.yaml:74-77 aliases both pass and fail edges to test-reviewer, and agents/test-writer.md:20 documents :broken rejection that nothing implements. The correct classification (:unexpected_pass / :red / :broken via MinitestSummary) exists only in lib/os_sdlc/red_assert.rb:21-26, reachable solely from the legacy session-less CLI. Decision (2026-08-23, option C): gates declare expectations per entry, mixed scalar-or-hash form, green default.

Tasks

  • Map schema: a gates entry is either a string (expect green) or {gate: NAME, expect: red|green}; Map::Validator normalizes both to one internal form and rejects unknown expect values.
  • GateVerdict asks each gate entry for its classifier: green = exit-zero; red = port of RedAssert#classify (exit zero → unexpected_pass fail; nonzero with executed failure per MinitestSummary → pass; else broken fail). Reply names the classification so the agent knows broken vs red (depends on ticket: gate reply feedback).
  • test-writer node becomes gates: [{gate: test, expect: red}] with real edges: pass → test-reviewer, fail → test-writer (bounded), replacing the aliased edges.
  • Correct agents/test-writer.md:20 to describe the actual gate behavior.
  • Amend ADR-0139 (or record a new ADR) covering per-gate expect and the session-less CLI's status per #476.
  • Tests cover normalization, red classification (unexpected_pass, red, broken), and map edge routing on each.

Acceptance criteria

  • A test-writer round whose suite exits nonzero from a LoadError settles as fail (broken), not pass.
  • A genuinely red round settles as pass and routes to test-reviewer.
  • Programmer node map entry is unchanged in form (gates: [test, lint] style strings stay valid).

Out of scope: deleting the legacy session-less red-assert/green-assert CLI chain (RedAssert/GreenAssert/BaseAssert/GateRunner and wrappers) — separate cleanup after this lands.

Origin

  • Trigger: #476 grilling session 2026-08-23, trace showing RedAssert unreachable from os-sdlc-runner gate.
  • Improvised this session: none.
  • Chain: red-for-right-reason unenforced ← GateVerdict exit-zero-only check (gate_verdict.rb:31, command_result.rb:6-7) + aliased map edges (poodr-implementation.yaml:74-77) ← DESIGN (ADR-0133/0134 map-driven routing shipped without a red gate kind; classification stayed in the v1 CLI per ADR-0052).
  • Root candidate: this ticket.
## Context GateVerdict judges every gate by `exit_status.zero?` (command_result.rb:6-7). The test-writer node needs the inverse — red for the right reason — which today is unenforced: maps/poodr-implementation.yaml:74-77 aliases both pass and fail edges to test-reviewer, and agents/test-writer.md:20 documents `:broken` rejection that nothing implements. The correct classification (`:unexpected_pass` / `:red` / `:broken` via MinitestSummary) exists only in lib/os_sdlc/red_assert.rb:21-26, reachable solely from the legacy session-less CLI. Decision (2026-08-23, option C): gates declare expectations per entry, mixed scalar-or-hash form, green default. ## Tasks - [ ] Map schema: a gates entry is either a string (expect green) or `{gate: NAME, expect: red|green}`; Map::Validator normalizes both to one internal form and rejects unknown expect values. - [ ] GateVerdict asks each gate entry for its classifier: green = exit-zero; red = port of RedAssert#classify (exit zero → unexpected_pass fail; nonzero with executed failure per MinitestSummary → pass; else broken fail). Reply names the classification so the agent knows broken vs red (depends on ticket: gate reply feedback). - [ ] test-writer node becomes `gates: [{gate: test, expect: red}]` with real edges: pass → test-reviewer, fail → test-writer (bounded), replacing the aliased edges. - [ ] Correct agents/test-writer.md:20 to describe the actual gate behavior. - [ ] Amend ADR-0139 (or record a new ADR) covering per-gate expect and the session-less CLI's status per #476. - [ ] Tests cover normalization, red classification (unexpected_pass, red, broken), and map edge routing on each. ## Acceptance criteria - [ ] A test-writer round whose suite exits nonzero from a LoadError settles as fail (broken), not pass. - [ ] A genuinely red round settles as pass and routes to test-reviewer. - [ ] Programmer node map entry is unchanged in form (`gates: [test, lint]` style strings stay valid). Out of scope: deleting the legacy session-less red-assert/green-assert CLI chain (RedAssert/GreenAssert/BaseAssert/GateRunner and wrappers) — separate cleanup after this lands. ## Origin - Trigger: #476 grilling session 2026-08-23, trace showing RedAssert unreachable from `os-sdlc-runner gate`. - Improvised this session: none. - Chain: red-for-right-reason unenforced ← GateVerdict exit-zero-only check (gate_verdict.rb:31, command_result.rb:6-7) + aliased map edges (poodr-implementation.yaml:74-77) ← DESIGN (ADR-0133/0134 map-driven routing shipped without a red gate kind; classification stayed in the v1 CLI per ADR-0052). - Root candidate: this ticket.
Author
Owner

Acceptance addendum (2026-08-24, bundling the #499 decision): the test-writer node also carries a test-lint gate ({expect: green}) over the diffed TEST files, and the programmer/repair lint gate is scoped to exclude test files. Acceptance: a test-file rubocop offense settles the test-writer round as fail and routes back to test-writer (bounded), never to programmer-repair; production-file offenses behave as today. Fail edge bound: 2, exhausted -> error, matching the programmer-repair convention. Work started 2026-08-24 direct on main (probe + change-interpreter consult done this session; new ADR to be recorded, NOT an ADR-0139 amendment — 0139 is unrelated --target binding).

Acceptance addendum (2026-08-24, bundling the #499 decision): the test-writer node also carries a test-lint gate ({expect: green}) over the diffed TEST files, and the programmer/repair lint gate is scoped to exclude test files. Acceptance: a test-file rubocop offense settles the test-writer round as fail and routes back to test-writer (bounded), never to programmer-repair; production-file offenses behave as today. Fail edge bound: 2, exhausted -> error, matching the programmer-repair convention. Work started 2026-08-24 direct on main (probe + change-interpreter consult done this session; new ADR to be recorded, NOT an ADR-0139 amendment — 0139 is unrelated --target binding).
Author
Owner

Resolution

Done: Per-gate expect shipped: GateExpectation normalizes scalar-or-hash gates entries (green default, unknown expect rejected at Map::Validator); GateVerdict classifies expect-red gates via the RedAssert port (unexpected_pass/red/broken through MinitestSummary); settled reply names the classification; test-writer node now gates: [{gate: test, expect: red}, test-lint] with a real bounded fail edge; agents/test-writer.md corrected

Evidence: Commit c7ee237 on main, 21 files; ADR-0147 (Accepted) records the schema, classification table, test-lint ownership, and retained legacy CLI; all four classifications covered in tests/runner/gate_verdict_test.rb; acceptance verified: LoadError nonzero settles broken/fail, genuine red settles pass to test-reviewer, programmer string-form gates unchanged (regression test); suite 1080 runs green except the 4 pre-existing ChangeInterpreterAgentTest baseline failures; rubocop clean

Follow-ups: Legacy RedAssert/GateRunner session-less CLI cleanup explicitly deferred in ADR-0147, already tracked as out-of-scope per the ticket. Live supervised pipeline run of the new red gate rides on #501's review gate (the planned replay). Otherwise none.

## Resolution **Done:** Per-gate expect shipped: GateExpectation normalizes scalar-or-hash gates entries (green default, unknown expect rejected at Map::Validator); GateVerdict classifies expect-red gates via the RedAssert port (unexpected_pass/red/broken through MinitestSummary); settled reply names the classification; test-writer node now gates: [{gate: test, expect: red}, test-lint] with a real bounded fail edge; agents/test-writer.md corrected **Evidence:** Commit c7ee237 on main, 21 files; ADR-0147 (Accepted) records the schema, classification table, test-lint ownership, and retained legacy CLI; all four classifications covered in tests/runner/gate_verdict_test.rb; acceptance verified: LoadError nonzero settles broken/fail, genuine red settles pass to test-reviewer, programmer string-form gates unchanged (regression test); suite 1080 runs green except the 4 pre-existing ChangeInterpreterAgentTest baseline failures; rubocop clean **Follow-ups:** Legacy RedAssert/GateRunner session-less CLI cleanup explicitly deferred in ADR-0147, already tracked as out-of-scope per the ticket. Live supervised pipeline run of the new red gate rides on #501's review gate (the planned replay). Otherwise none.
jared closed this issue 2026-08-24 21:19:05 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#492
No description provided.