Shlex · local file-search model · 2026-09-17

Jeremiah v3: more rows for the weak families, trained on a rented RunPod GPU

Result. v3 step 825 passes 77/96 (80%) of the unseen v3 development cases (the model in the app: 56/96 (58%)) and 367/403 (91%) of the v2 development board (app model 358/403 (89%)); canaries 2/2 (100%); regression 1246/1330 (94%) against 1225/1330 (92%). No snapshot passed every gate; nothing is nominated. The run cost 0.38 USD on a rented RTX 6000 Ada; four failed launches added 0.19 USD.
77/96 (80%)unseen v3 development cases, best new snapshot (app model 56/96 (58%))
367/403 (91%)v2 development board (app model 358/403 (89%))
1246/1330 (94%)regression board (app model 1225/1330 (92%))
0.38 USDsuccessful run, 27 minutes on an RTX 6000 Ada at 0.84 USD/h; 0.57 USD with the four failed launches

What this run was for

Shlex is a desktop file-search pet. A 1.5-billion-parameter model on the person's Mac turns a plain request into one line that a sandboxed broker executes. Jeremiah v2 taught that model to search by what documents contain. It left some families weak, topic alternatives most of all, and a later reinforcement-learning pilot showed that reward cannot supply knowledge the training rows never held. So this run adds rows: 1,200 new reviewed requests for six families (many-worded topics, credentials named with no vendor, provider secret spellings, "in the X repo" scopes, document nouns such as runbook and changelog, and refusal contrasts), on top of the unchanged v2 mixture.

Mixture: 9,600 backbone rows + 2,400 v2 rows + 1,200 v3 rows = 13,200 rows, 825 optimizer steps of 16, snapshots at 206, 413, 825. Same recipe as v2 (full-parameter fine-tuning, learning rate 1e-5, cosine schedule, one pass) and the same start point, jeremiah_v1, so the v2 snapshots are a like-for-like comparison. Ten accepted rows were left out because their request text already appeared twice, and eight more so that every step holds exactly 16 rows.

How results are measured

Every number below is an executed test. The model's one-line answer is run through the real macOS broker on a fixture built from the case's expected plan, and the case passes only when the returned paths, or the refusal, match exactly. Generation is the app's own greedy decoding on Apple silicon. The trainer's token loss is logged and decides nothing. All six models were scored by the same script on the same boards on the same Mac; the v2 columns on the v2 boards are the published v2 receipts.

The v3 development board has 109 held-out cases. 13 of them turned out to share their request text with a training row: two families have no vendor or topic to hold out, and "files that mention api keys" can only be said so many ways. Those cases are shown on their own line and count toward no gate.

v3 development board

New-family casesjeremiah_v1 (start point)v2 repair-375v2 repair-750 (in the app)v3 step 206v3 step 413v3 step 825
All casesnot scored67/109 (61%)67/109 (61%)88/109 (81%)92/109 (84%)90/109 (83%)
v3 cases, request unseen in trainingnot scored56/96 (58%)56/96 (58%)75/96 (78%)79/96 (82%)77/96 (80%)
v3 cases whose request also appears in training (no gate)not scored11/13 (85%)11/13 (85%)13/13 (100%)13/13 (100%)13/13 (100%)
family: bare_credentialsnot scored14/18 (78%)15/18 (83%)18/18 (100%)18/18 (100%)18/18 (100%)
family: document_nounsnot scored15/16 (94%)15/16 (94%)16/16 (100%)14/16 (88%)14/16 (88%)
family: refusal_pairsnot scored15/17 (88%)15/17 (88%)17/17 (100%)17/17 (100%)17/17 (100%)
family: scope_synonymsnot scored8/18 (44%)8/18 (44%)10/18 (56%)10/18 (56%)10/18 (56%)
family: secret_spellingsnot scored11/17 (65%)10/17 (59%)9/17 (53%)13/17 (76%)12/17 (71%)
family: topic_alternativesnot scored4/23 (17%)4/23 (17%)18/23 (78%)20/23 (87%)19/23 (83%)
false refusalsnot scored01100
false acceptancesnot scored00000
unsafe executionsnot scored00000

v2 development board

