Jev call copilot CurrencyTransfer research and architecture built 2026-09-25

Copilot — Milestone 1: replay proof of concept for CurrencyTransfer calls on Cloudflare + Jev

1. Overview

2. Requirements

Functional Requirements

Core (M1a)

Secondary (M1b)

Out of scope (M2/M3, §11)

Non-Functional Requirements

3. Technical Architecture

flowchart LR
  subgraph devbox
    EXP[("data/call-coach/*.jsonl.gz")]
    IMP["scripts/import-call-coach.mjs<br/>stitch v1 · redact v1"]
    EVAL["scripts/eval.mjs<br/>L1 L4 L6 (L5 M1b)"]
    POL["policy/src/*.json → scripts/build-policy.mjs<br/>→ scripts/publish-policy.mjs --bootstrap"]
    EXP --> IMP
  end
  subgraph CF["Cloudflare 694e4cd3… — Access on every hostname"]
    W["Worker jev-copilot (src/index.ts)<br/>/api/* · /ws/* · static assets (public/)"]
    DO[("DO CallSession (src/session/CallSession.ts)<br/>serial chain · step() · seek cache")]
    D1[("D1 copilot — migrations/0001_init.sql")]
    R2[("R2 copilot-raw — raw/ eval/ golden/")]
    GW["AI Gateway jev-copilot (logs off)"]
    AI[["Workers AI: typesafe/jev · @cf/deepgram/nova-3"]]
    PG["Pages ct-copilot-docs (site/build.mjs)"]
  end
  IMP -->|"POST /api/calls/import (service token)"| W
  POL -->|"POST /api/policy (service token)"| W
  W --> D1
  W --> R2
  DB["Browser dashboard (web/src → public/)<br/>owns the clock"] <-->|"WS /ws/calls/:id"| DO
  DB -->|"GET /api/calls/:id · /audio (Range)"| W
  DO -->|"env.AI.run('typesafe/jev', input, {gateway:{id}})"| GW --> AI
  DO --> D1
  EVAL -->|"POST /api/evaluate · WS replay"| W
  EVAL --> PG

Key components (real files; new files are named so stories can cite them).

