test(acceptance): resend unaccepted input_busy in the navigation repro
- commit
- b3155ec1820bbe46cd1702c5ebfd326ff80b8652
- author
- brice · · 2026-09-23 03:05 UTC
- parent
- 1c179ae
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +21 -0
M scripts/remote-picker-harness/navigation_repro.py +21 -0
| @@ -8,9 +8,14 @@ host named remote reaching the remote container. No host session is touched. | |||
| 8 | 8 | import json | |
| 9 | 9 | import subprocess | |
| 10 | 10 | import sys | |
| 11 | + | import time | |
| 11 | 12 | import uuid | |
| 12 | 13 | ||
| 13 | 14 | ||
| 15 | + | class InputBusy(AssertionError): | |
| 16 | + | pass | |
| 17 | + | ||
| 18 | + | ||
| 14 | 19 | class Driver: | |
| 15 | 20 | def __init__(self, container, session): | |
| 16 | 21 | self.process = subprocess.Popen( | |
| @@ -43,6 +48,19 @@ class Driver: | |||
| 43 | 48 | return context["generation"] | |
| 44 | 49 | ||
| 45 | 50 | def call(self, operation, **fields): | |
| 51 | + | # input_busy is an unaccepted refusal while the input owner still | |
| 52 | + | # delivers an earlier terminal read; resending it is safe and bounded. | |
| 53 | + | # Accepted actions and every other error are never retried. | |
| 54 | + | deadline = time.monotonic() + 3 | |
| 55 | + | while True: | |
| 56 | + | try: | |
| 57 | + | return self._call_once(operation, **fields) | |
| 58 | + | except InputBusy: | |
| 59 | + | if time.monotonic() >= deadline: | |
| 60 | + | raise | |
| 61 | + | time.sleep(0.05) | |
| 62 | + | ||
| 63 | + | def _call_once(self, operation, **fields): | |
| 46 | 64 | self.request_id += 1 | |
| 47 | 65 | request = dict(version=1, id=self.request_id, op=operation, | |
| 48 | 66 | attachment=self.attachment, **fields) | |
| @@ -51,6 +69,9 @@ class Driver: | |||
| 51 | 69 | self.process.stdin.write(json.dumps(request) + "\n") | |
| 52 | 70 | self.process.stdin.flush() | |
| 53 | 71 | response = json.loads(self.process.stdout.readline()) | |
| 72 | + | error = response.get("error") | |
| 73 | + | if isinstance(error, dict) and error.get("code") == "input_busy" and not error.get("accepted"): | |
| 74 | + | raise InputBusy(response) | |
| 54 | 75 | assert "error" not in response, response | |
| 55 | 76 | result = response["result"] | |
| 56 | 77 | self.generation = result.get("context", {}).get("generation", self.generation) | |
| @@ -8,9 +8,14 @@ host named remote reaching the remote container. No host session is touched. | |||||
| 8 | import json | 8 | import json | ||
| 9 | import subprocess | 9 | import subprocess | ||
| 10 | import sys | 10 | import sys | ||
| 11 | + | import time | |||
| 11 | import uuid | 12 | import uuid | ||
| 12 | 13 | ||||
| 13 | 14 | ||||
| 15 | + | class InputBusy(AssertionError): | |||
| 16 | + | pass | |||
| 17 | + | ||||
| 18 | + | ||||
| 14 | class Driver: | 19 | class Driver: | ||
| 15 | def __init__(self, container, session): | 20 | def __init__(self, container, session): | ||
| 16 | self.process = subprocess.Popen( | 21 | self.process = subprocess.Popen( | ||
| @@ -43,6 +48,19 @@ class Driver: | |||||
| 43 | return context["generation"] | 48 | return context["generation"] | ||
| 44 | 49 | ||||
| 45 | def call(self, operation, **fields): | 50 | def call(self, operation, **fields): | ||
| 51 | + | # input_busy is an unaccepted refusal while the input owner still | |||
| 52 | + | # delivers an earlier terminal read; resending it is safe and bounded. | |||
| 53 | + | # Accepted actions and every other error are never retried. | |||
| 54 | + | deadline = time.monotonic() + 3 | |||
| 55 | + | while True: | |||
| 56 | + | try: | |||
| 57 | + | return self._call_once(operation, **fields) | |||
| 58 | + | except InputBusy: | |||
| 59 | + | if time.monotonic() >= deadline: | |||
| 60 | + | raise | |||
| 61 | + | time.sleep(0.05) | |||
| 62 | + | ||||
| 63 | + | def _call_once(self, operation, **fields): | |||
| 46 | self.request_id += 1 | 64 | self.request_id += 1 | ||
| 47 | request = dict(version=1, id=self.request_id, op=operation, | 65 | request = dict(version=1, id=self.request_id, op=operation, | ||
| 48 | attachment=self.attachment, **fields) | 66 | attachment=self.attachment, **fields) | ||
| @@ -51,6 +69,9 @@ class Driver: | |||||
| 51 | self.process.stdin.write(json.dumps(request) + "\n") | 69 | self.process.stdin.write(json.dumps(request) + "\n") | ||
| 52 | self.process.stdin.flush() | 70 | self.process.stdin.flush() | ||
| 53 | response = json.loads(self.process.stdout.readline()) | 71 | response = json.loads(self.process.stdout.readline()) | ||
| 72 | + | error = response.get("error") | |||
| 73 | + | if isinstance(error, dict) and error.get("code") == "input_busy" and not error.get("accepted"): | |||
| 74 | + | raise InputBusy(response) | |||
| 54 | assert "error" not in response, response | 75 | assert "error" not in response, response | ||
| 55 | result = response["result"] | 76 | result = response["result"] | ||
| 56 | self.generation = result.get("context", {}).get("generation", self.generation) | 77 | self.generation = result.get("context", {}).get("generation", self.generation) | ||