Skip to content

CONTRACT: feat(ui-server): prepare durable turn egress records - #2399

Merged
schlessera merged 6 commits into
mainfrom
feat/1665-durable-egress-records-r2
Oct 22, 2026
Merged

schlessera merged 6 commits into
mainfrom
feat/1665-durable-egress-records-r2

Conversation

@schlessera

@schlessera schlessera commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Refs #1665

Bottom layer of the #1665 stack: this PR, then #2644 (first-party path measurement), then #2645 (default-on for governed and strict Claude turns).

What changed

Adds the typed egress_connection governance event and an internal per-turn proxy recorder (prepareTurnEgress). Real completed and refused HTTP/CONNECT connections persist host, port, method, visible path, bytes, duration and outcome in SQLite, and an egress_denial is written for allowlist, deny and byte-cap refusals. A recorder failure cancels the owning turn. Outward destinations wait for the existing approval bridge; voice has no approval surface. A zero-byte max_bytes_per_turn is a valid policy that refuses before any upstream contact or credential release. No server path consumes the recorder in this layer; #2645 composes it.

Why this shape

This is the independent recording prerequisite from #1665. The payload is additive (CONTRACT:; server minor) and documented in docs/http-api.md and docs/integration-contract/http.md. It adds no public extension seam or credential authority.

Proof

Head 8406e0062 merges origin/main 4ba50d824 (#2671) into eded2a0da, which merged origin/main 46380183b into round 2's head 5861e4e5b. Both merges were clean (round 3 refresh for #2657, #2660, #2558, #2653, #2397, #2665, #2671), and they touch none of this layer's files.

Stack squash simulation (round 3). In a scratch worktree at origin/main 4ba50d824, each layer's own diff (parent head → layer head) was squashed on in order (#2399, #2644, #2645) with git merge-tree --merge-base. All three applied without conflict, and the final tree equals #2645's head tree. Each layer head also merges cleanly with origin/main on its own.

bun run check:pr --base origin/main

Result on 8406e0062: passed (exit 0). Lint, metadata and the fast phase pass (446 tests / 17 files), and import-graph feedback passes 3169 tests across 262 files.

The previous round 3 head eded2a0da failed one test that main already fails: turn-scope.test.ts › periodic capture resumes a productive worker and snapshot failure terminates its held worker (Scope fixture timed out). It failed the same way in isolation on bare origin/main 46380183b and on aeaaf7794, and is tracked as #2666. It passed in this run.

Round 2's result on 5861e4e5b: passed (exit 0), 3123 import-graph tests across 257 files.

Two earlier runs on this head each failed one test, Maintenance temporary storage is busy, while another preflight held the shared per-UID maintenance ledger. Both pass in isolation (mask-scratch.test.ts 14/14, brain-client.test.ts 35/35), and the third, uncontended run is the one above.

The receipts below were taken on round 1's head 757c0d957. The merge changes none of the files they guard (turn-egress.ts, egress-proxy.ts, governance/events.ts and their tests).

  • Focused: turn-egress, governance-events, egress-proxy, inference-egress and decision citations pass 87 tests / 444 assertions on this head. Strict types and lint are clean.
  • Mutations on this head, each restored byte-for-byte and re-run green (9/9):
    • The proxy rejects a zero ceiling again (< 0 → <= 0): TURN_EGRESS_ZERO_CAP_ADMISSION_VALID fails.
    • The recorder-failure controller.abort is dropped: TURN_EGRESS_FAILED_RECORD_ABORTS fails (expected true, received false) for both kinds.
    • The allowlist check in the proxy is bypassed: a real turn refusal durably retains not_allowlisted fails (expected 403, received 200). This is ui-sdk: give governed and strict workers a private network with a recorded, limitable egress proxy #1665's required "bypass the allowlist" receipt.

Checklist

  • Focused local preflight/behavioral receipts and complete selected hosted proof passed; actual head/base/job checkout evidence is in Proof. (Local preflight above; hosted proof is the coordinator's.)
  • Acceptance criteria from the issue are all met, or the gap is named below.
  • The PR and completed/advanced issues have verified release milestones under docs/process/github.md.
  • A changeset is included, or this changes nothing a consumer can see.
  • No personal data anywhere in the diff — the leakage gate covers the whole tree.
  • The integration contract index or relevant component is updated in this same commit, or nothing in the machine surface moved.
  • Docs under docs/ reflect the change, or nothing documented moved.
  • Provider history stays append-only (docs/decisions/provider-history.md): the provider-history guard passes, or nothing on a turn's path changed.

Anything left open

The remaining #1665 work is listed on #2645. This layer is partial and stays Refs.

🤖 Generated with Claude Code

@schlessera schlessera added this to the 0.44.0 milestone Oct 18, 2026
@schlessera schlessera added type: feat A new capability or a change in user-visible behaviour. area: ui-server Hono app factory: turn coordinator, auth, session catalog, routes, cron. contract Touches the integration contract: CONTRACT: commit, contract doc updated in the same commit. labels Oct 18, 2026
@schlessera
schlessera marked this pull request as ready for review October 22, 2026 17:21
@schlessera
schlessera marked this pull request as draft October 22, 2026 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui-server Hono app factory: turn coordinator, auth, session catalog, routes, cron. contract Touches the integration contract: CONTRACT: commit, contract doc updated in the same commit. type: feat A new capability or a change in user-visible behaviour.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant