feat(runtime-bridge): SW-C — aif-handoff WebSocket status read-back (getStatus + awaitDone) - #296
Merged
Merged
Conversation
…ot silent drop)
Cold-QA (T19) of the SW-C commit found a gap against kickoff §3 SW-C item 5:
"aif-handoff disconnect → state.md notes the disconnection AND awaitDone
returns a graceful error (NOT silent drop)". The graceful-error half was
implemented (BackendError 'unavailable'); the state.md-note half was not —
appendStateFile only fired on status messages, never on the disconnect path.
Fix:
- aifWsStatus.ts: generalise appendStateFile → appendStateLine(content); the
onclose handler now appends `event=ws_disconnected …` on both the reconnect
path (attempts_left=N) and the terminal give-up path (terminal=true) before
surfacing BackendError('unavailable').
- aif-status-readback.test.ts: +2 paired tests — (a) disconnect with
stateFilePath set → state.md contains event=ws_disconnected + terminal=true
AND awaitDone rejects unavailable (both halves of item 5); (b) NEGATIVE — a
clean done does NOT spuriously write a ws_disconnected event.
32 tests pass (was 30). Typecheck clean.
Prior-art: skipped — completes SW-C item 5 on existing files (state.md disconnect note + paired test); no new capability, no new dependency, no new ≥80 LOC file.
artyhoo
enabled auto-merge (squash)
May 29, 2026 23:10
artyhoo
added a commit
that referenced
this pull request
May 31, 2026
…uthored autonomously by aif) (#318) The "human-in-the-loop" half of the aif-question-loop: pulls every task aif parked for human input (manualReviewRequired / blocked_external / non-empty blockedReason) from GET /tasks and prints them in one place (--json for piping into a chat). Sibling of dispatch.ts (#313) → await.ts (#296) → questions.ts. Provenance: this is the FIRST file authored end-to-end by aif-handoff via the runtime-bridge autonomous dispatch (task 048fa1d1, 2026-05-31) and brought into the repo by the coordinator. Reviewed: pure testable functions, Superset-agnostic, env config per sibling convention, entrypoint-guarded. Verified in this repo: 10/10 tests pass, typecheck clean. Pure functions (isParked / selectParked / parkedReason / formatHuman / fetchTasks / parseQuestionsArgs) + entrypoint guard so importing for tests is a no-op. Task fields verified live against GET /tasks (2026-05-31), not assumed. Prior-art: design docs/superpowers/specs/2026-05-31-aif-question-loop-design.md §2 reuse-vs-build table — verdict BUILD (thin) for cli/questions.ts; BFR §3 deep search deferred to SW-B per design §6; reuses runtime-bridge siblings #313/#296, no new dependency.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 Sub-wave C — aif-handoff WebSocket status read-back
Umbrella:
aif-handoff-runtime-bridge-iphase. Replaces SW-B'sgetStatus/awaitDoneplaceholders inAifHandoffBackendwith real implementations backed by aif-handoff's REST + WebSocket surfaces. New modulesrc/aifWsStatus.ts(WS consumer + REST snapshot helper + status-enum mapping). Scope:packages/runtime-bridge/only.MUST COVER (kickoff §3 SW-C, lines 206–214)
aifWsStatus.awaitTaskDone→ws://localhost:3009/wsaifWsStatus.tsif (payload.id !== taskId) return— verified against real source thatbroadcast()fans out to all clients (no server-side topic), so client filtering is mandatoryappendStateLineappends[ISO] taskId=<id> status=<status>done/verified→success;blocked_external→!success (resolves); disconnect→bounded reconnect (≤3)→unavailableawaitDoneraisesunavailableAifHandoffBackend.awaitDoneintegration testCold-QA (T19) finding — fixed in commit 2
Reviewing the SW-C commit (
abb6593) against item 5, I found the state.md-notes-the-disconnection half was missing —appendStateFileonly fired on status messages, never on the disconnect path (the graceful-error half was present). Commit390aabacompletes it:onclosenow appendsevent=ws_disconnected …on both reconnect and terminal paths, with 2 added paired tests (PASS: state.md records the disconnect; NEGATIVE: a cleandonedoes not spuriously write a disconnect event).T16 — schema verified against real aif-handoff source (not assumed)
toTaskBroadcastPayloadreturns{ id, title, status }→payload.idis the taskId —packages/api/src/repositories/tasks.ts:21/wsviaupgradeWebSocket—packages/api/src/ws.ts:27{ type, payload: toTaskBroadcastPayload(task) }, status changes usetask:updated—packages/api/src/routes/tasks.ts:419,438broadcast()sends to all connected clients (no server topic filter) —packages/api/src/ws.ts:75-84→ confirms SW-A's finding; client-side filter is correctTASK_STATUSES=backlog|planning|plan_ready|implementing|review|blocked_external|done|verified(noerror/failed) —packages/shared/src/types.ts§1.7 forward+backward
no-paid-llm-in-ci.md §1(zero LLM/API calls — pure WS/REST client);build-first-reuse-default.md(ADAPT, not BUILD — extends SW-B'sRuntimeBackendinterface verbatim and wraps aif-handoff's existing broadcast stream with a thin client-side filter; no re-implementation of the event bus);dual-implementation-discipline.md(@dual-pair: runtime-bridge-aif-handoffmarker retained onAifHandoffBackend.ts:68). Testability via DI (WebSocketImpl) becausevi.stubGlobalcannot intercept named imports fromnode:http.AifHandoffConfiggainswsUrl/stateFilePath/WebSocketImpl(all optional, defaults derived);getStatus/awaitDonereplace SW-B placeholders behind the unchangedRuntimeBackendcontract. No rule/principle/SKILL touched (so no §1.7 commit trailer required — pre-push s17 confirms no-op).Verification (self, human verifies nothing)
npm run typecheck(tsc --noEmit) — cleannpx vitest run— 32 passed (2 files)npm --prefix packages/core run test:principles— 153 passed, 4 skippedabb6593cites SSOT docs(research-patches): Wave 6 AI-doc cold audit (§13.26) #27/docs(wave-6): close §13.26 — AI-doc effectiveness cold audit + review verdicts #28/docs(wave-5): readiness REVISE fixes — SSOT ID remap + §13.25 status #30/Arm Phase 10 (§13.32) + ai-laziness-traps project rule #44/docs: post-1A follow-up (Class C reviewer-discipline, Q3/Q5, README absolutism) #67/docs(skills): sync shipped prose to C-1 resolution (close DN-1) #84 — all verified to exist🟢 Auto-merge on green CI (staging-trunk model).