Commit Graph
45 Commits
Author SHA1 Message Date
DottaandPaperclip 904199558d fix(runner): qualify release eval artifacts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-12 02:12:22 +00:00
DottaandPaperclip db349f7670 feat(runner): publish eval integration contract
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 20:49:39 +00:00
DottaandPaperclip 90caa73b95 feat(runner): wire provider-neutral PRP receipts
Add versioned semantic-tool and budget stop receipts with replay parity, conformance fixtures, and normalized adapter evidence. Score eval trace completeness from PRP receipts while preserving the existing live evidence fallback.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 19:54:37 +00:00
DottaandPaperclip 3cf6db82c1 fix(runner): restore capability issue thread imports
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 18:26:48 +00:00
DottaandPaperclip 3294ca06c8 feat(runner): execute live eval slice
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 17:42:07 +00:00
DottaandPaperclip 8ec8a68d4a docs(runner): publish canonical operation groups
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 15:39:35 +00:00
DottaandPaperclip 0a0eb19713 feat(runner): define package and eval boundaries
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 15:21:28 +00:00
DottaandPaperclip 59e57017b1 refactor(runner): organize package by responsibility
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 12:56:59 +00:00
DottaandPaperclip ae80fe4d00 feat(runner): consolidate remaining Phase 5 and 6 work
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 11:45:45 +00:00
DottaandPaperclip cf27ba2869 fix(runner): recreate clean-room scratch roots
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-11 11:17:28 +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 e995b59933 fix(runner): drop the untracked standalone export from the package barrel
`src/index.ts` re-exported `./standalone/phase6-demo.js`, but that module only
ever existed as an untracked file in the shared worktree. The commit therefore
built there and failed `build:typescript` with TS2307 in any clean checkout,
which blocked the Phase 7U deployment. Nothing tracked consumes the module, so
removing the export restores a buildable tree without touching the accepted
remediation semantics — the issue-thread bundle stays byte-identical at
index-Df3v8-4P.js, SHA-256 2e37dcb74d544ed103fdf6f22554de9e8747ab11067f7b3e8fb6c5811e38a528.

Guard the class of defect rather than the instance. `check:tracked-imports`
rejects any tracked source whose relative import resolves only against an
untracked file, and any `package.json` script whose entry point is untracked.
Ignored build output is excused via `git check-ignore`.