Area Existing New in M1
Worker src/index.ts (router, evaluate() at src/index.ts:34-38, Env at :4-6), wrangler.jsonc (ai binding only) src/env.ts (typed Env), src/routes/{health,evaluate,calls,audio,moments,policy,upload,ws}.ts, src/auth/access.ts, src/db/d1.ts (chunkForD1), src/jev/{client,stub,types}.ts, public/ (built dashboard), web/src/ (dashboard TS)
Ingestion scripts/replay_via_cf.py:74-85 (ct_call, naive same-speaker stitch), data/samples/*.json (turns[]: {i,start,end,role,text}), exports (call_records, call_turns, call_features) src/ingest/{stitch,redact,normalize,stt,speakerMap}.ts, scripts/lib/call-coach.ts, scripts/import-call-coach.mjs, scripts/make-fixtures.mjs
Policy brief §4.3 (shared bank, lines 222-407), §4.4 (onboarding, 408-619), §4.5 (CS, 620-755), §5.1 (onboarding playbook, 772-935), §5.2 (CS playbook, 936-1090); scripts/replay_via_cf.py:26-35 (cf_safe_questions, the Score flattening rule) policy/src/{shared,onboarding,customer-success,verify}.json, policy/src/playbook-{onboarding,customer-success}.json, policy/src/rules-{onboarding,customer-success}.json (checklist, risk rules, weights, thresholds), policy/approvers.json, policy/REDPEN.md, src/policy/{types,canonical,build,loader}.ts, scripts/{build-policy,publish-policy}.mjs
Engine reference copilot/engine.py (build_state :185-199, extract_features :227-246, _update_facts :257-271, _update_objection :273-297, _coaching :299-320, _apply :322-361, recompute :369-382, snapshot :406-463, composite :61-81, ema :51-52, sensitivity :84-93); copilot/constants.py (masks :271-281, thresholds :326-336, EMA_ALPHA :319) src/engine/{types,features,facts,concern,stage,resolution,risk,amount,hero,card,step,moments,recompute,state,budget,request}.ts
Session reference copilot/server.py (run_replay :192-212, websocket :215-247, handle_message :250-308) src/session/CallSession.ts, src/session/protocol.ts (ClientMsg/ServerMsg from architecture §8.1)
Dashboard reference static/index.html (controls :130-160, panels :166-230, chart :241-260, WS :263-300); docs/design/reference-copilot-dashboard-{1,2}.jpg docs/design/DESIGN.md, docs/design/mockups/*.png, web/src/{app,api,clock,player,transcript,render,stage,concern,hero-onboarding,hero-cs,card,signals,telemetry,debug,mark,upload}.ts, web/index.html, web/app.css, scripts/build-web.mjs (esbuild), scripts/seed-local.mjs
Eval reference tests/test_integration_jev.py (check helpers :38-56, CASES :62-180, MIN_PASS_RATE :25), eval/cf-parity.json eval/labelled/{onboarding,cs}.json, eval/schema.json, scripts/eval.mjs, scripts/lib/{eval-cases,replay-client,eval-l1,eval-l4,eval-l6}.ts, scripts/cost-report.mjs, eval/reports/
Docs site/build.mjs (CATALOG :12, layout() :57, _headers :188) docs/demo-script.md, docs/reports/cost-latency.md, docs/runbook/ (one file per story; never published), docs/verification/M1.md

Data flow for one replayed utterance (architecture §3): browser clock passes utterances[i].t_end → {type:'utterance', i} → DO enqueues on this.tail → builds JevState (§6.2) → applies the token budget (which may shrink the window) → hashes the exact post-budget state → looks up (call_id, i, bank_hash, state_hash) in answers and (call_id, i, playbook_hash, state_hash) in move_answers → both hit: no Jev call; judging hit + move miss: moves-only request; else one fan-out request → budget drop order → store raw answers → step() → persist SessionState to ctx.storage + decision/moments to D1 → push decision.

Integration points.

Conventions every story follows.

4. Product Invariants

(INV-COPILOT-001 … INV-COPILOT-014 are all defined below; stories cite them by id.)

5. User Stories

Phase P0 — Foundations

Phase P1 — Ingestion

Phase P2 — Policy

Phase P3 — Engine (pure, src/engine/**)

Phase P4 — Session (Durable Object)

Phase P5 — Dashboard (Worker static assets, web/src → public/)

Phase P7 — M1b

6. Risks and Mitigations

# Risk Mitigation in this PRD
1 Messy Aircall speech vs literal v0 criteria: first L1 pass well under 90 %, uncertain common (architecture §13 risk 1) L1 is report-only in M1a (COPILOT-057), the 90 % target is the M1b gate (COPILOT-074) with failing cases listed for criteria repair; uncertain moments (COPILOT-032) feed the red-pen loop
2 Stevan's red-pen sitting blocks policy v1 (risk 5) COPILOT-020 ships policy/REDPEN.md early; every engine/DO/UI story tests on the fixture policy; only COPILOT-023 and the live replays wait on approvals; the loader never bypasses
3 The API token in ~/.config/jev/cloudflare.env lacks Workers/D1/R2/Access/Pages permissions COPILOT-005/COPILOT-006's scripts exit with the missing permission name; §7 Q3 asks for an Account API token with the listed scopes; nothing is deployed from the personal wrangler whoami account (account_id pinned)
4 Binding does not honour {gateway:{id}} (D17) COPILOT-008 day-1 probe; JEV_MODE=rest fallback with CF_AI_TOKEN secret
5 No model version pinning on Cloudflare (risk 2) model_expected, model on every answer, model_drift alert, L6 fails on drift
6 Token budget miscalibrated (risk 3, D25) divisor is policy data; COPILOT-060 measures it; L6 checks usage.input_tokens, not estimates; the hash is taken on the post-budget state
7 DO interleaving / hibernation (D21) explicit chain with generations + pending, per-step storage, ctx.abort eviction test and in-flight seek test in COPILOT-024
8 Stitch parameters are eyeballed; decision-point counts may be far from 40-80 COPILOT-013 reports counts per sample; constants live in STITCH_V1 and are re-tuned without touching the engine; a re-stitch is a new stitch_version + forced re-import with invalidation
9 Parallel otto stories collide on hot files §3 HOT FILES rule: one import + one dispatch/registration line, additive Env fields, frozen 0001_init.sql, HANDLERS map in the DO, registerPanel in web/src/app.ts, ordered appends to src/routes/eval.ts
10 Repo has no commits; otto worktrees need a base §7 Q10 — operator makes the initial commit before otto implement
11 Pseudonymisation misses a name or an identifier the regexes do not know unresolved proper-noun review on every import (COPILOT-012/COPILOT-018), tests/fixtures/names/ extras, env-gated raw-sample tests, assertRedactedText guard on every write path; fixtures never carry raw text
12 DPA status of Jev/Deepgram through Cloudflare Stevan signed off for pseudonymised text/audio in M1 (binding decision); M1 still runs on the four samples + synthetic cases; corpus expansion is M2
13 jev-lab worker is public and spends credits on /demo COPILOT-007 deletes it after confirmation (§7 Q8)
14 Story count (STORYCOUNT) exceeds the 25-40 guideline Splits adopted from two adversarial-check rounds (one commit each); phases and dependencies keep parallelism high; /prd-task-sizer may merge trivially small siblings at convert time
15 Runbook/probe notes edited by several stories one file per story under docs/runbook/ (gateway, access, deploy, jev-route, stt); the index is written last by the verification story

7. Unresolved Questions

Answered by Stevan on 2026-09-25 (supersede the matching questions below):

  1. Stevan's email for the Access Allow policy and policy/approvers.json — the account owner shown by wrangler whoami is Stevan@litobac.com; is that the Access identity, or a CurrencyTransfer address? Also the RM first names for policy/src/rep-first-names.json. Needed by COPILOT-006, COPILOT-012, COPILOT-020, COPILOT-023.
  2. Red-pen sitting — when will Stevan mark policy/REDPEN.md (confirm/rewrite/delete the 9 [VERIFY lines and approve the rest)? COPILOT-023 and every live replay wait on it; the executor will block with statusNote: 'awaiting red-pen approvals'.
  3. API token scope — does the token in ~/.config/jev/cloudflare.env carry Workers Scripts, D1, R2, Durable Objects, Pages, AI Gateway, Access (Apps/Policies/Service Tokens) and Workers AI edit permissions, and is it an Account token (guide §4.5)? If not, who mints a new one? COPILOT-005/COPILOT-006 report the first missing permission.
  4. Zero Trust team domain (<team>.cloudflareaccess.com) for JWT verification (ACCESS_TEAM_DOMAIN) — does one exist on account 694e4cd3…? COPILOT-004/COPILOT-005.
  5. Hosting — confirm jev-copilot.currencytransfer.workers.dev + ct-copilot-docs.pages.dev behind Access for M1 (binding decision says workers.dev); a currencytransfer.com subdomain is M2 (architecture §13 Q5).
  6. Retention for R2 raw/ audio and transcripts (lifecycle rule value) — COPILOT-005 sets none until answered.
  7. said_recording_disclosure — must calls be announced as recorded, and is that a checklist item? Shipped with applies: 'never' until answered (COPILOT-020).
  8. Delete jev-lab? It answers 200 unauthenticated and /demo spends credits (COPILOT-007).
  9. Design board sign-off — COPILOT-011 records a recommended variant; Stevan flips it to approved in docs/design/DESIGN.md (approval: approved <date> or accepted-recommendation <date> under §Approved variant) before COPILOT-045 runs; the executor blocks otherwise.
  10. Initial commit — the repo is git init only (no commits). otto worktrees need a base commit, and reference/jev-sales-copilot and reference/call-coach each contain their own .git, which a plain git add would record as embedded repositories (worktrees would then lack the cited source). The operator must run: rm -rf reference/jev-sales-copilot/.git reference/call-coach/.git (vendoring the reference as plain files), verify with git add --dry-run -A | grep -c '^add .reference/jev-sales-copilot/copilot/engine.py' printing 1, then git add -A && git commit -m "chore: initial import (vendored reference, data/ ignored)" before otto implement; data/ stays ignored and absent from worktrees (§3).
  11. client_type per call — inferred (unknown unless obvious) in M1; the checklist applicability rules for personal vs corporate wait on a source (architecture §13 Q9).
  12. Rewrite path — acceptable in onboarding, CS only, or neither (architecture §13 Q4)? COPILOT-075/COPILOT-076 ship off.
  13. Spend limit amount — USD 25/month is assumed for the jev-copilot gateway (COPILOT-005); confirm.
  14. Beneficiary / account-holder names in the CS-strong sample — the pseudonymiser needs them listed in data/names/3303259297.json (inside the git-ignored data/) before the import is trusted; Stevan or the executor reviews the unresolved list printed by --dry-run.

Adversarial-check notes (§7 addendum, maintained across otto check rounds).

8. Implementation Steps

  1. Operator: answer §7 Q1/Q3/Q4 (email, token scope, team domain), make the initial commit (Q10), then otto plan convert prd.md, /prd-task-sizer, otto implement N --llm claude.
  2. P0 Foundations: COPILOT-001 → 002 → 003 → 004; 005 and 006 after 002 (parallel); 007 (first deploy via scripts/deploy.mjs, behind Access, then the secret) after 004 + 005 + 006; 008 → 009 → 010 after 007 (010 also needs 012); COPILOT-011 (design board) right after 001.
  3. P1 Ingestion: COPILOT-012 (redact) → 013 (stitch + pseudonymised fixtures) → 014 → 015 → 016/017 → 018 → 019.
  4. P2 Policy: COPILOT-020 (opus, early, after 012) → 021 → 022; COPILOT-023 waits for Stevan's red-pen and for the state builder (030).
  5. P3 Engine (opus, pure): COPILOT-024 → 025/026/027 (parallel) → 028 → 029 → 030 → 031 → 032 → 033/034.
  6. P4 Session: COPILOT-035 → 036 → 037 → 038 → 039 → 041/042 (parallel) → 040 (seek + live replay) → 043.
  7. P5 Dashboard (opus, all against the approved DESIGN.md, light + dark evidence): COPILOT-044 → 045 → 046 → 047 → 048 → 049/050 → 051/052 → 053 → 054 → 055.
  8. P6 Eval and deliverables: COPILOT-056 (opus, early) → 057 → 058 → 059 → 060 → 061 → 062 → 063; M1a demo on call-3339895706 after 046 + 058.
  9. P7 M1b: COPILOT-064 → 065 → 066 → 067/068 → 069 → 070/071 → 072; COPILOT-073 → 074 (promotion gate, removes bootstrap); COPILOT-075 → 076 → 077 (stretch, off by default).
  10. COPILOT-078 final verification: register every live probe with quoted output, assemble the runbook index, replay the four samples on the deployed Worker, publish the docs.
33,695 words · Internal working documents. Do not share outside CurrencyTransfer.