Skip to content

Send one response for GET /api/activity - #67

Merged
dmccoystephenson merged 1 commit into
mainfrom
feature/activity-single-response
Oct 31, 2026
Merged

dmccoystephenson merged 1 commit into
mainfrom
feature/activity-single-response

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

  • GET /api/activity (and HEAD) in src/arcade/server.py _api was missing a return after its 200 response. Control fell through to the trailing 404 not found, so a second response was written on the same keep-alive connection, and the client's next request on that connection received the stray 404. One return is added.
  • A regression test, test_activity_answers_once_on_a_kept_alive_connection, sends two pipelined requests over a raw socket and asserts exactly two 200 OK status lines, with the second body being /version.json's. A raw socket is used because http.client reads ahead and silently discards the extra bytes, which is why the existing activity test never caught this.
  • CHANGELOG: an entry under ## [Unreleased] → ### Fixed.

Closes #66

Test plan

  • python3 -m pytest -q: 408 passed, 13 skipped (local, Python 3)
  • Stash-and-run: with the server.py fix stashed, the new test FAILS (only one status line arrives before the stray 404); with the fix restored, it PASSES
  • check-registry examples/games.yaml was run by CI (the local run was blocked by the sandbox)
  • CI: test (3.8), test (3.12), docker

Deferred

Coverage note

The request routing is covered by the loopback server tests. The behaviour behind Traefik's pooled connections is not exercised by CI, but the fix only stops an extra write, so nothing more is expected to change there.

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

🤖 Generated with Claude Code


drafted by Claude on behalf of Daniel Stephenson

The /api/activity branch in _api had no return after sending the levels,
so it fell through to the final 404 and wrote a second response on the
same connection. A client reusing the kept-alive connection got that
404 as the answer to its next request. A raw-socket test sends two
pipelined requests and checks that both get their own 200.

Fixes #66.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review rubric (run against CI on head of feature/activity-single-response):

  • Scope: PASS. Three files changed, each needed for GET /api/activity writes a stray 404 after its response on a kept-alive connection #66: the one-line return, its regression test and the CHANGELOG entry.
  • Tests-new: PASS. No new public function was added; the new test exercises the changed route.
  • Tests-fix: PASS. Stash-and-run was done locally: with the server.py change stashed, test_activity_answers_once_on_a_kept_alive_connection FAILED (the stray 404 took the second status line). With it restored, the test PASSED.
  • Sibling structure: PASS. The test is a plain def test_<behaviour> in tests/test_server.py and uses the existing arcade fixture, like test_isolation_is_decided_per_request_on_a_kept_alive_connection.
  • Sibling renames: PASS. Nothing was renamed.
  • Docs: PASS. README's /api/activity row already describes the correct single response, so no README change is needed. CHANGELOG has an entry under [Unreleased] → ### Fixed.
  • Issue resolution: PASS. GET /api/activity writes a stray 404 after its response on a kept-alive connection #66 names the missing return in _api, and that is the line added.
  • CI: PASS. test (3.8), test (3.12), docker and engine are all green on the PR head.
  • Stdlib-only: PASS. No imports were added under src/; socket was added to a test only.
  • Py3.8: PASS. No 3.9+ constructs were added, and test (3.8) is green.
  • Vendored client untouched: PASS. trace_client.py is not in the diff.
  • Naming: PASS. No new identifiers were added in src/. The test locals (sock, received, statuses) are single words.
  • Isolation headers: PASS (not applicable). No headers on any response path changed. The fix only stops a second response from being written.
  • Config / CLI documented: PASS (not applicable). No ARCADE_* variables or CLI changes.
  • Changelog: PASS. The entry was added and __version__ is unchanged.

Findings:

  • tests/test_server.py:456: the status-line scan would also match a body line that starts with HTTP/1.1 . Neither body here can contain one (both are JSON), so this is acceptable, but the test is tied to these two routes.
  • black --check on the changed files could not be run locally because the sandbox blocked it, so formatting is UNVERIFIED locally. The added lines are under 110 columns and follow the formatting of nearby code.
  • Do-not-auto-merge check: src/arcade/server.py is in the diff, but the changed hunk touches only the /api/activity route's control flow. It touches no token checking, ISOLATION_HEADERS or CORS header values (the Access-Control-Allow-Origin line appears only as unchanged context).

The fix is one line with an empirically confirmed regression test, and CI is green.

This review comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

@dmccoystephenson
dmccoystephenson merged commit ea2eb16 into main Oct 31, 2026
4 checks passed
@dmccoystephenson
dmccoystephenson deleted the feature/activity-single-response branch October 31, 2026 07:03
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.

GET /api/activity writes a stray 404 after its response on a kept-alive connection

1 participant