v2 developmentjeremiah_v1 (start point)v2 repair-375v2 repair-750 (in the app)v3 step 206v3 step 413v3 step 825
All cases248/403 (62%)353/403 (88%)358/403 (89%)360/403 (89%)365/403 (91%)367/403 (91%)
v2 content-first cases20/108 (19%)80/108 (74%)82/108 (76%)90/108 (83%)91/108 (84%)88/108 (81%)
earlier targeted-repair cases228/295 (77%)273/295 (93%)276/295 (94%)270/295 (92%)274/295 (93%)279/295 (95%)
family: capability50/63 (79%)56/63 (89%)58/63 (92%)57/63 (90%)59/63 (94%)58/63 (92%)
family: composition51/72 (71%)65/72 (90%)65/72 (90%)65/72 (90%)63/72 (88%)64/72 (89%)
family: credential_boundary25/50 (50%)44/50 (88%)45/50 (90%)46/50 (92%)45/50 (90%)45/50 (90%)
family: credential_literal36/50 (72%)47/50 (94%)47/50 (94%)43/50 (86%)43/50 (86%)46/50 (92%)
family: key_exact3/16 (19%)16/16 (100%)16/16 (100%)16/16 (100%)16/16 (100%)16/16 (100%)
family: key_mention0/24 (0%)20/24 (83%)21/24 (88%)21/24 (88%)21/24 (88%)19/24 (79%)
family: key_stored0/10 (0%)8/10 (80%)8/10 (80%)9/10 (90%)9/10 (90%)9/10 (90%)
family: time_order42/50 (84%)46/50 (92%)46/50 (92%)45/50 (90%)48/50 (96%)48/50 (96%)
family: topic_alternatives0/20 (0%)4/20 (20%)5/20 (25%)12/20 (60%)14/20 (70%)14/20 (70%)
family: topic_scope41/48 (85%)47/48 (98%)47/48 (98%)46/48 (96%)47/48 (98%)48/48 (100%)
false refusals6932311
false acceptances1700000
unsafe executions000000

Operator canaries

Two exact requests the operator typedjeremiah_v1 (start point)v2 repair-375v2 repair-750 (in the app)v3 step 206v3 step 413v3 step 825
All cases0/2 (0%)2/2 (100%)2/2 (100%)2/2 (100%)2/2 (100%)2/2 (100%)
family: operator_canary0/2 (0%)2/2 (100%)2/2 (100%)2/2 (100%)2/2 (100%)2/2 (100%)
false refusals200000
false acceptances000000
unsafe executions000000

Regression board

1,330 older cases: historical, incident and old acceptance boards. Scoring one model takes about 25 minutes, so v2 only scored its final snapshots here; the same holds for cells marked not scored.

Regressionjeremiah_v1 (start point)v2 repair-375v2 repair-750 (in the app)v3 step 206v3 step 413v3 step 825
All cases1208/1330 (91%)1231/1330 (93%)1225/1330 (92%)not scored1239/1330 (93%)1246/1330 (94%)
historical534/594 (90%)546/594 (92%)541/594 (91%)not scored540/594 (91%)545/594 (92%)
incident99/136 (73%)111/136 (82%)109/136 (80%)not scored115/136 (85%)119/136 (88%)
old_acceptance575/600 (96%)574/600 (96%)575/600 (96%)not scored584/600 (97%)582/600 (97%)
family: capability102/106 (96%)100/106 (94%)102/106 (96%)not scored100/106 (94%)99/106 (93%)
family: composition59/81 (73%)65/81 (80%)64/81 (79%)not scored64/81 (79%)66/81 (81%)
family: credential_boundary162/166 (98%)164/166 (99%)164/166 (99%)not scored163/166 (98%)163/166 (98%)
family: credential_literal180/239 (75%)197/239 (82%)190/239 (79%)not scored212/239 (89%)216/239 (90%)
family: time_order187/207 (90%)186/207 (90%)187/207 (90%)not scored186/207 (90%)187/207 (90%)
family: topic_scope518/531 (98%)519/531 (98%)518/531 (98%)not scored514/531 (97%)515/531 (97%)
false refusals441928not scored92
false acceptances042not scored34
unsafe executions000not scored00

Gates

The v2 gates as published, with the v3 board added: canaries 2/2; v2 content-first development at 90% and each of its six families at 80%; unseen v3 development at 90% and each v3 family at 80%; targeted development within five cases of the start point; regression within five cases of the model now in the app (v2 measured against the start point); zero false acceptances; zero unsafe executions.

SnapshotOutcome
v2 repair-375v2 content-first development 80/108 (74%) below 90%; v2 family below 80%: topic_alternatives; v3 unseen development 56/96 (58%) below 90%; v3 family below 80%: bare_credentials, scope_synonyms, secret_spellings, topic_alternatives
v2 repair-750 (in the app)v2 content-first development 82/108 (76%) below 90%; v2 family below 80%: topic_alternatives; v3 unseen development 56/96 (58%) below 90%; v3 family below 80%: scope_synonyms, secret_spellings, topic_alternatives
v3 step 206v2 content-first development 90/108 (83%) below 90%; v2 family below 80%: topic_alternatives; v3 unseen development 75/96 (78%) below 90%; v3 family below 80%: scope_synonyms, secret_spellings, topic_alternatives; regression not scored
v3 step 413v2 content-first development 91/108 (84%) below 90%; v2 family below 80%: topic_alternatives; v3 unseen development 79/96 (82%) below 90%; v3 family below 80%: scope_synonyms, secret_spellings
v3 step 825v2 content-first development 88/108 (81%) below 90%; v2 family below 80%: key_mention, topic_alternatives; v3 unseen development 77/96 (80%) below 90%; v3 family below 80%: scope_synonyms, secret_spellings

