Jev.
A new kind of AI is going viral, from one of the co-creators of ChatGPT.
He built Jev:
In simple words:
- The problem: chatbots often feel slow because they must think and write their answers one word at a time. Like asking someone to write a full paragraph just to say yes or no.
- Then the app has to read that paragraph, pick out the answer, and hope the chatbot didn’t invent something weird (= hallucinations).
- Jev is a fresh new take, skipping the writing. You give it a situation, ask a clear question with options (like an MCQ), and it comes back with a decision plus how confident it is.
- Jev’s comparison with other chatbots is… fascinating. 200x faster. 400x cheaper. Actually so cheap you will hardly find a way to spend $5.
- So Jev is not a better chatbot. More like a smart yes/no button that takes decisions hundreds of times per second, for almost $0.
Here are great examples of how LLMs (like ChatGPT or Claude) answer, compared to Jev:
So how can we best use Jev?
This newsletter will show you:
- How to install Jev.
- Jev to classify my Linkedin.
- Jev to go through my Gmail.
- Jev to help me research papers.
- How you can use Jev for your work.
Each case study has an easy copy&paste prompt for Claude or ChatGPT.
Before diving in, take a second to share this with your colleagues so you can answer: why is everyone obsessed with Jev?
0. How to install Jev.
First, open a Typesafe account. It’s free.
Go to typesafe.ai. Click at the top right.
- Then go to Claude Code or Codex.
- Choose Opus 5-Low or Sol-Low.
- Copy and paste this prompt:
[claude or codex] plugin marketplace add typesafe-ai/skills
[claude or codex] plugin install typesafe@typesafe-aiYou don’t need to add your credit card to get the $5.
And you will see at the end of this newsletter, it’s impossible to spend it. If I wanted to spend $5, I would need to ask Jev 500,000 questions. I won’t.
We are now all set. Let’s dive in.
1. Jev for Linkedin.
Why? I have 38,000 connections & invitations on Linkedin. I could ask Claude/ChatGPT to filter them all, but it would take too long & cost too much.
On Wednesday, I launch a new company, ‘GPC’. I need to find the couple of hundreds of people to message.
Jev does it for $x in 0.9 seconds.
What you need:
- To extract your Linkedin contacts & invitations first.
- To connect your Claude or Codex to GitHub + Vercel + Apify.
- You do need to create an Apify API key (to enrich your data).
Linkedin will send you an email with a zip file. Open it. Get the “Invitations” and the “Contacts” CSV files. You will upload them.
Then make sure your Claude/Codex is connected to GitHub & Vercel. I wrote a guide about it here.
We also connect Apify to find the public information about each Linkedin profile.
If you’re on Claude Code, use the “Plan” mode first.
The prompt (it’s quite long, so scroll to see the result):
Use the TypeSafe skill (/typesafe:typesafe-ai) to design every Jev question and review the thresholds. Build, test, deploy, and report back in plain English; I do not read code. Use the GitHub, Vercel and Apify connectors.
BUILD
A private web app, "LinkedIn Radar". The user uploads their own LinkedIn exports in the browser — no files are attached to this conversation and none exist on disk. Two tabs: CONNECTIONS and INVITATIONS. Every row is scored live by TypeSafe's Jev, and the user can rewrite the classification questions and thresholds inside the app.
INPUT SCHEMA — build against exactly this, verify at upload time
Connections.csv: a "Notes:" preamble of several lines precedes the header row "First Name,Last Name,URL,Email Address,Company,Position,Connected On". Skip to that header. Dates are "18 Sep 2026". Email Address is populated on a small minority of rows. Company and Position are empty on some rows.
Invitations.csv: header "From,To,Sent At,Message,Direction,inviterProfileUrl,inviteeProfileUrl". Dates are "9/18/26, 1:20 AM". Direction is INCOMING or OUTGOING. Message is empty on most rows.
Join key: an OUTGOING inviteeProfileUrl present in Connections = accepted; an INCOMING inviterProfileUrl absent from Connections = still pending.
UPLOAD
The app opens on an empty state with two labelled upload slots, "Connections.csv" and "Invitations.csv", each with a drop target and a file picker. Under each slot, a three-line reminder of how to export that file from LinkedIn (Settings → Data privacy → Get a copy of your data). Either file may be uploaded alone; the corresponding tab activates when its file is present, and the Invitations tab shows accepted/pending matching only when both are loaded.
On drop, identify the file from its header row regardless of filename; if a file is dropped in the wrong slot, move it and say so. Show parsed row count against raw line count, the detected date format, the number of rows with empty Position or Company, and the number of invitations without a message. Reject a file whose header matches neither schema with a message naming the expected columns.
After upload, the slots collapse into a compact bar showing both filenames and counts, with "Replace" on each. Parsing happens entirely in the browser; the files are never sent to the server.
FIELD CONTRACT
Fields Jev may read before enrichment:
CONNECTIONS — position, company, name_suffixes (credentials following the last name, passed only when present), connected_for (code-computed bucket from Connected On: "under 1 year" / "1–3 years" / "3+ years").
INVITATIONS — name, message, invitation_age (code-computed bucket: "this week" / "this month" / "older").
Computed in code and never asked of Jev: has_email, all date arithmetic, direction, accepted/pending matching, all counting, all tier math.
Absent from both files and forbidden in any pre-enrichment question: location, country, company size, industry codes, revenue, photo, headline, activity, mutual connections.
Each question declares which contract fields it reads. The app refuses to run a question naming a field absent from the uploaded file, and the build fails if any default question references a field outside this list. Render the contract beside the question editor.
INCOMING invitations with an empty Message are not sent to Jev. They go to a "No signal" bucket sorted by recency, from which the user can select rows for enrichment.
ICP CONFIG — one config file, editable in the app, shipped as the default preset
Company: [your company and what it sells]
Target roles: [e.g. Chief of Staff, COO, CEO; President / Managing Director / Owner at family-owned companies]
Geography: [e.g. United States first; other countries rank lower, not excluded]
Company profile: [e.g. privately held or family-owned; manufacturing, industrial, distribution, construction, logistics preferred; over $50M revenue or 150+ employees when revenue is unknown]
Disqualifiers: [e.g. students, job seekers, recruiters, agencies selling to me, founders of pre-revenue startups]
Derive the option lists and criteria of the default questions below from this block, keeping the same structure.
DEFAULT QUESTIONS — all editable in the app
CONNECTIONS, one request per row, state = {position, company, connected_for, name_suffixes?}:
- role: Choice over the target roles plus ["other_executive","not_executive","unknown"], one criterion per option. Instructions must direct "unknown" when Position is empty or too vague.
- company_type: Choice over the preferred industries plus ["professional_services","tech_or_software","finance","healthcare","education_or_nonprofit","other","unclear"]. Instructions must state the judgment comes from the company name and title text only, and that "unclear" is correct when the name carries no industry signal.
- likely_private_or_family: Noul "The company name suggests a privately held or family-owned business (a surname, '& Sons', 'Group', 'Holdings', a regional or trade-specific name) rather than a well-known public corporation."
- is_big_public_brand: Noul "The company is a well-known large public corporation."
- disqualified: Noul "The position text shows this person matches one of the ICP disqualifiers."
INVITATIONS, rows with a non-empty Message only, state = {name, message, invitation_age}:
- message_intent: Choice ["genuine_networking","wants_to_sell_me_something","wants_a_job_or_mentoring","fan_or_learner","spam_or_bot"].
- self_described_seniority: Choice ["c_level_or_owner","director_or_vp","manager_or_ic","student_or_junior","not_stated"], read only from what the message or name credentials state.
OUTGOING invitations: no Jev call.
Post-enrichment questions, second pass, state = enriched fields: in_geography (Noul, from the ICP geography), revenue_over_threshold (Score: "Almost certainly under" / "Probably under" / "Unclear" / "Probably over" / "Almost certainly over", threshold from the ICP), family_business (Noul), decision_maker (Noul, approval authority for an engagement of the size the ICP implies), active_on_linkedin (Choice ["posted_in_last_30_days","posted_in_last_6_months","dormant","unknown"], from explicit activity fields only). Retain both passes per row.
CLASSIFIER STUDIO — a page in the app
1. Each question rendered as a card: id, type, instructions, criteria — option list with per-option descriptions for Choice, ordered levels for Score, statement for Noul.
2. Inline editing of every field: rewrite instructions, rename, add, remove and reorder Choice options, edit Score levels, add, duplicate, disable or delete a question, convert a question between Choice, Score and Noul.
3. Per-question field selection, offered as checkboxes built from the uploaded file's actual columns and nothing else.
4. Validation while typing, with inline errors: Choice 2–255 options each with a criterion, Score 2–10 ordered levels, Noul statement with no criteria, at least one field selected per question. Warn on instructions that contradict their criteria, and on any field missing from the current upload.
5. "Test on 25 rows": runs the edited questions on a random sample and shows previous answer and confidence beside new answer and confidence, with flipped rows highlighted.
6. Threshold sliders for every tier rule, recomputing tier counts instantly from stored probabilities with no new Jev calls, labelled as such.
7. Named presets: save, switch, download as JSON, upload. Ship the ICP CONFIG as the default preset. Reset-to-defaults button.
8. "Re-run all with this preset", preceded by a token and cost estimate.
The scoring route validates any question spec it receives against these same rules and rejects fields outside the contract.
TIERS — code-computed, slider-adjustable, defaults below
Connections Tier 1: role in target roles with confidence >= 0.7, disqualified < 0.3, is_big_public_brand < 0.5.
Tier 2: target role with confidence 0.5–0.7, or role "unknown" with company_type in the preferred industries, or likely_private_or_family >= 0.6 with any executive role.
Tier 3: remainder, not disqualified. Rejected: disqualified >= 0.7. Rows with empty Position resolve to "unknown" and land in Tier 2, never Rejected.
Invitations — Accept: message_intent genuine_networking or fan_or_learner with self_described_seniority in the senior buckets. Ignore: spam_or_bot or wants_to_sell_me_something >= 0.7. Review: remainder. No signal: message-less rows.
Final ranking after enrichment: decision_maker × revenue score, ICP geography first.
ARCHITECTURE
Next.js App Router, TypeScript, Tailwind, deployed on Vercel. No database. CSVs are parsed in the browser and leave it only as scoring batches to the app's own API route. Results and presets persist in IndexedDB. CSV download per tier and per tab.
/api/score: accepts up to 50 rows plus the active question spec, calls Jev via @typesafe-ai/sdk with model "jev-latest", all questions for a row in one request, 8 rows in parallel, exponential backoff on 429 and 529, returns answers, probabilities, confidence, latency and usage.input_tokens per row. TYPESAFE_API_KEY is a Vercel environment variable, never exposed to the browser or committed.
The browser iterates batches and repaints after each one. Pause and Resume controls. Request size stays small so runs of any length fit Vercel's function limits.
/api/enrich: before writing this route, use the Apify connector to search actors and select one LinkedIn profile scraper that accepts a list of profile URLs per run, requires no LinkedIn cookies, and returns at minimum photo, headline, location and current company, preferably company size, industry and last activity. Read its input schema and pricing, test it against five public profile URLs, and record the actor ID and input mapping in one config file. The route starts runs of 100 URLs via the Apify REST API with APIFY_TOKEN as a Vercel environment variable, polls, and streams dataset items back as they arrive. When the actor returns a company URL without size, run a cheaper company actor for the distinct Tier 1 companies only. Show selected count and estimated cost and require confirmation before any run. Include an "Import enrichment JSON" drop zone as a fallback path.
UI
Editorial design: one large serif headline, small uppercase labels, black and white with a single accent colour, thin horizontal probability bars, dense avatar grid, generous whitespace. Dark and light mode. Desktop first, usable on a phone.
1. Top bar: the collapsed upload bar, Run and Pause, and six live tiles — ROWS READ, TYPED ANSWERS (rows × questions), TIER 1 SO FAR, ANSWERS / SEC, ELAPSED, COST SO FAR (summed input tokens × $0.042 per million, four decimals).
2. Left: avatar grid, one tile per person, initials before enrichment and the photo after. Tiles colour by tier as they are scored: Tier 1 accent, Tier 2 dark, Tier 3 grey, Rejected faded. Click to inspect.
3. Right, upper: "VERIFYING PROFILE" — the row currently scoring, or the clicked row: name, position, company, connected-for, then one line per question with the chosen answer, a bar for its probability or confidence, request latency in ms and tokens used. Updates continuously during a run. Each line carries an "Edit this question" link into the Studio.
4. Right, lower: live distributions — role mix, company-type mix, connected-for histogram; after enrichment, country, company-size bands, activity mix.
5. Combinable filters: tab, tier, role, company type, country, company-size band, activity, connected-for range, has email, private/family flag, free text. Table view with the same filters, each row linking to the profile, with locally saved "DM sent" and "Accepted" checkboxes.
6. Methods page rendering the active preset verbatim — questions, criteria, thresholds — plus the field contract and the selected Apify actor.
TEST
Generate synthetic fixture files matching both schemas exactly — preamble, both date formats, empty Position and Company rows, INCOMING and OUTGOING rows, messages on a minority of rows, invitee URLs overlapping Connections — and run the app end to end against them.
Verify: the preamble is skipped, parsed count matches raw line count, both date formats parse, a file dropped in the wrong slot is moved, a file matching neither schema is rejected, empty-Position rows resolve to "unknown" and reach Tier 2 rather than Rejected, message-less invitations never reach Jev, outgoing invitations match correctly against Connections, threshold changes recompute without new Jev calls, an invalid question spec is rejected by the API route, and no fixture or personal data is committed.
Report the fixture results before deploying.
DEPLOY
Private GitHub repo "linkedin-radar", connected Vercel project, TYPESAFE_API_KEY and APIFY_TOKEN set as environment variables — ask for the values, never print them. Deploy, confirm the production build accepts a fixture upload and completes a run, and send the URL.
README.md: how to export Connections and Invitations from LinkedIn, where to add keys, how to swap the Apify actor, how to edit the ICP config, how to share a preset.
RULES
Jev judges meaning only, and only from contract fields. Code owns all counting, dates, matching and tier math.
"unknown", "unclear" and "not_stated" remain available answers, and instructions state when to use them.
No pre-enrichment question may infer location, company size, revenue or activity.
The app sends no messages and takes no action on LinkedIn.
Uploaded data stays in the browser and the app's own functions; nothing is committed or logged; nothing reaches any third party except Jev (contract fields) and Apify (selected profile URLs).
Stop and report if TYPESAFE_API_KEY, APIFY_TOKEN, or the GitHub, Vercel or Apify connector is unavailable.Paste it, run it, and you will get this website:
I then made a second website to fix the invitations bug:
Sharing is caring. It’s also free!
If you don’t want to build the app and just want to use the app, upgrade your subscription today. I will be sharing the app for free with my paid members.
2. Jev for Gmail.
Why? I can’t go through all of my Gmail contacts. But I have an important launch next Wednesday, and I want to email the right people.
What you need? Extract all of your Google contacts. Go to https://contacts.google.com/u/0/other and select all and export all.
Upload it to Claude Codex/ Codex.
The prompt:
Use the TypeSafe skill for everything that touches Jev in this project.
## What we are building
A single-page web app called "Contact Cleaner" that helps a non-technical person clean their Google "Other contacts". Google collects thousands of these addresses automatically: everyone who ever emailed you, every newsletter, every student ID that hit reply-all. Most are junk. The app reads the CSV Google exports, asks Jev a fixed set of typed questions about every contact, and sorts them into three buckets the user can review and export.
The app is for people who have never opened a terminal. Every screen must be self-explanatory.
## Stack
- Next.js (App Router) with TypeScript and Tailwind. No database. Nothing is stored server-side. Contacts live in browser memory for the session only.
- Jev through the TypeSafe JavaScript SDK (`@typesafe-ai/sdk`), called only from a server route so the key never reaches the browser. Read `TYPESAFE_API_KEY` from the environment. If that variable is missing and `AI_GATEWAY_API_KEY` is set instead, call Jev through the Vercel AI SDK `experimental_evaluate` with model `typesafe-ai/jev`, and read confidence from `providerMetadata.typesafe.confidence`. Support both behind one internal function so the rest of the app does not care.
- Git repo pushed to GitHub, deployed on Vercel. Walk me through creating the repo and the Vercel project at the end, and tell me exactly where to paste the API key in Vercel's environment variables.
## The input file
The user uploads the CSV Google produces from Contacts > Other contacts > Export > Google CSV. Real files have these columns, in this order:
First Name, Middle Name, Last Name, Phonetic First Name, Phonetic Middle Name, Phonetic Last Name, Name Prefix, Name Suffix, Nickname, File As, Organization Name, Organization Title, Organization Department, Birthday, Notes, Photo, Labels, E-mail 1 - Label, E-mail 1 - Value, E-mail 2 - Label, E-mail 2 - Value
Expect the file to be sparse. In a real 4,710-row export, about half the rows had a first or last name and every row had one email. Organization, title, notes, birthday, and photo were empty in every row. Design for that: the name and the email address are the only signals we have. Also expect a UTF-8 BOM, emoji in name fields, addresses that are pure digits before the @, and occasional extra "E-mail N" columns. Parse with papaparse. Deduplicate on lowercased email. Parse client-side, then send to the server in batches.
## Do the deterministic work in code first
Jev is bad at counting, math, and string comparison, so compute these facts per contact in code and pass them to Jev as plain fields:
- `first_name`, `last_name` (trimmed, may be empty)
- `email`, `local_part`, `domain`, `tld`
- `local_part_is_all_digits` (boolean)
- `local_part_digit_share` bucketed as a word: "none", "some", "mostly", "all"
- `is_freemail_domain` from a hardcoded list (gmail, googlemail, yahoo, hotmail, outlook, live, icloud, me.com, proton, protonmail, aol, gmx, yandex, mail.com, zoho)
- `is_known_disposable_domain` from a hardcoded list of common throwaway providers
- `local_part_matches_role_pattern` regex over: noreply, no-reply, donotreply, notifications, notification, newsletter, news, digest, mailer, bounce, support, info, hello, team, sales, billing, admin, contact, marketing, updates, alerts
- `name_appears_in_email` (does first or last name, lowercased, appear inside the local part)
- `contacts_sharing_this_domain` bucketed as: "1", "2 to 5", "6 to 20", "more than 20"
Only send Jev the fields above. Do not send the whole row.
## Jev design rules
Follow these exactly:
1. One Jev call per contact. Ask every question in that one call. Never chain calls per contact.
2. Every question and every threshold lives in ONE file: `lib/jev-questions.ts`. Nothing else in the codebase contains a question string or a threshold number. I will edit that file with you.
3. Write the full question in `instructions`. The key name is not sent to the model.
4. Every Choice has an `other` or `unclear` escape option.
5. Describe situations in criteria, not degrees. Say what belongs to each option and what does not.
6. Use backticked field paths in instructions, like `local_part` and `first_name`.
7. Pin the model version in the request once we have tuned thresholds. Log the versioned model ID that answered.
The questions to ask about each contact:
- `contact_kind` (Choice): what kind of sender is this address? Options: `individual_person` (a real human's personal or work address), `role_inbox` (a shared company inbox like info@, sales@, support@), `automated_sender` (noreply, notifications, newsletters, system mail), `institutional_id` (a student or employee number used as an address, digits before the @, at a university or large institution), `throwaway_or_spam` (disposable providers, random character strings, obvious spam), `unclear`.
- `name_looks_like_a_real_person` (Noul): does the combination of `first_name` and `last_name` read as a real human name? An emoji, a brand, initials only, or empty fields all mean no.
- `domain_kind` (Choice): `personal_freemail`, `company_or_organization`, `university_or_school`, `government`, `marketing_or_email_platform` (mailchimp, sendgrid, substack, beehiiv, hubspot and similar), `disposable_or_suspicious`, `unclear`.
- `looks_like_a_professional_contact` (Noul): does the address look like someone at a company or organization the user might do business with?
- One optional custom Noul. The UI has a text box labeled "Add your own yes/no filter" with a placeholder like "Is this person likely based in Israel?" or "Does this look like someone in tech?" If filled, add it as a fifth question named `custom_filter`. If empty, do not send it.
Bucketing logic (in code, in the same file as the questions, with the thresholds as named constants):
- REMOVE if `contact_kind.choice` is `automated_sender`, `institutional_id`, `throwaway_or_spam`, or `role_inbox`, AND `contact_kind.confidence >= KIND_CONFIDENCE_FLOOR`.
- KEEP if `contact_kind.choice` is `individual_person` AND `contact_kind.confidence >= KIND_CONFIDENCE_FLOOR`.
- REVIEW for everything else, including anything with `unclear` and anything below the floor.
- Start `KIND_CONFIDENCE_FLOOR` at 0.5. We will tune it against labeled rows.
- Also compute a `professional` flag from `looks_like_a_professional_contact.noul >= PROFESSIONAL_FLOOR` (start 0.7) so the user can filter Keep down to work contacts.
- Show the `custom_filter` probability as its own column when present.
## Throughput and cost
Process in batches of 50 contacts per server request, with up to 25 concurrent Jev calls inside each batch. The SDK retries 429 and 529 with backoff; do not write your own retry loop. Show a live progress bar with contacts processed, elapsed time, and running cost, computed from the returned `usage.input_tokens` at $0.042 per million input tokens. A 5,000-contact file should finish in well under two minutes and cost a few cents. Display the final total.
## UI
Three screens, one page:
1. Upload. A drop zone, a short "how to export from Google" note with the three steps (Other contacts, select all, three dots, Export, Google CSV), the optional custom filter box, and a Start button. Show row count and duplicate count after parsing, before starting.
2. Processing. Progress bar, cost counter, and a live tally of Keep / Review / Remove.
3. Results. A table with columns: bucket, name, email, contact kind, confidence, domain kind, professional flag, custom filter (if any). Filter chips for each bucket. Sort by confidence. Group-by-domain toggle so the user can remove an entire university or spam domain in one click. Users can drag any row to another bucket. Nothing they do calls Jev again.
Export buttons:
- "Download Keep as Google CSV": same 21 columns as the input, same order, so it re-imports into Google Contacts cleanly. Set the Labels column to `* myContacts ::: Cleaned by Contact Cleaner` so imported rows land in the main Contacts list with a label.
- "Download Remove list": a plain CSV of emails so the user can find and delete them in Google.
- "Download full results": everything, including all probabilities and confidence, for their records.
Plain language throughout. No jargon on screen. Explain what confidence means in one sentence with a tooltip.
## Before you write code
Show me the plan and the full contents of `lib/jev-questions.ts` first. Wait for my approval. Agents write mediocre questions; we will fix them together before anything else is built.
## Validation
After the app runs end to end:
1. Add a script `scripts/eval.ts` that reads `eval/labeled.csv` (columns: email, first_name, last_name, expected_bucket), runs the same questions, and prints accuracy per bucket plus a table of where confidence and correctness disagree.
2. Take 40 rows from my real CSV that cover every bucket, put them in `eval/labeled.csv` with your best guess of the expected bucket, and ask me to correct the guesses.
3. Run the eval, then propose changes to the criteria or the floor. Do not change them without showing me before and after numbers.
## Privacy
Contacts never leave the user's browser except for the fields listed above going to the server route, which forwards them to TypeSafe and discards them. No logging of names or emails on the server. Say this in the footer in one sentence.
## Deliverables
- Working app running locally
- `lib/jev-questions.ts` reviewed and approved by me
- Eval script with results on my 40 labeled rows
- GitHub repo pushed, Vercel project deployed, env var set, live URL working
- A README for non-technical users: how to export from Google, how to get a key, how to deploy their own copy with one clickHere’s the result:
Share it with the guy who has 4,630 unread emails.
3. Jev for research.
Why? I can’t read 200 new AI papers, published every day. I need to filter them by category and interest. Jev can do it for me.
If you’re into real estate, it can do the same for you, for real estate.
If you’re in a heavily regulated industry, it can classify a large corpus (like laws?) and classify them.
If you’re in sales, the sky is the limit to rank potential leads.
The prompt (it’s an example for AI papers, but switch it with your own case study):
Use the TypeSafe skill (/typesafe:typesafe-ai) for every classification. You write and run the code (Python, typesafe-sdk, model "jev-latest"); I only read results.
QUESTION I WANT ANSWERED
Which arXiv papers from the last 6 months actually help a NON-technical person who uses Claude (or any LLM) in a chat window do better work — research, writing, analysis, finding information, decision-making — not coding, not training models? I want the prompting techniques and the LLM caveats/limitations they should know about.
PLAN
1. Pull metadata (title, abstract, authors, all categories, primary category, first-submitted date, arXiv ID, link) for every paper first submitted in the last 6 months whose categories include cs.AI, cs.CL or cs.HC. Do NOT use the search API for this — it is built for queries, not bulk, and paging past a few thousand results is unreliable. Use arXiv's bulk harvesting interface instead:
- OAI-PMH at https://oaipmh.arxiv.org/oai with verb=ListRecords, metadataPrefix=arXiv, set=cs, from=. Follow the resumptionToken until it is empty; each page is ~1,000 records. Respect a 503 with Retry-After, wait at least 3 seconds between requests, and write every page to ~/jev-case-studies/arxiv/raw.jsonl as it arrives so the harvest is resumable if it dies. The "from" filter is by last-update date, so the harvest will include older papers that were revised recently — filter in code on the first version's date and on categories containing cs.AI, cs.CL or cs.HC. Expect roughly 60–90k records harvested and 40–60k kept after filtering. Dedupe on arXiv ID (strip version suffixes).
- Fallback if OAI-PMH is down or blocked: download the Cornell "arXiv Dataset" metadata snapshot from Kaggle (a single JSON-lines file, refreshed weekly) and apply the same filters locally. Tell me if you fall back, and why.
- Sanity check before moving on: pick one week, count the kept cs.AI papers for that week, and compare against the number shown on arXiv's own listing page for cs.AI for that week. If you are off by more than 10%, stop and tell me.
Print the kept count per category per month.
2. STAGE 1 (Jev, all papers) — the "technical filter". State = {"title", "abstract"}. Ask at once:
- primary_contribution: Choice ["training_or_pretraining","model_architecture","fine_tuning_or_alignment_method","infrastructure_or_efficiency","benchmark_or_dataset","prompting_or_interaction_technique","evaluation_of_llm_behavior_or_limits","human_ai_interaction_study","application_in_a_domain","survey_or_position","other"] — one crisp criterion per option, written so that "how to talk to a model at inference time with no code" is clearly different from "how to build or train a model".
- needs_code_or_model_access: Noul "Applying this paper's main idea requires writing code, fine-tuning, or access to model weights or logits — it cannot be done by typing into a chat interface."
- about_llm_behavior: Noul "The paper reports how LLMs behave, fail, or can be misled (hallucination, sycophancy, bias, prompt sensitivity, position effects, overconfidence, context-length effects, etc.)."
Drop: primary_contribution in the first four options with confidence >= 0.7. Keep everything else for Stage 2. Report how many were dropped and why.
3. CHECKPOINT: run Stage 1 on 50 random papers first, show me title + Jev answers + confidence, wait for my OK, then run all with 8 workers and backoff.
4. STAGE 2 (Jev, survivors). Same state. Ask at once:
- chat_user_usefulness: Score ["Useless to a chat user","Marginal","Somewhat useful","Directly actionable in a chat window","Changes how a chat user should work"]. Instructions must say: judge only what a person with no coding skills could apply by changing how they write prompts, structure conversations, verify outputs, or choose tasks.
- topic: Choice ["prompt_structure_and_wording","reasoning_and_step_by_step","role_and_persona","examples_and_few_shot","context_and_long_documents","verification_and_self_checking","hallucination_and_factuality","bias_and_sycophancy","multi_turn_conversation_strategy","tool_and_search_use_from_chat","writing_and_editing","research_and_summarization","decision_support_and_analysis","safety_and_misuse_caveats","other"].
- audience_fit: Choice ["general_knowledge_worker","researcher_or_analyst","writer_or_marketer","manager_or_executive","educator_or_student","developer_only"].
- evidence_strength: Score ["Opinion / anecdote","Small or single-model study","Multi-model or large controlled study"].
- generalizes_beyond_one_model: Noul "The findings are claimed or shown to hold across several LLM providers, not just one model."
5. Rank in code: keep chat_user_usefulness score >= 3 with confidence >= 0.6; sort by usefulness × evidence_strength. Everything with confidence < 0.6 goes to a "Borderline" tab with the topic's parent bucket only ("technique" vs "caveat"), following the confidence-broadening pattern from the TypeSafe classification cookbook.
6. For the top 150 only, YOU (Fable, not Jev) read the abstract and write: a 2-line plain-English "what to do differently in your next Claude chat", and a 1-line "watch out for". Cite the arXiv link. Do not paraphrase the abstract; translate it into an action.
7. OUTPUT
a. Google Sheet "arXiv → Claude chat users (Jev)" with tabs: Top 150 (with your takeaways), All survivors, Borderline, Dropped sample (300 random), By topic (pivot counts), Run log. Share with me@rubenhassid.ai.
b. GitHub repo "arxiv-for-chat-users" deployed to Vercel: a browsable page filterable by topic, audience and evidence strength, reading the published CSV; a "Techniques vs Caveats" split view; a funnel chart (papers in → after technical filter → useful → top 150); and a methods page showing the Jev questions verbatim.
c. ~/jev-case-studies/arxiv/summary.md answering my question directly in one paragraph, then counts, the top 10 techniques and top 10 caveats as one-liners, sample precision, total Jev tokens and dollars (expect a few dollars), and time.
RULES
- Jev decides categories; code does all counting, dates, ranking. Fable writes prose only for the top 150.
- Abstracts only — never download PDFs in this run.
- Respect arXiv's rate limits; cache everything so a rerun is free.
- Stop only for missing credentials/connectors or the 50-paper checkpoint.Here’s the result:
348 papers analyzed for $0.18 in 3-4 seconds.
Share this with your Teams.
4. You got lost.
Claude Code, MCP connectors, GitHub, Vercel, Apify, Jev, Typesafe.
I feel you.
It’s a bit overwhelming.
The truth is: if I were next to you & your computer, I could set you up in 20 minutes.
So I host live workshops for my paid members of this Substack.
This newsletter is free. But you can upgrade to a paid option. It’s $200 a year.
For example each week, we have the ‘AI 101’ class. 23 minutes to get started with AI.
But we sometimes add ‘201’ classes or ‘301’ classes:
So this Friday, we will show you how to set up Jev on your computer.
20 minutes live. And then a Q&A for as long as you have questions.
We will cover:
- How to connect Github, Vercel & Apify to Claude Code.
- How to connect Jev & its API to Claude Code.
- How to build your first Jev tool.
To save your seat, you must go beyond this paywall: