Skip to content

test(phase3-C): end-to-end acceptance — unattended UI resolution cycle (#33) - #38

Merged
CameronCrow merged 1 commit into
mainfrom
phase3-c-acceptance-test
Jul 23, 2026
Merged

CameronCrow merged 1 commit into
mainfrom
phase3-c-acceptance-test

Conversation

@CameronCrow

Copy link
Copy Markdown
Owner

Resolves #33 — Phase 3 deliverable C, the end-to-end acceptance proof of the
unattended UI-resolution cycle, deferred until A (#35) and B (#32) landed (both now on main).

What this adds

tests/test_acceptance_phase3.py — the cohesive Phase-3 acceptance test. It drives the three
real cores wired exactly as Claude Code runs them, against a tmp_path project with synthetic
hook payloads (no live Claude Code runtime is needed to exercise the deterministic contract):

edit a UI file            -> PostToolUse flag_ui_change.flag() records it in pending-ui
agent tries to stop       -> Stop stop_gate.decide() BLOCKS (nothing verified yet)
cyclaudes verify runs      -> verify_result.write_result() records the outcome
agent tries to stop again -> Stop routes pass -> allow / fail -> block+diff / abstain -> allow+escalate

Scenarios (one per planning/PHASE_3.md success criterion)

  • Happy unattended cycle (criterion 1): UI edit → flag → Stop blocks → pass covering the file → Stop allows. Zero human input.
  • Self-correct loop (criterion 3): fail → Stop blocks with the expected-vs-actual diff → agent fixes → re-verify pass → allow. The break is caught, never reclassified as a pass.
  • Non-UI change (criterion 4): a non-UI edit does not flag and the Stop gate does not block — the cheap relevance guard.
  • Abstain escalates, never thrashes (criterion 2, the load-bearing rule): abstain makes Stop allow + escalate; 12 re-entries stay block_count == 0 and it is tallied once — proving it cannot consume the block budget toward Claude Code's 8-consecutive-block cap and false-pass there. A blocking abstain is the exact failure the phase exists to prevent.
  • Fail bounded retry: blocks with the diff up to CYCLAUDES_RETRY_CAP, then escalates (allow + "after N attempts") rather than blocking forever.
  • Wiring honesty: the exit-code → outcome mapping can't silently reclassify a real failure as pass/abstain, and the writer rejects an out-of-contract outcome.

Deterministic / fake-driven; green under the default python -m pytest (no desktop, no live runtime).

Test run

python -m pytest -q → 239 passed, 6 deselected (the 6 are -m live desktop checks).

Docs

Checked the Phase-3 success-criterion line in planning/TODO.md and noted C landed in planning/PLAN_MAIN.md. No other phases' TODO lines touched.

🤖 Generated with Claude Code

#33)

The cohesive Phase-3 acceptance proof, closing deliverable C now that A (#35)
and B (#32) have landed. Drives the three real cores wired exactly as Claude
Code runs them — PostToolUse `flag_ui_change.flag` -> `verify_result.write_result`
(the `cyclaudes verify` writer) -> Stop `stop_gate.decide` — against a temp
project with synthetic hook payloads (no live Claude Code runtime needed for the
deterministic contract).

Scenarios (one per PHASE_3.md success criterion):
- Happy unattended cycle: UI edit -> flag records pending-ui -> Stop blocks
  (nothing verified) -> pass covering the touched file -> Stop allows. Zero
  human input (criterion 1).
- Self-correct loop: fail -> Stop blocks WITH the expected-vs-actual diff ->
  agent fixes -> re-verify pass -> allow; the break is caught, never
  reclassified as a pass (criterion 3).
- Non-UI change: does not flag and the Stop gate does not block on it — the
  cheap relevance guard (criterion 4).
- Abstain escalates, never thrashes (the load-bearing rule, criterion 2): an
  abstain makes Stop ALLOW + escalate; 12 re-entries stay block_count == 0 and
  it is tallied once, proving it can't consume the block budget toward the
  8-consecutive-block cap and false-pass there.
- Fail bounded retry: blocks with the diff up to CYCLAUDES_RETRY_CAP, then
  escalates (allow + "after N attempts") instead of blocking forever.
- Wiring honesty: the exit-code -> outcome mapping can't silently reclassify a
  real failure as pass/abstain, and the writer rejects an out-of-contract
  outcome.

Deterministic/fake-driven; green under default `python -m pytest` (239 passed,
6 deselected). TODO + PLAN_MAIN Phase-3 status updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@CameronCrow
CameronCrow force-pushed the phase3-c-acceptance-test branch from e3a4883 to b6c0641 Compare July 23, 2026 15:11
@CameronCrow
CameronCrow merged commit 2aca559 into main Jul 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(phase3-C): end-to-end acceptance — unattended UI resolution cycle (deferred until A+B)

1 participant