claw-code/tests
YeonGyu-Kim 11f9e8a5a2 feat: #164 Stage B CLOSURE — turn-loop JSON + cancel_observed coverage + CLAWABLE promotion
Closes all three gaebal-gajae-identified closure criteria for #164 Stage B:

1. turn-loop runtime surface exposes cancel_observed consistently
2. cancellation path tests validate safe-to-reuse semantics
3. turn-loop promoted from OPT_OUT to CLAWABLE surface

Changes:

src/main.py:
- turn-loop accepts --output-format {text,json}
- JSON envelope includes per-turn cancel_observed + final_cancel_observed
- All turn fields exposed: prompt, output, stop_reason, cancel_observed,
  matched_commands, matched_tools
- Exit code 2 on final timeout preserved

tests/test_cli_parity_audit.py:
- CLAWABLE_SURFACES now contains 14 commands (was 13)
- Removed 'turn-loop' from OPT_OUT_SURFACES
- Parametrized --output-format test auto-validates turn-loop JSON

tests/test_cancel_observed_field.py (new, 9 tests):
- TestCancelObservedField (5 tests): field contract
  - default False
  - explicit True preserved
  - normal completion → False
  - bootstrap JSON exposes field
  - turn-loop JSON exposes per-turn field
- TestCancelObservedSafeReuseSemantics (2 tests): reuse contract
  - timeout result has cancel_observed=True when signaled
  - engine.mutable_messages not corrupted after cancelled turn
  - engine accepts fresh message after cancellation
- TestCancelObservedSchemaCompliance (2 tests): SCHEMAS.md contract
  - cancel_observed is always bool
  - final_cancel_observed convenience field present

Closure criteria validated:
-  Field exposed in bootstrap JSON
-  Field exposed per-turn in turn-loop JSON
-  Field is always bool, never null
-  Safe-to-reuse: engine can accept fresh messages after cancellation
-  mutable_messages not corrupted by cancelled turn
-  turn-loop promoted from OPT_OUT (14 clawable commands now)

Protocol now distinguishes at runtime:
  timeout + cancel_observed=false → infra/wedge (escalate)
  timeout + cancel_observed=true → cooperative cancellation (safe to retry)

Test results: 182 → 192 passing, +10 tests, zero regression, 3 skipped unchanged.

Closes #164 Stage B. Stage C (async-native preemption) remains future work.
2026-04-22 19:49:20 +09:00
..
test_cancel_observed_field.py feat: #164 Stage B CLOSURE — turn-loop JSON + cancel_observed coverage + CLAWABLE promotion 2026-04-22 19:49:20 +09:00
test_cli_parity_audit.py feat: #164 Stage B CLOSURE — turn-loop JSON + cancel_observed coverage + CLAWABLE promotion 2026-04-22 19:49:20 +09:00
test_command_graph_tool_pool_output_format.py fix: #169 — command-graph and tool-pool now accept --output-format; diagnostic inventory JSON parity 2026-04-22 18:47:34 +09:00
test_exec_route_bootstrap_output_format.py fix: #168 — exec-command / exec-tool / route / bootstrap now accept --output-format; CLI family JSON parity COMPLETE 2026-04-22 18:34:26 +09:00
test_flush_transcript_cli.py fix: #166 — flush-transcript now accepts --directory / --output-format / --session-id; session-creation command parity with #160/#165 lifecycle triplet 2026-04-22 18:04:25 +09:00
test_json_envelope_field_consistency.py feat: #173 — wrap_json_envelope() applied to all 13 clawable commands (LOOP CLOSED) 2026-04-22 19:35:37 +09:00
test_load_session_cli.py feat: #173 — wrap_json_envelope() applied to all 13 clawable commands (LOOP CLOSED) 2026-04-22 19:35:37 +09:00
test_porting_workspace.py feat: #173 — wrap_json_envelope() applied to all 13 clawable commands (LOOP CLOSED) 2026-04-22 19:35:37 +09:00
test_run_turn_loop_cancellation.py fix: #164 Stage A — cooperative cancellation via cancel_event in submit_message 2026-04-22 18:14:14 +09:00
test_run_turn_loop_continuation.py fix: #163 — remove [turn N] suffix pollution from run_turn_loop; file #164 timeout-cancellation followup 2026-04-22 17:37:22 +09:00
test_run_turn_loop_permissions.py fix: #159 — run_turn_loop no longer hardcodes empty denied_tools; permission denials now parity-match bootstrap_session 2026-04-22 17:50:21 +09:00
test_run_turn_loop_timeout.py fix: #164 Stage A — cooperative cancellation via cancel_event in submit_message 2026-04-22 18:14:14 +09:00
test_session_store.py fix(#160): harden delete_session contract — idempotency, race-safety, typed partial-failure 2026-04-22 17:11:26 +09:00
test_show_command_tool_output_format.py fix: #167 — show-command and show-tool now accept --output-format flag; CLI parity with session-lifecycle family 2026-04-22 18:21:38 +09:00
test_submit_message_budget.py fix: #162 — budget-overflow no longer corrupts session state in submit_message 2026-04-22 17:29:55 +09:00
test_submit_message_cancellation.py fix: #164 Stage A — cooperative cancellation via cancel_event in submit_message 2026-04-22 18:14:14 +09:00