Requirement 7's read bound credits any loop whose condition is a leading read drawing on a descriptor-0 redirect, and it does not distinguish while from until. For until, that reasoning is exactly inverted: input exhaustion makes read return non-zero forever, which keeps the until condition false forever, so the loop never exits.
until read status; do sleep 60; done < /tmp/state.txt is allowed. So is until read l; do sleep 30; done < f.
Measured against an empty source, with a bounded experiment rather than the wait itself:
timeout 3 bash -c 'until read l; do sleep 0.2; echo x >> marker; done < empty.txt'
exits 124 with 14 lines in the marker file. The same shape over a non-empty file exits 0 at once, so the leak is specifically the exhausted or empty source, which is what an agent writes for "wait until the status file has a line".
Two statements are false as written and are part of the fix:
gh-write-guard.py, _reads_its_input's docstring: "a read, which ends the loop when the input is exhausted".
host-setup/agent-safety/README.md requirement 7: "which is bounded by that input".
No background operator, no setsid and no coproc is involved, so none of the deliberate coarsening covers it. Found by a local strict review pass on #1632 and recorded there rather than fixed, per the local-strict-review edit budget.
Requirement 7's
readbound credits any loop whose condition is a leadingreaddrawing on a descriptor-0 redirect, and it does not distinguishwhilefromuntil. Foruntil, that reasoning is exactly inverted: input exhaustion makesreadreturn non-zero forever, which keeps theuntilcondition false forever, so the loop never exits.until read status; do sleep 60; done < /tmp/state.txtis allowed. So isuntil read l; do sleep 30; done < f.Measured against an empty source, with a bounded experiment rather than the wait itself:
timeout 3 bash -c 'until read l; do sleep 0.2; echo x >> marker; done < empty.txt'exits 124 with 14 lines in the marker file. The same shape over a non-empty file exits 0 at once, so the leak is specifically the exhausted or empty source, which is what an agent writes for "wait until the status file has a line".
Two statements are false as written and are part of the fix:
gh-write-guard.py,_reads_its_input's docstring: "aread, which ends the loop when the input is exhausted".host-setup/agent-safety/README.mdrequirement 7: "which is bounded by that input".No background operator, no
setsidand nocoprocis involved, so none of the deliberate coarsening covers it. Found by a local strict review pass on #1632 and recorded there rather than fixed, per the local-strict-review edit budget.