The same sweep found the second half of the trap: the repository-root
`.gitignore` swallows `packages/paperclip-runner/scripts/check-*.mjs` because
its `!scripts/check-*.mjs` negation is anchored to the root. Existing checks
survived only through `git add -f`. This package's `.gitignore` now re-includes
them so new checks cannot be silently dropped from a commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 23:02:58 +00:00
DottaandClaude Fable 5 31b1e228b3 fix(runner): re-record the Phase 7 UI matrix under the Inter font environment
The committed matrix was captured while fontconfig resolved the sans stack
to the DejaVu Sans fallback; ambient user-font churn then made Inter (the
stack's first choice) resolve, drifting all 24 shots at once. Re-recorded
under Chromium 151.0.7922.34 with Inter active, taught the recorder to
refuse to record or compare when Inter does not resolve distinctly, and
wrote the recording environment into the generated evidence index.

PAP-17001

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 22:57:07 +00:00
DottaandClaude Opus 5 b0f8b1e91e fix(runner): redact the public Phase 7 stream and bind sessions to a capability
Both findings from the exact-build security review (PAP-16983) against
`b73524ec2ac5`.

Sensitive information disclosure: every projected view carried raw
`provider_event` records and unredacted tool results, so one ordinary turn
handed an admitted browser provider protocol traffic, the echoed prompt,
provider thread/turn/item identifiers, model and token metadata, and complete
tool payloads. Redaction now happens where evidence is recorded, not where it
is rendered — `redactPhase7EvidenceData` is the only way an entry enters a live
session, so a raw payload never reaches the snapshot, the store, a frame, or a
log. `toPhase7PublicThreadView` then publishes an allowlisted DTO on every
response path, aliases provider-authored identifiers consistently, and scrubs
withheld provider identity. A streamed failure answers with a code and fixed
operator copy instead of the underlying message.

Cross-session BOLA: the only capability was minted once per gateway process, so
possession of a session id authorized reads and mutations of it. Each surface
now mints a per-browser capability, stores only its SHA-256 with the session,
and compares it in constant time on every session-scoped route. A valid id
without its capability is answered 404, exactly like an id that never existed.
`New chat`, a scenario POST, and reset rotate the capability after revoking the
caller's prior bindings.

Also normalises the stream frame cap to encoded UTF-8 bytes rather than UTF-16
code units, which the review flagged as low-severity hardening.

Each new test was observed failing against the reinstated rejected behaviour
(19 failed, 18 pre-existing passed) before passing against the fix.

Refs PAP-16991.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 22:40:32 +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
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 865d1a7234 fix(runner): close Phase 7 security gate
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-10 12:51:11 +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
DottaandPaperclip 6419af9448 feat(runner): consolidate accepted Phase 7 semantic policy
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-10 12:19:36 +00:00
DottaandPaperclip da0d32d74a fix(runner): precompile Phase 7 validators for strict CSP
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-09 20:32:56 +00:00
DottaandPaperclip 4e625a5dc2 fix(runner): allow Phase 7I proxy config load
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-09 17:16:03 +00:00
DottaandPaperclip 7d6ec9fbf3 fix(runner): support direct tailnet HTTP demo
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-09 17:10:17 +00:00
DottaandPaperclip 362754a633 fix(runner): pin Phase 7I proxy authority
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-09 16:19:49 +00:00
DottaandPaperclip 0566a9f3ca feat(runner): harden Phase 7I demo deployment
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-09 16:15:17 +00:00
DottaandClaude Opus 5 e72925a848 feat(runner): Phase 7I interactive scenario chat over the mock control plane
Adds a chat surface to the Phase 7 browser explorer where the board sends
prompts to a scenario and sees exactly how the mock Paperclip control plane
was used on every turn — the point of the phase is that the activity stays
visible rather than hidden behind the conversation (PAP-16916).

Runtime owns the turn model, so the UI derives nothing:

- `Phase7ChatSession` keeps one long-lived mock core across turns, so the
  state a turn changes is still there for the next one. Turn 0 is the
  control-plane seed (checkout, wake routing, exposure) that exists before
  anyone can type.
- Timeline entries gain a `turn`; the artifact gains `turns[]` with per-turn
  exposure, authorization records, state diff, reconciliation events, counts,
  and a turn-scoped verdict. A new `user_message` kind joins the five
  existing ones.
- `phase7TurnParity` judges a turn on what that turn did. A session-level
  expectation about work nobody has asked for yet would read as a failure of
  turn 1 rather than as work still to come.
- Recording machinery moves to `scenario-execution.ts`, shared with the 7F
  single-shot runner so a scenario reads the same either way.

Two scenarios carry authored multi-turn scripts: `ap-mcp-gate-01` (allowed
turn, then a denied one) and `ix-checkbox-01` (open an interaction, then the
board answers it — a control-plane-owned action with no agent tool, which is
what makes the control plane schedule the continuation wake). Every other
corpus scenario falls back to its 7F plan as one scripted turn.

The surface implements the approved 7I interaction map: turn strips bridging
each turn to its evidence, the six-section activity stream, the mobile
Chat/Activity segments with a deny count chip, reset and scenario-switch
confirmation, and deterministic deep links including a staged streaming hold.
Deviations taken during implementation are recorded in §11a of the map rather
than left for a reviewer to find.

No ACPX implementation, no real control-plane integration, no credential in
the browser: Codex mode stays disabled with a named reason and scripted mode
drives the same in-page mock core offline.

Tests: 30 new runtime/component tests and 18 browser tests; 79 phase-7 unit
and 43 phase-7 browser tests pass. Ten §11 screenshots recorded by
`record:phase7i`, which fails the run on any horizontal page scroll.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 15:56:47 +00:00
DottaandClaude Opus 4.8 ab7b85878a docs(runner): strip leaked generation wrappers, sync Phase 7 counts, guard against regressions
Remove trailing generation-wrapper markup (</content>, plus </invoke> in the
tutorial) that had leaked at EOF into eleven committed Phase 7 reference,
tutorial, evidence, and OKF-journal files.

Sync the stated suite counts in the current-state docs (tutorial,
scenario-explorer reference, verification-commands reference, and OKF journal)
to the verified results: test:phase7 49, test:browser:phase7 25,
docs:validate 73 files / 28 OKF concepts / 5 indexes. Drop a brittle wall-clock
timing from the journal. The dated 7F verification evidence and log.md milestone
entry keep their point-in-time 45/19 figures on purpose.

Add a wrapper-markup guard to docs:validate (via scripts/lib/doc-wrappers.mjs)
that fails with the exact file and line when generator envelope tags reappear,
plus a node:test regression suite wired into the package test script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-09 14:46:49 +00:00
DottaandPaperclip a8211dce1d test(runner): add Phase 7 eval conformance suite
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-09 14:05:17 +00:00
DottaandClaude Opus 5 fb006b75ca feat(runner): add Phase 7 browser scenario explorer
Implements the standalone, package-local Phase 7 scenario explorer against the
approved interaction map (docs/design/phase-7-scenario-explorer-ux.md).

Scenario runtime (src/phase7, no DOM, no network):
- Scenario index over all 106 eval cases and 16 groups, with actor role, task
  mode, fixture seed, and catalog claims declared in a profile table rather
  than inferred from case prose. Claims that cannot unlock an operation in a
  scenario's task mode or for its actor role are dropped, so no scenario shows
  a grant that unlocks nothing.
- Deterministic fixture seeds and fake-agent plans: twelve authored plans for
  the UX acceptance scenarios, derived plans for the rest. Derived plans only
  invoke operations the run actually exposes, so any denial in a derived
  transcript is a real policy result, not a scripting artefact.
- A run-artifact builder that executes each plan against the Phase 7C mock
  control plane through the Phase 7D authorization engine and records the
  ordered timeline (both channels), tool exposure, authorization records, an
  immutable ten-domain state diff, and a parity verdict. Descriptor redaction
  rules are applied at the artifact boundary so no raw secret can reach a
  rendered record.
- Parity is judged in the runtime, never in a component. Phase 7E's per-case
  result is carried through verbatim in its own labelled block and renders
  "Not run" when no report is bundled.

Explorer (examples/phase7-explorer):
- Read-only surface over the frozen SDK: hash routing, scenario picker with
  faceted filters, agent and control-plane transcript channels, and Context,
  Authorization, State diff, and Parity inspector tabs. Tokens only; the
  0.1.2 export surface is unchanged and the runtime is reached through the
  deliberately package-local @paperclip-runner-local/phase7 alias.
- Checked-in inputs arrive as declared virtual modules, so the example
  consumer keeps its boundary and fake mode does no network I/O.

Verification: 45 unit tests (index, plans, all-scenario runs and parity,
components, routes), 19 Playwright tests (route determinism, evidence routes,
credential boundary, keyboard paths, responsive), and the 24-image screenshot
acceptance set.

Fixes found while building: SDK badges overflowed at 390px inside disclosure
summaries; SDK badge capitalisation broke the label vocabulary; hash-change
deep links left the wrong mobile segment; screenshot capture leaked run state
between shots. All recorded as revisions in the interaction map.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 13:30:32 +00:00
DottaandPaperclip 4d97afc2ed feat(runner): generate normative Phase 7 inventory
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-09 12:33:39 +00:00
DottaandClaude Fable 5 5e0014a239 docs(runner): add Phase 7B scenario explorer UX spec and mockup
Interaction map, screenshot acceptance matrix, and annotated mockup for
the Phase 7 browser scenario explorer (PAP-16899). Binds picker facets,
transcript channels, inspector tabs, deterministic routes, and the
twelve-shot acceptance set to the 7A capability contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 12:29:21 +00:00
DottaandPaperclip cc945e4c72 feat(runner): extract Phase 5 browser SDK
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-08 07:42:57 +00:00
DottaandClaude Opus 5 cda1fb03ab fix(runner): preserve phase 4b request terminal and goal semantics
Five protocol corrections from the CTO review of the Phase 4b work.

Canonical runtime request outcome. The real driver emitted `resolution`
while the scripted driver and browser read `action`, so a real resolved
card reported an unknown outcome. `harnessRuntimeRequestOutcome` is now
the one payload every driver emits for a terminal `runtime_request.*`
fact, carrying `requestId`, `requestKind`, `turnId`, and `itemId` in the
payload as well as the event binding.

Decoded request identities. Path segments are split before they are
decoded, so a percent-encoded reserved character stays inside the segment
the browser meant it for; malformed encoding fails with 400.

Resolution shapes validated by request kind.
`parseHarnessRuntimeRequestResolution` requires `answers` for a
`user_input` submit and `content` for an elicitation submit, and rejects
a mismatched body instead of accepting it as an empty response. Both
drivers and the browser transport run it, and the request card now sends
elicitation answers as `content`.

One terminal fact per reconnect. The demo server snapshots after the
close rather than before it, so requests the close already cancelled are
not replayed into the driver's stale-request recovery path and reported
terminal a second time. Reading afterwards also carries the closing
events' source sequence into the resumed session.

Goal support retired only on a provider denial. A transport or protocol
failure during `thread/goal/get` said nothing about what the build
supports but still set `goals: false`. `CodexRpcError` now carries the
JSON-RPC code, and the capability is retired only when the provider
answers that it will not serve the method.

Three defects surfaced while verifying the above are fixed here too: a
same-origin `EventSource` GET sends no `Origin`, so requiring one on
`/stream` rejected the console's own subscription and no live-console
test could connect; the browser payload redactor covered only
`.paperclip` paths and leaked the app-server's `~/.codex/memories`; and
the browser suite shares one server across 19 sessions against an
interactive cap of 16, now sized through `PAPERCLIP_PHASE4B_MAX_SESSIONS`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 06:04:03 +00:00
DottaandPaperclip 405a4a4506 fix(runner): secure phase4b browser transport
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-08 05:43:13 +00:00
96092b832d feat(runner): add phase 4b live codex protocol console
Add the package-local browser tracer over the Phase 4b protocol boundary as a
new Live console mode in the standalone devtool.

Surfaces derive only from canonical PRP events and the shared reducer: live
transcript and composer, same-turn steering with stale-turn recovery, three
distinct interrupt races, inline runtime request cards, capability-gated goal
controls, parent/child lineage, reconnect, refresh, replay, and a redacted
protocol inspector.

Add a deterministic scripted driver and eleven demo chats behind the same demo
server routes the real Codex driver uses, so every protocol state is reachable
without a provider account and PAPERCLIP_PHASE4B_DRIVER=codex swaps in the real
session.

Adapt shadcn/ui and Vercel AI Elements source with zero new runtime
dependencies, and extend the boundary check to keep it that way.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 05:26:45 +00:00
DottaandPaperclip 62d384b383 feat(runner): add phase 4b protocol server
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-08 04:44:00 +00:00
DottaandPaperclip eef193508d fix(runner): stabilize warmed Codex isolation evidence
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-08 03:11:05 +00:00
DottaandPaperclip 8d4ffe7f2b fix(runner): isolate Codex credentials and hostile output
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-08 02:07:33 +00:00
DottaandPaperclip 2161ddcc4a feat(runner): add skillless Codex app-server tracer
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-08 01:33:36 +00:00
DottaandPaperclip b6ec51df04 feat(runner): add durable Phase 3 recovery transport
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-07 23:37:33 +00:00
DottaandPaperclip a9e6db06e0 fix(runner): address phase 2 tutorial QA
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-07 22:13:41 +00:00
DottaandPaperclip bfef34393c fix(runner): harden phase 2 local boundaries
Enforce loopback/same-origin transport and bounded browser resources, frame harness output before allocation, and reconcile terminal state from runner-owned facts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-07 21:32:24 +00:00
DottaandPaperclip f32331a567 feat(runner): add Phase 2 local harness loop
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-07 21:09:14 +00:00
DottaandPaperclip 1b51f95c6c feat(runner): add phase 1 static replay contract
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-07 19:13:00 +00:00
DottaandPaperclip a86edda579 fix(runner): establish phase 0 rust boundary
Add the production-direction Cargo workspace, make the Rust tracer the default Phase 0 path, and enforce shared-fixture parity with the TypeScript reference. Extend standalone boundary checks and record the correction in the implementation plan and OKF journal.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-07 18:12:48 +00:00
DottaandPaperclip 164e44ef4e feat(runner): add standalone phase 0 skeleton
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-07 16:33:01 +00:00