Skip to content

Seating-loop refusal invariant only matches a bare exit 1 — widen it or fail on shapes it cannot analyse #357

Closed

Description

@tps-flint

Follow-up from cli#352 r7. Not a defect in what shipped — a stated coverage boundary, written down so nobody assumes more than the control actually provides.

What the control does

scripts/test-tps-office-supervisor.sh asserts, over the supervisor source, that every refusal inside the agent seating loop calls shutdown_children before exiting. This exists because write_pids_file runs only after the loop and the EXIT trap merely removes the file, so an exit that skips the shutdown orphans sandboxed agents from earlier iterations with nothing durable to reap them. A team is either whole or absent, never partial.

It earned its place immediately: Sherlock found one unguarded refusal by reading, and the assertion found four more (the roster-shape checks for missing id, invalid id, missing configPath, invalid configPath) that two reviewers had missed.

The boundary

The matcher keys on a bare exit 1 alone on its own line:

if (L[j] !~ /^[[:space:]]*exit[[:space:]]+1[[:space:]]*$/) continue

So these would not be seen:

  • exit 2 or any non-1 status
  • exit "$rc" or any variable status
  • an exit sharing a line with other content, e.g. ... && exit 1
  • an exit reached through a helper function rather than written inline in the loop

Today the supervisor only uses the bare form, so coverage is complete for the current shape. The risk is the ordinary one: someone adds a refusal in a different shape, the control stays green, and the class quietly returns — the control still passes while no longer proving what its name claims.

Suggested fix

Widen the matcher to any exit statement within the bounded region regardless of status expression or line position, and keep the existing backward walk for the guard. If a form genuinely cannot be analysed, fail rather than skip it — the control already does the right thing when it cannot bound the loop at all (it calls bad with "cannot assert the all-or-nothing refusal invariant" instead of silently finding zero violations), and the same principle should apply per-exit.

Acceptance: add a fixture exit in one of the currently-invisible shapes, confirm the control goes red, then fix the shape and confirm green.

Refs cli#341 cli#352.

Activity

  1. moved this to Triage in Office queueon Oct 1, 2026
  2. self-assigned this
    on Nov 9, 2026
  3. moved this from Ready to In review in Office queueon Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2Normal queue

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions