Issue #906 — The GitHub App Manifest Callback Server Could Drop a Code

The Broader Goal at the End of the Previous PR

Role provisioning (synlynk team join / role bootstrap) walks an operator through GitHub's App Manifest flow: open a pre-filled form, submit it, GitHub redirects to a loopback server on 127.0.0.1 with a one-time code, and synlynk exchanges that code for App credentials. That loopback server needs to be correct exactly once per operator per role — a dropped code means re-running the whole manifest flow by hand.

What This PR Shipped

synlynk/team.py::_run_manifest_callback_server used a threading.Event plus a plain list to capture the single expected OAuth code:

if code and not code_ready.is_set():
    captured.append(code)
    code_ready.set()

If a second /callback request arrived — a duplicated browser tab, GitHub retrying the redirect, a re-submitted manifest form — before wait_for_code() drained the first one, its code was silently discarded by construction: the guard only ever keeps the first code, and the check-then-set itself isn't atomic across request threads.

The fix swaps the Event/list pair for a queue.Queue() — every valid code is unconditionally put(), so puts never drop data no matter how many requests land before the queue is drained — and swaps the plain http.server.HTTPServer for ThreadingHTTPServer, so concurrent callback requests get their own handler thread instead of serializing behind one accept loop. wait_for_code() becomes a queue.get(timeout=...), keeping the external contract (port, wait_for_code, shutdown) unchanged.

Added tests/test_agent_cli.py::test_manifest_auth_prevent_dropped_oauth_codes_in_manifest_callback_server, which fires two concurrent /callback requests with distinct codes from two threads (synchronized with a threading.Barrier) against the real loopback server, and asserts both get a 200 response and both codes are retrievable via two successive wait_for_code() calls.

Design spec: docs/superpowers/specs/2026-09-02-manifest-callback-concurrency-design.md. Plan: docs/superpowers/plans/2026-09-02-manifest-callback-concurrency.md.

What This Achieved on the Path to Autonomy

Role provisioning is a one-shot, human-in-the-loop flow that every new teammate or role goes through exactly once — a silent drop there is maximally confusing because there is no earlier signal that anything went wrong. Removing the drop makes that onboarding step reliable under the kind of double-click/double-tab behavior real operators actually exhibit.

Strategic Note: The Goal at the End of This PR

Loopback callback servers used anywhere else in synlynk's auth flows should default to a queue-backed capture + ThreadingHTTPServer, not Event/list-based single-shot capture, as the standard pattern.