Commit Graph
7 Commits
Author SHA1 Message Date
DottaandPaperclip 59e57017b1 refactor(runner): organize package by responsibility
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 12:56:59 +00:00
DottaandPaperclip 3165f057a3 feat(runner): surface live chat progress
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 01:54:47 +00:00
DottaandPaperclip 011d5046b0 fix(runner): self-host Phase 7 thread fonts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-10 23:19:28 +00:00
DottaandClaude Opus 5 b73524ec2a feat(runner): stream live Codex turns end to end
The clean-room chat revealed a finished reply instead of writing one: the
live session accumulated agent deltas in its turn waiter, the package
session server awaited the whole turn and answered with one JSON body, and
the browser waited on `response.json()`. Nothing between the provider and
the page could show work in progress, so the board saw the answer appear
all at once.

A turn now streams:

- `Phase7LiveSession` publishes bounded, ordered live turn events and
  mirrors the waiter's assembled text into the transcript entry the reply
  will finally occupy, so one rendered card grows and the settled text is
  still assembled in exactly one place.
- `src/phase7/turn-stream.ts` defines the NDJSON frame codec and reader.
  The decoder refuses a replayed, reordered, oversized, late, or truncated
  frame rather than applying a stale view.
- `POST /api/phase7/ui/message` flushes headers, then writes one frame per
  event-loop tick of provider activity. Admission checks still answer with
  a JSON body and their own status code, because they are decided before
  the first frame. Client disconnect interrupts the provider turn.
- The browser client consumes frames and renders each projection in place;
  the settled payload is applied last and remains the authority. Stop,
  reset, and `New chat` abandon the stream, and frames from an abandoned
  turn generation are dropped.

Measured against real Codex through real runnerd: headers at 265 ms, 62
growing assistant states from the first character to a 301-character reply
at 3.1 s, the last interim state identical to the settled one, and a single
assistant card in the settled thread.

The scenario explorer and every fixture, preset, and replay route are
unchanged and stay deterministic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 18:29:28 +00:00
Dotta 5becbbd216 fix(runner): clear the stale pending-request announcement
The Phase 7 issue-thread live region re-armed "A request is waiting for
your answer." on every snapshot while a request was pending. On the live
path a submit re-renders with the same request still pending (state
`submitting`), so the pending copy overwrote "Your answer was recorded."
and then stayed put once the request settled — clean-room QA (PAP-16974)
heard the stale guidance during later turns.

The pending copy is now announced once per request, on arrival, and the
settle path replaces it (and only it) with the request's resolved state
label, so a request that goes away server-side corrects itself too.

Covers pending -> answered -> later turn, plus a server-side withdrawal,
in the issue-thread browser suite: 60/60 pass, `check:phase7:ui` still
reproduces all 24 screenshots byte-for-byte.
2026-08-10 16:49:12 +00:00
DottaandClaude Opus 5 3e875c6f9b feat(runner): add Phase 7M clean-room live Codex chat
Adds a second primary path beside the preset scenario explorer. `#/chat`
opens a blank thread on a freshly minted mock tenant — one company, one
agent, one blank issue — and drives real Codex through real runnerd. The
scenario explorer and its deterministic deep links are unchanged.

The clean room is live-only by construction. The route pins `mode=live`
and drops `shot`/`at`, the package server has no scripted path for it and
refuses any non-live projection, and a failure to start runnerd or Codex
is reported with a retry rather than replaced by a fixture.

`createPhase7CleanRoomSessionInput` seeds the minimum a runner session
needs and fails closed if anything else is present, so "starts blank" is
enforced at the seam rather than assumed. The exposure profile is fixed
and inspectable while the calls stay conversation-driven; three grants
(`governance:approvals:decide`, `workspace:control`,
`test:generic_api_request`) stay withheld so a typed denial is reachable
in a chat nobody scripted.

`Reset` and `New chat` retire the previous session's authority, drop its
workspace, and mint new mock identities; the old session id answers 404
afterwards. The projection now publishes the real-API block as a
`network-guard-<sessionId>` control-plane record, so "no request reached
a real Paperclip API" is inspectable in the drawer on both surfaces.

Also fixes two things the clean room surfaced: the unanchored `sk-`
credential heuristic matched mock ids of the form `task-cleanroom-<token>`,
and a hash change left one committed frame reporting `settled` for the
surface just navigated away from.

The 24-image screenshot matrix is re-recorded: the surface rail carrying
the clean-room entry is a real, always-visible element, and it reproduces
byte-for-byte against Chromium 151.0.7922.34.

Verified: test:phase7 159/159; test:browser:issue-thread 58/58 with axe
clean; check:phase7:ui 24/24 byte-identical; docs:validate 93 files, 34
OKF concepts; smoke:phase7:cleanroom all 15 assertions true against real
Codex 0.132.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 15:45:35 +00:00
DottaandPaperclip c6d99430ee feat(runner): consolidate accepted Phase 7 issue thread UI
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-10 12:22:23 +00:00