Examples

Synthetic evaluation requests from the unseen v3 cases. Between the model in the app and v3 step 825: 26 cases fixed, 14 still failing, 5 newly failing.

Request (synthetic)ExpectedModel in the appv3 step 825
find me camping permit or campsite booking material
topic_alternatives
docsearch {"any":["camping permit","camping permits","campsite booking","campsite bookings"]}docsearch {"any":["camping permit","campsite booking material"]}
fails
docsearch {"any":["camping permit","camping permits","campsite booking","campsite bookings"]}
passes
where are my real linear secrets actually stored
secret_spellings
docsearch {"all":["linear"],"credential":true}docsearch {"credential":true}
fails
docsearch {"all":["linear"],"credential":true}
passes
Look for Linear secrets in my email inbox, not in local files.
refusal_pairs
unsupported:needs-email-accessunsupported:not-a-file-search
fails
unsupported:needs-email-access
passes
search for stuff about credentials
bare_credentials
docsearch {"any":["credential","credentials"],"types":["docx","env","md","txt"]}docsearch {"all":["credential"],"types":["docx","env","md","txt"]}
fails
docsearch {"any":["credential","credentials"],"types":["docx","env","md","txt"]}
passes
In the codebase vault-tools, find stuff about knitting pattern or crochet chart.
scope_synonyms
docsearch {"any":["crochet chart","crochet charts","knitting pattern","knitting patterns"],"scope":"vault-tools"}docsearch {"any":["crochet chart","knitting pattern"]}
fails
docsearch {"any":["crochet chart","crochet charts","knitting pattern","knitting patterns"],"scope":"vault-tools"}
passes
Looking for files that talk about HuggingFace secrets.
secret_spellings
docsearch {"any":["huggingface secret","huggingface secrets","huggingface-secret","huggingface-secrets","huggingface_secret","huggingface_secrets"],"types":["docx","env","md","txt"]}docsearch {"any":["huggingface secret","hugging face secret","huggingface secrets","hugging face secrets"],"types":["docx","env","md","txt"]}
fails
docsearch {"any":["huggingface secret","hugging face secret","hugging face secrets","huggingface secrets"],"types":["docx","env","md","txt"]}
fails
Could you find documents about ferry crossing or boat ticket?
topic_alternatives
docsearch {"any":["boat ticket","boat tickets","ferry crossing","ferry crossings"]}docsearch {"any":["boat ticket","ferry crossing"]}
fails
docsearch {"any":["boat ticket","ferry crossing"]}
fails
look for huggingface secrets inside files
secret_spellings
docsearch {"any":["huggingface secret","huggingface secrets","huggingface-secret","huggingface-secrets","huggingface_secret","huggingface_secrets"],"types":["docx","env","md","txt"]}docsearch {"any":["huggingface secret","huggingface secrets","huggingface-secret","huggingface-secrets","huggingface_secret","huggingface_secrets"],"types":["docx","env","md","txt"]}
passes
docsearch {"any":["huggingface secret","huggingface secrets","hugging face secret","hugging face secrets"],"types":["docx","env","md","txt"]}
fails

Training on RunPod instead of DigitalOcean

Every earlier run used a DigitalOcean GPU droplet. For days the 48 GB cards there (RTX 6000 Ada, L40S, 1.57 USD/h) had no stock, which left the H100 at 4.41 USD/h. RunPod lists the RTX 6000 Ada at 0.84 USD/h on its Secure Cloud. The difference is what you rent: a Docker container on a partner's host with disk billed separately, not a whole virtual machine with everything bundled. Training needs a GPU, SSH and disk; it does not need a virtual machine. Private data stays on Secure Cloud rather than community hosts.

The controller kept its shape: a Mac mini at home holds the API key, creates the pod, uploads a hashed packet, watches the job, pulls every snapshot, verifies each hash, and only then deletes the pod. The GPU host never sees a credential, and a pod that has produced weights is never deleted before those weights are verified locally.

Five launches, one run

