How I built an AI job-application assistant (plain version)¶
This is the story of how WorkWingman came to be — told in plain language, prompt by prompt, so you could follow the same path even if you're not a programmer. It's a living document: it grows as the project does.
Detailed engineer version: ../technical/making-of.md
The short version¶
I described what I wanted to an AI (Claude), first as a design — drawing the screens — and then as real working software. At each step I typed a plain request, and the app grew. A second AI reviewer (Codex) checked the work before anything was saved. That's the whole method: describe, review, keep, repeat.
Part A — designing it (the picture)¶
I started in a design tool and shaped the look and behavior one request at a time:
- "Make a job-application assistant" — an app that reads your resume, asks about your job hunt, and logs into your accounts. → the basic idea and first screens.
- "Show me a few visual styles and viewer layouts." → six options: a calm warm style, a dense data-table style, and a dark "console" style, plus three ways to watch it work.
- "Use the calm style, but denser, and combine the best viewers." → the look the app has now.
- "Add encouragement, and study links for each job." → supportive pop-ups and a study hub with videos, courses, and practice.
- "Offer a focused study workspace per job." → the "make me a Claude study project" button.
- "Put my interviews on Google and Apple calendars." → calendar sync.
- "Add Apple sign-in." and 8. "Store my course logins in KeePass." → more secure connections.
- "Since everything's on my computer, make it a desktop app, not a website." → the big decision that everything runs locally.
Part B — building it (the real thing)¶
Board ledger: FND · Foundation
Then I asked the AI to build it for real:
- The big one: build it with these exact tools, put it in a code repository, learn scraping tricks from two of my old projects, and copy the folder structure from two others. → the AI imported the design, set up the whole project, wrote the screens and the behind-the-scenes "brain," and put it on GitHub.
- "Use GitHub's built-in automation, not the other kind." → automatic testing on every save.
- "Run it — and always have the reviewer check first." → the review-before-keep habit began.
- "How do I give GitHub permission?" → a quick account setting, then everything uploaded.
- "Update everything to the newest versions without breaking it." → all the building blocks refreshed.
- "Don't forget the reviewer before uploading." → I made that a permanent rule.
- "Add a helper that installs the other software I need — but ask me first." → the dependency doctor: it checks what's missing, shows you, and only installs after you tick the box and approve.
- "Go ahead and install KeePassXC." → the doctor installed it, using its own feature.
- "Write the full documentation and desktop testing." → these very documents.
What kept it honest¶
Every single change went through the same loop: the building AI wrote it, the reviewer AI (Codex) looked for mistakes and security holes, real problems got fixed, and only then was it saved and uploaded. The reviewer caught genuine issues every time — like a way a stray website could have triggered installs, which got locked down before it ever shipped.
The journey at a glance¶
timeline
title From idea to working app
section Designing
Idea : a job-application assistant
Style : pick the look and the live viewer
Extras : encouragement · study help · calendars · secure logins
Decision : make it a local desktop app
section Building
Build : set it all up and put it online
Ship : automatic testing · reviewer gate
Maintain : update everything · always review
Doctor : auto-install helper with your approval
Document : write the guides and desktop tests
Overnight: the big multi-agent build-out¶
Board ledger: LAB · ScraperLab
One night the work changed shape: instead of one AI having one conversation, it became a whole team of AI agents working at once, each on its own separate copy of the project, with a supervisor checking everyone's work before it counted as done.
How a team of AIs works without stepping on each other¶
Each agent got its own private copy of the code (in git terms, a "worktree") and its own branch — so ten-plus agents could all be editing at the same time without any risk of one overwriting another's work. Before any agent's finished work was allowed to join the main project, it had to pass the same reviewer-AI gate used all along (Codex, reviewing for bugs and security issues) and pass the automatic tests. Nothing skipped the line, even at 3 a.m.
Splitting the work across two AI services¶
Partway through, the work split across two different AI subscriptions — Claude and Codex — instead of leaning on just one, so neither one got overloaded running so many agents at once. Codex, which had been the "reviewer," was also trusted to write some of the work directly once a technical snag (a permissions setting) was fixed. And the rule stayed the same both ways: whoever writes the code, the other AI reviews it just as strictly. That rule caught a real mistake later in the night (see below).
Cheaper, faster AI models handled the easy tasks; the most capable model was saved for the hard ones — the same idea as not renting a moving truck to pick up a single box.
Eleven practice runs, one for each major job-application website¶
The biggest chunk of the night was building eleven separate practice labs, one for each major company that powers online job applications — Workday, UKG, Greenhouse, Lever, Oracle (two flavors), SAP SuccessFactors, iCIMS, Paycom, Dayforce, ADP, and Paycor.
Each lab worked the same way, and it's worth spelling out because the safety promise is the whole point: build a fake copy of that company's application form that only exists on this one computer, then run the real automation against that fake form hundreds of times, each time with something slightly different broken or moved, and write down what worked and what didn't. No real website was ever touched, no real account was ever created, and nothing was ever actually submitted — every lab checks, in code, that its own "submit" button was never really clicked.
What came out of it, in plain terms: every one of these websites builds its forms a little differently under the hood, so a filler-inner that only knows one trick breaks constantly. The fix was always the same shape — try the best clue first, and if it's not there, try the next-best clue, all the way down a list, rather than betting everything on one guess. Some of these sites also hide their form fields inside little "windows within the window" (like a video embedded in a webpage), so the automation had to learn to look inside those too. One site (Dayforce) turned out to be a timing problem more than a clue problem — the fields simply aren't there yet when the page first loads, so the automation learned to wait for the page to finish "waking up" before trying to fill anything in.
Getting the tests to actually prove something: 86% to 99%¶
Board ledger: TST · Testing foundation
There's a clever way to check whether your automated tests are any good: deliberately break the code in a small way and see if a test catches it. If nothing catches it, that test coverage was an illusion. Running this check found real gaps and closed almost all of them — from roughly 86% of these deliberate breaks being caught, up to 99.34%. The tiny number that remain unfixed are ones a human confirmed truly can't be caught by any reasonable test.
Here's the part worth remembering: the reviewer AI double-checked those "can't be fixed" claims — and found two of them were wrong. One would have silently reset some saved data every time the app restarted; the other could have deleted a file if saving it failed partway through. Both got properly fixed with real tests this time. That's the whole value of having a second, independent reviewer: it's easy to convince yourself a shortcut is safe when you're the one who took it.
A new feature: filling in the tricky extra questions¶
Job applications often ask more than just "name and resume" — reference contacts, salary expectations, yes/no questions that unlock follow-ups, questions that can quietly disqualify you. The new feature teaches the app to recognize twelve categories of these questions and handle the tricky cases honestly: mark something "not applicable" when it truly doesn't apply, fill in reference details it already has, and flag anything that could disqualify you so you confirm it yourself rather than guessing. It also drafts (never sends) short outreach notes for LinkedIn — with a test proving the drafting code has no way to actually send or post anything.
Could an AI just "look" at the form instead of using clues? (Research)¶
One more question got a real, honest answer overnight: instead of teaching the automation dozens of specific clues per website, could you just have an AI look at the page like a person would and figure out what to click? The research says: not by itself, but as a helpful assistant to the clue-based system, yes. Full AI-only automation would be slower, cost real money per application, lacks the built-in "never submit without asking" safeguard the current system has, and can't peek inside those embedded windows-within-windows the same way. So the plan going forward is a hybrid: the clue-based system keeps driving the whole application, and only when it hits a field it genuinely can't figure out does it ask the AI for a second opinion — kept rare on purpose, so it stays fast and cheap. See architecture.md § the hybrid approach for how that's wired.
Something we tried and turned down (and why)¶
Not every experiment makes the cut, and it's worth remembering the ones that didn't. In a separate thread we looked at a brand-new Google technology called A2UI — a way for an app to build question screens on the fly, which sounded useful for the ever-changing custom questions different companies ask. We said no to it, for both apps. The main reason: the thing it would help with is already handled better by the app's own built-in list of about fifty known question types plus its "ask the user when unsure" safety net — and switching to A2UI would trade away the predictable, knows-what-it's-doing behavior the whole app is built around, to gain something we don't really need. It also simply didn't fit the plumbing yet (no support for the app's backend language, and a version clash with the app's front-end framework). The lesson we kept: a new tool working isn't the same as a new tool being better than what you already have — and this one wasn't.
A plan for study MP3s and MP4s you can carry around¶
Board ledger: INS · Job insights & interview prep
One planning-only pass (nothing built yet) designed a way to turn your study notes into files you can take with you — MP3s for your phone or car, and for falling asleep, plus MP4s for a TV in the background. Every tool picked is free and runs on your own computer: a program called ffmpeg builds the audio/video, and a program called Piper reads your notes aloud, offline. Two rules were baked into the plan from the start: the calming "alternating side to side" sleep sound is described only as relaxing background ambience — never as the medical therapy it sounds similar to (EMDR is real, licensed, and different) — and every rain sound, music loop, or video clip must be free-to-use-by-anyone, never something borrowed from a copyrighted source. Full write-up: study-audio.md.
Adding stock-grant math, without ever giving advice¶
Later on, we tackled a common part of job offers: stock (RSUs or options). Before writing a line of code, the rule was locked in: show the facts, never the opinion. Current price, when shares actually vest, how bumpy the stock has been in the past — full stop. No "this is a good offer," no predictions.
Along the way we hit the same kind of honest surprise as the A2UI story above: the free stock-data site we planned to use first, Stooq, had quietly added a "prove you're a real browser" wall sometime before we got around to testing it live. Rather than treat that as a dead end, the app just falls back to the next source automatically — the same safety-net pattern already used elsewhere (see the layoffs story above) absorbed the surprise without any special-casing.
We also asked, again, "could we just use Robinhood?" — same as the LinkedIn question earlier in this story. Same answer: no legitimate way exists without handing over someone's actual brokerage login, so we didn't build it, and documented exactly why.
Full story: equity-comp.md.
Homework before an interview (Research)¶
Getting the tailored resume and cover letter right is only half the job — walking into an interview knowing what a company actually does, what technology their engineers use, and whether they've been in the news lately helps too. This feature builds that "interview prep" homework automatically, pulling only from free, official sources — Wikipedia for the "what they do" summary, GitHub for a peek at the company's public engineering tech stack, and GDELT (a free, open news-monitoring project) for recent headlines. A few well-known paid tools were looked at and turned down on purpose — NewsAPI, Bing News Search, BuiltWith, Wappalyzer, StackShare, Crunchbase — because they'd require an account or a subscription, which doesn't fit an app meant to run free, forever, on your own machine. See company-research.md for the full plain-language rundown, including how these same facts quietly find their way into your cover letter when there's something genuine to say.
Adding "know yourself" — an optional personality/style suite¶
A later addition, built on its own separate branch so nothing else could be disturbed: an optional set of quick self-assessments — a real 50-question Big Five personality test, a spot to type in your Myers-Briggs-style result from a free outside test, and short work/communication/ learning-style quizzes.
Before writing any code, three honest questions had to be answered: is the personality test we're using actually free to use (yes — it's a well-known one published for free by a nonprofit research project), can we build our own Myers-Briggs test (no — the real one is a paid product we don't have rights to, so instead we point you to good free alternatives and let you type in your result), and can we copy the popular "how do you learn best" quiz called VARK (no — it's copyrighted, so ours is our own version of the same idea).
The privacy rule got the same honest treatment: everything stays on your computer by default, with exactly one clearly labeled button that copies your results to Google Forms if you actually want that — and it's impossible to trigger by accident. See self-assessment.md for the full, plain-language version of all of this.
A practice list for each job, and a question we said no to¶
A later request added a short "practice for this role" list to the Study screen — a handful of LeetCode courses and study plans, plus a few well-known free platforms like Databricks and Kaggle Learn, matched automatically to whatever job you're looking at. Every link was checked by hand before it went in — most by fetching the page directly, the LeetCode ones a different way since LeetCode blocks that kind of automated check (checking that search engines had already indexed the exact real page instead).
The trickier question was: should the app also peek at your public LeetCode stats — how many Easy/Medium/Hard problems you've solved — to suggest what to study next? We looked into it honestly, and the answer was no. LeetCode's own settings file that tells automated tools what they can and can't touch says "don't touch this," and its rules for using the site separately say no scraping. Plenty of side projects online do it anyway, but "people get away with it" isn't the same as "it's allowed," and the rule going in was: if it's not clearly allowed, don't do it. So instead, if you type in your own LeetCode username, the app just hands you a direct link to your own public profile page — you look at your own numbers, on LeetCode's own site. The app never peeks at it for you. Full story: learning-paths.md.
Prep studio: LeetCode company lists, a richer Claude project, and flashcards for your phone¶
Next we built out the Study tab into a full "prep studio." First, a curated list of about 65 well-known companies and their LeetCode practice-problem pages, with a clearly-labeled, never-automatic link to LeetCode's paid Premium tier if you want the full company list. Second, a richer study kit that gets loaded into your Claude project — gap skills, the detected hiring system, a system-design nudge for senior roles, and a behavioral-prep reminder. Third, and biggest: flashcards you can carry on your phone with zero app installs — one self-contained web page you download, move to your phone however you like, and open in the browser. It reads cards aloud using your phone's own free built-in voice, and — this was the important promise — it never talks to the internet once it's on your phone. We even wrote an automatic test that opens the file and makes sure it doesn't contain a single link to an outside website.
What happens after the interview¶
Everything that happens right after an interview is easy to forget: the questions you were asked, how it actually felt, that thing you and the interviewer both turned out to like. So this pass added a page for exactly that moment. It drafts a thank-you note — email and a shorter LinkedIn version — using what you tell it (never sending either one, same as everywhere else). It also asks a quick "how'd that go?" survey, lets you jot down the questions you were asked so they build into one growing personal study list over time, and lets you note what you had in common with the interviewer. Later, when the company actually responds, you can paste their feedback in next to your own gut feeling and see how well they matched. All of it — except the thank-you draft text itself — stays on your computer; none of it gets uploaded anywhere. See interview-retro.md for the full story.
Study videos, playlists, and podcasts you actually own¶
Study & prep grew two more helpers: real YouTube videos matched to a job, and podcast suggestions. Both follow the same "your stuff stays yours" rule as everything else — search results come straight from YouTube's and Apple's own search tools, never made up. The one part that needed extra care was letting WorkWingman build you a YouTube playlist: since that actually creates something in your own YouTube account, it only happens after you tick which videos you want and then click a second, clearly-worded "yes, really create this" button — and even then, the app double-checks behind the scenes that the request genuinely came from WorkWingman itself, the same protection it already uses before installing any software on your machine.
Letting WorkWingman actually fill out an application¶
Board ledger: APL · The apply-automation vertical
Until now, "Start Run" mostly showed you a checklist. This session made it real: WorkWingman can open the job's application page in a browser you can watch and fill it in for you — your name, contact details, your actual resume, and the screening questions it can confidently answer from your profile.
Two rules are absolute. It never clicks Submit — it fills everything in, finds the submit button, and hands it back to you to review and send yourself. And it never guesses on anything that matters: if a question is ambiguous (an unusual dropdown, a "why do you want to work here?"), it pauses and asks you — keeping the page open — then picks up exactly where it left off once you answer, instead of starting over. It only auto-answers legal-eligibility questions when it's sure (for example, it only uses your US work-authorization answer when the question is actually about the US), and it fills your education from your highest degree. If you stop a run, it closes the page and stops right away.
Because starting one of these runs now drives a real browser, the app double-checks that the request genuinely came from WorkWingman itself — the same protection it already uses before installing software or creating anything in your accounts. And the whole feature was proven against built-in practice "fake" application sites first, so its behavior was verified safely offline before it ever touches a real employer's form.
Workday, and the "create an account first" wall¶
Workday applications are the awkward ones: before you can apply, the employer's site makes you create an account. WorkWingman now handles that whole journey. It gets to the sign-in wall, prepares a strong password in your local vault, and stops — it never creates the account or types a password for you. You sign in yourself, click continue, and it picks up right where it stopped: contact details, work history, education. If the education dropdown doesn't have an obvious match for your degree, it pauses again and shows you the choices. It skips the voluntary-disclosure (EEO) questions entirely — those are always yours — and finishes by finding, never clicking, Submit.
A lot of polish went into being honest about progress. The run only ever reports a field as filled if it really landed on the page, only says "you're at the final review" if it truly reached that page, and if something required still blocks the way, it says so plainly and leaves the page open for you. If two applications hit the same employer's account wall at the same time, they now share one prompt instead of asking you twice. And pressing Stop always means stop — even at the worst possible moment, a stopped run can't accidentally come back to life.
Teaching it to translate your credentials¶
Application forms never offer exactly what you have. There's no "Bootcamp" choice on their education menus, "Game Programming" isn't on their majors lists, and some ask an expiry date for certifications that never expire. The app now translates: your bootcamp certificate honestly picks "Certificate" (never claiming a degree), your own answers to "how should sites read this degree?" are used to pick the closest major they DO offer, an expected-finish date is given only for studies you're actually still in, and a non-expiring certification ticks the "does not expire" box instead of making a date up. When none of that clears the bar, it pauses and asks you — same as always.
Ticking the right skills, in whatever form the site uses¶
Every site asks for your skills and languages a different way — a checklist, a "pick and add" box, or a plain text field. The app now handles all three, and follows one rule everywhere: it only ever selects or lists skills you actually put on your profile, and when the site's list doesn't include one of yours, it tells you so instead of dropping it silently or making something up. Your languages fill in with their proficiency level too.
"How did you hear about us?" — answered honestly¶
Almost every application asks how you found the job. The app answers in a fixed order of honesty: if you recorded a referral for that specific job, the answer is "Referral" (and the referrer's name and work email fill in the follow-up boxes); if not, it uses whatever source you told it; and if you never said, it picks LinkedIn — where the app finds your jobs in the first place. One careful detail: it only falls back to LinkedIn when you haven't said anything, so if you told it "Company Website" and a particular site doesn't list that, it leaves the box for you rather than quietly changing your answer to LinkedIn. It never makes up a source.
Reading your resume to get you started — as suggestions, not facts¶
If you already have a resume, the app can read it to save you typing — but it treats everything it finds as a suggestion you approve, never something it quietly fills in. A first, offline pass pulls out the things it can be sure about: your email, phone, name, LinkedIn/GitHub links, and your listed skills. Anything it can't be certain of — your job history, your education — it honestly marks as "still needed" rather than guessing. Then it shows you what it found so you can confirm or fix each piece before anything is saved. Getting this "never put words in your mouth" behaviour right took a few passes: making sure a phone number doesn't accidentally grab the digits from your address line, that a heading like "Professional Summary" is never mistaken for your name, and that a skill like "SQL" on its own line is kept as a skill.
For the harder parts of a resume — your job history and education — the app can ask your own Claude (the one you're already signed into) to read the resume and pull those out, still only as suggestions you approve. This is completely optional and never gets in the way: it happens in the background after the quick results already show, and if Claude isn't set up or takes too long, the app simply moves on with what it already found. Nothing is sent to us — it uses your own Claude, and your onboarding never waits on it.
On screen, this shows up as an optional "Have a resume? Paste it to prefill" box on the first onboarding step. You paste, the app shows you exactly what it found — each item with a checkbox and an editable value, the confident ones ticked for you — and only fills your form when you press Apply. You can untick or edit anything first, and nothing is saved until the usual review step at the end.
A "what's still missing" list, so nothing surprises you mid-application¶
The app keeps a running list of the things applications commonly ask for that your profile doesn't have yet — a phone number, a full address, your education, your top skills, a LinkedIn link — split into "recommended" and "optional." It fills itself in as you complete your profile, so a gap disappears the moment you add that detail. It also remembers anything a real application couldn't answer for you and adds it to the list, tagged so you know it came from an actual apply attempt. You can dismiss anything you don't care about, and the list stays on your machine.
No resume? The app can write you one from your answers¶
Not everyone starts with a resume — and now you don't need one. If you've never uploaded one, the app can build a starter resume straight from the answers you already gave during onboarding: your contact details, a short summary, your skills, education, and work history, laid out as a plain resume. It only uses what you actually told it and makes nothing up — if you skipped a section, that section simply isn't there, no empty headings. This is the mirror image of the earlier "read my resume to prefill" feature: that one turns a resume into answers, this one turns your answers into a resume. On the final onboarding review step there's a "Generate from my answers" button that saves your answers, writes the resume to a file on your machine, and hands it to the app as your resume — so from then on applications can attach it just like one you'd uploaded yourself. You can generate it once and it shows you a preview; if you already have a resume on file, it just tells you that instead.
It refuses to type a broken answer into a real application¶
The app now double-checks the things you filled in that have an expected shape — your email, phone number, your LinkedIn/GitHub/portfolio links, and the dates on your jobs and education — and quietly flags any that look malformed. It only ever checks what you actually typed: a field you left blank is just "still missing" (it shows up on the "what's still missing" list instead), never something it scolds you about. On the final onboarding review step you'll see a friendly banner listing anything that looks off, each with a plain reason like "not a valid email address" — it's advice, not a wall, so it never stops you from saving. The real payoff happens when the app is filling out an application for you: if one of your answers is malformed, it skips that field and leaves it for you to finish by hand, instead of typing something broken into a live form. Long, multi-line answers like a cover letter are always welcome — the only things it blocks are the kind of hidden junk characters that would corrupt a form. Your passwords are never inspected or written down anywhere in the process.
Pulling your details straight from your own LinkedIn¶
If you're already signed in to LinkedIn, the app can save you even more typing by reading your own profile and offering what it finds — your name, headline, skills, schools, and past employers — as suggestions you review before anything is filled in. It works just like the "read my resume to prefill" feature: nothing lands in your profile until you say yes, and anything your LinkedIn profile doesn't mention simply shows up on the "what's still missing" list instead of being made up. It only ever looks at your own profile, quietly and at a human pace, and it never signs you in — if you're not connected yet, it just tells you "Connect LinkedIn first" and leaves everything as it was. On the first onboarding step there's an "Import from your connected LinkedIn" button right next to the paste-your-resume box, and importing this way still leaves the "write me a resume from my answers" option available if you want it.
Making the address box actually find your address¶
A real person tried to type their home address into the app and the suggestions never found it. The list of addresses had been coming from OpenStreetMap, a free community map that's great for landmarks but spotty for ordinary US houses. So the app now asks the US Census Bureau first — the government's own free address service, which knows real residential addresses — and only falls back to the community map for the everyday cases it already handled. The Census service wants a whole address, not the first few letters, so the app waits until what you've typed looks complete enough (a house number plus a ZIP code, say) before asking it; until then your early keystrokes go straight to the fast fallback, so the box never feels sluggish. And if either service is slow or down, the app gives up on it in a few seconds and quietly tries the other, instead of leaving you staring at a spinner. Nothing about the box looks different — it just finds the address now. We checked it against the real service with a famous address (1600 Pennsylvania Avenue) and it came back clean, split neatly into street, city, state, and ZIP.
Teaching the app the words job forms use¶
Job-application forms speak a fairly standard vocabulary — the handful of "highest education" choices a menu offers, common skill names, well-known certifications, familiar schools. This addition gives the app its own curated lists of those words, so it can offer clean suggestions and tidy up messy typing. The education list is the complete set a dropdown would show. The lists of skills, certifications, and schools are deliberately just a starting set, not an attempt to list everything: if you type something that isn't on the list, the app never throws it away — it simply keeps your own words exactly as you wrote them.
Two things sit on top of these lists. One offers autocomplete-style suggestions as you type (matching the start of a word first, then anywhere in it) — that's what a friendlier picker will use next. The other quietly tidies a typed-in value toward the standard spelling — turning "c#" into "C#", for example — but only when it's genuinely confident; if it's unsure, it leaves your words alone rather than risk changing them into something you didn't mean. Both are careful not to overreach.
This piece was built to lay the groundwork and stop there: it adds the vocabulary and the tidy-up tool, but leaves plugging them into the existing form-filling for the follow-up work, so the already-solid form-fillers weren't disturbed while the new lists landed. The reviewers kept it honest as usual — one added a sensible length limit on the suggestion box, and a worry about the lists being huge was checked and set aside, because they're small, hand-picked sets, not giant scraped catalogs.
Pick one skill, and it suggests the rest¶
You've seen this on Spotify or X: you pick one artist or topic you like, and it immediately offers a few related ones to tap and add. This addition brings that same friendly move to setting up your skills. Until now, the "skills" step during setup didn't actually let you type a skill — the only way your skills got filled in was by reading them off your résumé or LinkedIn. Now there's a real picker: search for a skill and it suggests matches as you type, tap one to add it as a little removable chip, and the moment you add something, a "related to what you added" row appears with a handful of one-tap suggestions. Add "React," for instance, and it offers things like JavaScript, TypeScript, Next.js, and a few more that tend to go together. It's smart about not repeating itself — anything you've already added is quietly left out of both the search results and the suggestions, so it never offers you something you've already got.
Under the hood, "related" comes from a hand-curated map that groups skills into overlapping families — front-end skills, data skills, and so on — where two skills count as related if they share any family. A safety check makes sure every skill named in that map really exists in the app's known list, so the suggestions can never point at a skill the app doesn't actually recognize. The reviewers focused on the fiddly timing that type-as-you-go boxes always have: making sure a slow answer for a skill you'd just removed couldn't overwrite a newer one, and that suggestions never flash up in a box you'd already cleared. All of it was tightened up before it shipped.
A security checkpoint for the code itself¶
Board ledger: SEC · Security & scanning
WorkWingman handles your personal data, so this session also finished the project's security scanning setup. Every change to the code now passes through four automated checks before it lands: a code scanner that looks for vulnerability patterns, a scanner that checks the project's entire history for accidentally committed secrets, a check for known-vulnerable third-party packages, and a live probe that boots the app's local engine and pokes at it the way an attacker would. The very first full scan found two things — one real (a file-saving routine future code could have misused; fixed) and one false alarm (documented with its reason, not silently ignored). A nice side effect: the app now tells anything that talks to it "never keep a copy of this response on disk", because your job-search data has no business sitting in a cache.
Chapter: deciding how it makes money¶
Board ledger: BIZ · Business & strategy
Eventually a free app has to answer a hard question: how does it make money without turning into the kind of thing it was built to replace? The founder had big ideas — deals with LinkedIn and Workday, selling to recruiting agencies, a paid cloud version, a way to verify people's work history — and a belief that this could one day be a billion-dollar business. The danger was just as big: get the money part wrong and WorkWingman becomes another spammy apply-bot that gets people banned and sells their data.
So the decision was made the way the code gets reviewed here: by asking a whole council of AI models at once. The question went to several different AIs — including two running on the household's own gaming and streaming PCs — and Claude pulled their answers together. They mostly agreed: keep the core app free, first charge only for convenience (like backup and an easy "it just works" AI option), and save the big partnerships for last, once lots of people already trust the app. They also agreed on what to avoid: never become a spam machine, never sell people's data.
The most important lesson came from looking at the numbers: people stop using job-search apps the moment they get hired. That makes it really hard to build a business on individual users alone. The fix is to sell to the helpers — career coaches, bootcamps, and colleges — because they stick around, and to help people with their whole career, not just one job hunt. All of this got written up in a new set of business documents: the plan, a roadmap, the money details, a slideshow to show investors, and a plain-English guide to staying legal and safe — each with an easy-to-read version like this one. The one rule underneath all of it: the job seeker, not the recruiter, is who WorkWingman works for.
The first green build — reading the red lights instead of guessing¶
The first time the app went up to the shared build service, its automatic checks ran and three came back red. The temptation is to react to the name of each failing check. The better habit — and what happened here — is to open the actual log first, because two of the three were nothing like their name.
- The check called "determinism" sounded like a test that behaves differently each run. The log said otherwise: a browser-install step was failing to download because one of the runner's software sources had a broken signature that day. A twin job had just passed the exact same step, which gives it away as a hiccup in the rented machine, not a bug in WorkWingman. The fix was to skip that one flaky source (the browser only needs the standard system libraries anyway).
- The "app shell" check sounded like the app failing to open. It opened fine. The check was looking for the exact words "Work Wingman" in the corner, but the app now shows "Work Wingman REAL DATA" there (the little badge that tells you you're on your real data). The fix was to point the check at just the name, ignoring the badge.
- The "docs" check was a genuine one: a few documents lived in the wrong folder for the rule that every document have both a plain and a technical version. Instead of faking the missing versions, the documents moved to folders where they belong — reference notes together, the plain business summaries in their own place — so the rule stays honest.
Chapter: July 8 — the outage, the demo, and the lock on the door¶
The night the sessions died, and the watchdog that would have caught it¶
On the night of July 7 going into the 8th, something upstream failed for six hours and every single agent session died at once — stopped in the middle of what they were doing, couldn't be resurrected. Recovery that morning swept all of them back up and restarted them.
By that same afternoon, a standalone watchdog was built and installed that would have told someone immediately instead of the sessions just vanishing in the dark. It's a little Windows Scheduled Task that checks if the main work session is still alive, and if it dies, sends a wristband vibration (via the PavlokPager app) so someone gets woken up instead of finding dead sessions hours later. A crashed overnight session now pages Andrew's wristband within minutes (as long as the watchdog itself is healthy).
The demo at 9:30 a.m., and the file lock¶
Fresh 1.28 GB installer was rebuilt that morning for a live demo. The build nearly died mid-pack: Windows Defender was actively scanning the unpacked installer files, locking them and breaking the build step. Cleared out the quarantine, rebuilt, and shipped it in time.
Then the installed app hung on "Loading profile…" with API returning 401 (unauthorized). The demo unblocked by stopping the test runners — which was the tell that something was deeply wrong.
The app's own tests kept changing the lock on the front door¶
This is the bug worth explaining because it's a clean story about how a safety feature can become a hidden landmine.
The setup. When the app starts, it creates an auth token (like a key) and saves it to
%LOCALAPPDATA%\WorkWingman\api-token. The app's renderer (Electron) reads that key once at startup
and keeps it in memory. Then every time you tell the app to do something, it uses that key.
What went wrong. Every single test run that created a fake token for testing also overwrote that real file — thousands of times every night while the mutation test suite ran (remember, that's the thing where you deliberately break the code to see if a test catches it). So the app's stored key got corrupted thousands of times.
Then you'd launch the app for real, and it would read the broken key, and the API would reject it: 401 Unauthorized.
Worse: if you killed that first window and opened the app a second time, the API would try to create a fresh token and write it out — then immediately crash because the first instance was still holding the network port. Now you had two windows, neither with a valid token, neither able to talk to the API.
The fix. Four things (all merged same night):
-
Test runs get their own temp file. Instead of overwriting the real token file, tests write to a temp path that only exists during testing. When the test finishes, that temp file vanishes.
-
"Only one window at a time." The app now enforces a lock: you can only have one WorkWingman window open. Try to open a second one and it just exits cleanly. (It used to silently spawn zombies fighting over the same port.)
-
The test env var gets scrubbed before launching for real. The renderer tells the background process: "Hey, use the REAL token file, not the test one." So even if a test env var somehow leaked into the real app, it gets thrown away.
-
Tests verify it worked. A new test confirms the app never ever touches the test temp file during real runs.
The payoff. After the fix: 3,095 tests, all green. Same night, all five council seats (Codex, Gemini, local AI, and two house gaming/streaming PCs) reviewed it together and signed off.
Accessibility: making sure the app works for screen readers and keyboard-only users¶
A full WCAG 2.2 AA pass (the international standard for accessible websites and apps) happened in three parts:
-
Semantic labeling. ~40 screens got ARIA labels so screen readers know what everything is. Dialog boxes became proper system dialogs instead of custom designs. Every page got a "Skip to main content" link for keyboard-only users. Color contrasts got enforced so low-vision users can actually read the text.
-
Automated checking. A new automatic test runs on 22 production pages and checks for accessibility violations. If it finds one, the build fails — the build fails on the accessibility problems a machine can catch — a person with a screen reader is still the final check.
-
Checks against real data. The automated checker also ran against 25 pages filled with real job data (carefully, in read-only mode so it couldn't change anything), plus automated keyboard tests — because an empty page can pass a check that a full page fails. The one thing left is a real person using a screen reader end-to-end (WW-63c) — that's waiting on a human.
The reason this matters: you can't see accessibility violations looking at a sighted person's screen. So they're easy to ignore. By automating the checks, the build itself rejects inaccessible code before it ever ships.
The usage crisis: too many agents burning the budget¶
22 sessions (overnight builds, labs, business research, testing) were running at once and burning through API costs. Most of the cost wasn't even the AI thinking — it was rereading information it had already seen (a feature called caching). When you read the same big context twice, the second read is 90% cheaper. But even with a 90% caching discount, twenty-two sessions all re-reading that same giant context at once adds up fast. The fix is fewer sessions, not just cheaper ones.
The fix. A new budget-tracking service keeps tabs on real cost, and when one of the expensive caching operations hits a limit, it tells newer sessions "not right now — older sessions get first dibs." That way the sessions don't all spike at once.
Reorganizing the work into three lanes. Instead of a dozen scattered agent conversations:
- Lane A: Building WorkWingman — the nightly automated build and test suite.
- Lane B: Tools and infrastructure — keeping the plumbing working.
- Lane C: Business — research, financials, planning.
Each lane has a model hierarchy: Haiku for the easy stuff (just reading, basic searches), Sonnet for normal tasks, the top tier for the hard thinking. Everything still goes through the multi-model review gate before shipping.
The CI also moved. GitHub's cloud minutes ran out mid-month, so the automated tests moved to self-hosted runners (three of the home PCs) that are set up fresh after every test, never reused. This is more secure and cheaper — no rate limiting, no waiting for cloud capacity.
The council grew up: five independent reviewers, same night¶
The same night as the token fix, all five review councils (Codex, Gemini, local AI on the PC, plus two bigger models running on the gaming and streaming PCs) did their first full end-to-end review together and all passed. Two more model families announced for July 9.
This became the rule: no code ships without an independent review from a different AI with a different way of thinking than the one that wrote it. It's what changed when the scale got too big for one person to hand-check everything.
Related docs¶
- Engineer version: ../technical/making-of.md
- The business plan: ../business/README.md · business-overview.md
- overview.md · architecture.md · references.md
- equity-comp.md
- self-assessment.md
- learning-paths.md
- prep-studio.md
- interview-coaching.md
- interview-retro.md
- study-media.md
- Doc index: ../README.md