jobizzness / jobizzness/bench

Answers can still fail silently — #60 fixed two of four call sites

Open
#69 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
1
Forks
0
Avg merge
1h 21m
Merged PRs (30d)
41

Description

## Evidence

#60 fixed `Queue.tsx` and `PhoneUnblock.tsx`, which is what its acceptance criteria named. There are **four** places that post an answer, and the two it did not name are the desktop path — the one used most.

### 1. `answerIntake()` reports success on failure and destroys the answer

`src/client/components/App.tsx:187-199`:

```ts
async function answerIntake() {
if (!row || !decision || !bar || bar.blocked) return;
setError(null);

await postJson(`/api/sessions/${row.id}/answer`, {
answers: intakePayload(decision, answers),
text: note.trim(),
});
dismiss();
setSheetOpen(false);
setNote("");
await reload();
}
```

No `res.ok` check and no `catch`. On any failure — a rejected POST or a non-2xx — it still calls `dismiss()`, closes the sheet and clears the note. The developer watches the intake sheet close, which is the app's signal for *sent*, and their typed answers are gone.

It is invoked as `void answerIntake()` (`App.tsx:255`, `App.tsx:457`), so a rejection is discarded exactly as in #60.

This is strictly worse than the bug #60 fixed: `Queue.tsx` at least left the text alone.

### 2. `submit()` checks the status but not the rejection

`src/client/components/App.tsx:201-235`:

```ts
const res = await postJson(`/api/sessions/${row.id}/answer`, { optionId: choice, text: said, images: attachments });
if (!res.ok) {
setError((await res.json()).error ?? "could not send");
return;
}
```

Two holes:

- **No `try`/`catch`.** A network rejection — offline, daemon down, relay down — never reaches the `!res.ok` branch. Called as `void submit()` (`App.tsx:255`, `App.tsx:403`), so it is silent.
- **`await res.json()` on a non-JSON error body throws**, inside the error path itself. A 502 from the relay with an HTML body produces no message at all — the failure handler fails.

The same `/api/sessions/:id/message` branch at `App.tsx:226` has the identical `res.json()` hole.

### 3. Two more unguarded `loadArtifact` calls

Same shape as the `useReportFrame.ts` bug #60 fixed:

- `src/client/components/ArtifactCard.tsx:35` — `void loadArtifact(...).then(...)`, no `.catch()`
- `src/client/components/ArtifactDialog.tsx:43` — same

A failed relayed load leaves an empty box forever and an unhandled rejection. `useReportFrame.ts` now returns a `failed` flag for exactly this; these two predate it.

## Acceptance criteria

- [ ] `answerIntake()` does not dismiss, close the sheet or clear the note unless the POST actually succeeded
- [ ] A failed intake answer shows the developer that it failed, and the typed answers survive it
- [ ] `submit()` surfaces a rejected POST, not only a non-2xx one
- [ ] A non-JSON error body produces a readable message rather than throwing inside the error path — both the `/answer` and `/message` branches
- [ ] `ArtifactCard.tsx` and `ArtifactDialog.tsx` render something honest on a failed load and leave no unhandled rejection
- [ ] A test for each of: intake POST rejects (sheet stays open, note kept), `submit()` POST rejects, 502 with an HTML body, artifact load rejects
- [ ] `pnpm typecheck` and `pnpm test` clean against the #50 baseline of 4 known failures

## Out of scope

- Automatic retry or an offline queue — same call as #60: tell the truth first.
- Any change to `Queue.tsx`, `PhoneUnblock.tsx` or `useReportFrame.ts`. Those are done and merged at `816d91d`; follow their pattern, do not revisit them.
- Refactoring the four answer call sites into one shared helper. Tempting and probably right eventually, but it is a bigger change than this and would make the fix hard to review.

## Verification

```
pnpm typecheck
pnpm test # 4 failures expected: dispatch-modal, model-costs, model-picker, settings-ui (#50)
```

Manual: with the daemon stopped, answer an intake from the cockpit and confirm the sheet stays open with the answers still in it.

## Related

- #60 (fixed the other two sites; merged at `816d91d`)
- #50 (the 4-failure baseline)
- #65 (re-run any fifth failure alone before trusting it)

Contributor guide

Open the contributing guide

Research direction

Start with the four call sites named in src/client/components/App.tsx, ArtifactCard.tsx, and ArtifactDialog.tsx, comparing the completed pattern in useReportFrame.ts. Add tests for rejected POSTs, non-JSON errors, and failed artifact loads, then run pnpm typecheck and pnpm test against the documented four-failure baseline. Done means failures remain visible, entered answers are preserved, and no unhandled rejection occurs.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.