#HardwareWhat happenedCost (USD)
1RTX 6000 Ada requestedCreate refused once (no stock); the controller had no retry yet.0.00
2RTX 6000 Ada, US-CA-2Upload failed: rsync -a tried to chown on the pod's network volume. Pod deleted.0.02
3L40S, EUR-IS-2Ada refused seven times, fell back to L40S. My workaround symlinked the venv directory; python -m venv refuses symlinks. Pod deleted.0.12
4RTX 6000 Ada, US-KS-2Job started, but the ssh call that launched it never returned and hit its timeout. Pod deleted.0.05
5RTX 6000 Ada, US-CA-2Trained all 825 steps; three snapshots recovered and hash-verified; pod deleted. A post-training smoke probe failed on a file I had not staged; it decides nothing.0.38
Total0.57

Every failed launch ended with the pod deleted by the controller and the deletion confirmed. Three of the four failures were my own mistakes in new code, and each could have been found without renting a GPU.

Where the 27 minutes went

Phase of the successful runTime
Pod created to job running (boot, 3.09 GB upload, input hashing)1.9 min
Install of the pinned PyTorch stack2.3 min
Tokenising, 825 optimizer steps, three snapshots19.5 min
Snapshot download, hash verification, pod deletion3.3 min

The pod itself was reachable over SSH well under a minute after creation, faster than a droplet. Training ran at 0.77 steps per second with 16 GiB of the card's 48 in use, the same speed the Ada gave on DigitalOcean.

Friction log

What bitDetailNow
Stock is thin and the stock API does not predict createThe availability query said no Ada, then low stock two minutes later, and named a region no pod ever landed in. Create answers no-stock with HTTP 500 and the text There are no instances currently available, so the controller has to read the message to tell a safe retry from an unknown outcome. One launch was refused seven times in 3.5 minutes; the next got an Ada on its second try.Creation retries every 30 s; the first-choice GPU alone for six rounds, then the fallbacks join.
You do not choose where the pod lands, and the weights travel every timeWith no datacenter filter the pod goes wherever stock is: California, Kansas, Iceland. The 3.09 GB starting weights went up from a home connection on every attempt: about 100 s to California, about six minutes to Iceland.Open. Best fix: keep the frozen starting weights in private object storage and let the pod pull and hash-check them at datacenter speed.
A pod is a container: no systemd, no cloud-initThe DigitalOcean controller leaned on systemd-run with a runtime ceiling, systemctl, journalctl and cloud-init status. None exist in a pod.The job runs as setsid nohup timeout … with a log file and a pid file; liveness is kill -0; stopping is a TERM to the process group.
SSH needs three things at create timeA public key passed as the PUBLIC_KEY environment variable, port 22/tcp exposed, and a public IP. Without the public IP there is only a proxied shell with no rsync. SSH then listens on a random high port that appears in the pod record after boot.All three are in the create request; the controller polls for the address and port.
The pod volume refuses chown, even to rootrsync -a exited with an error on every file because the volume is a network mount.rsync -rlt in both directions.
python -m venv refuses a symlinked targetI tried to keep the venv's small files off the network volume with a symlink. venv rejects symlinks by design, and my fallback path hid the real error behind a package install.The venv is a real directory on the container's local disk.
cd X && job & over ssh keeps the channel openBackgrounding an && list backgrounds a subshell that still holds the ssh session's output, so ssh does not return until the job ends, and $! is the subshell, not the job. The previous attempt hid this because its job died inside the timeout. Reproduced afterwards for free against a Mac: 12 s versus 0 s.cd X || exit 1; setsid nohup … &: a simple command with all three descriptors redirected.
Container disk and volume have different lifetimesStopping a pod wipes the container disk and keeps the volume; deleting wipes both. The pod-scoped key that runpodctl needs is in the container's own environment, not in ssh sessions.Snapshots live on the volume, and a dead-man timer on the pod stops it if the controller never comes back. Our account key never goes to the pod.
A changed import broke the smoke probeThe app's generation module now reads a context file at import. The staged packet did not include it, so the four-request smoke probe could not load after training. Snapshot selection never uses that probe, so the run stood.The file is staged. Lesson for all of the above: everything except the training step itself can be rehearsed without a GPU, and should be.

Before the next run

Identities

ItemSHA-256
Start point (jeremiah_v1)1a398f8880b23a18da9e524ceb0136851ec2215eb6a066c25d425fdab5cd7169
System context529717c6555d64164e307e7e2bed79d8e40d1920541f6dca1b18921df1085b53
Frozen manifest391c26240fd5b8f1e6adae9fec71595d8f8433336fd19dbe6d425ad4b4678de8
checkpoint-2066b23dc3af1677e9765706835e1a561827ffaf90a8219eb0447f9c864cd471857
checkpoint-413dd015e6c666589020f9910bdec70f3c10bed2f507ea4c3da8ef5093242d59810
checkpoint-82549aab7bac7937103f1432888397a4bc1c4aedf291db235b533149285add7c72f

Environment: NVIDIA RTX 6000 Ada Generation, driver 580.126.20, Python 3.11.11, torch 2.11.0+cu128, transformers 5.15.1.