Most interview advice ends at "use STAR". That is a structure, not a measurement, and when you are rehearsing alone you cannot hear whether your answer has one.
I built a small offline checker to make the shape visible before you say it to a person. It is one HTML file, no build step, no network calls. This post is about the interesting part: what you can actually detect with word-level heuristics, and where they lie to you.
The four checks
const SIT = /\b(when|last|previous|at the time|our|my team|quarter|project|incident|release|customer)\b/i; const TASK = /\b(my job|i had to|i was asked|the goal|needed to|responsible for|target|deadline)\b/i; const ACT = /\b(i (pulled|built|wrote|shipped|ran|asked|tested|changed|designed|debugged|led)\b|then i|so i|first i)/i; const RES = /\b(result|outcome|went from|dropped|increased|reduced|saved|ended up|as a result|impact)\b/i; Four booleans is a crude model, but it is the right crude model for the failure people actually have. In my own drafts the missing piece was never the story — it was the last sentence. The answer stopped at what I did, not at what changed.
That is why the checker's most useful output is not a score. It is the line:
✗ Result — End with a before/after number.
Numbers are the cheapest signal
Counting quantities is a two-line regex and it correlates with the thing interviewers are listening for:
const quantities = raw.match(/\b\d+(\.\d+)?%?|\b(one|two|...|million)\b/gi) || []; Zero quantities is almost always a real problem. "We made the pipeline faster" is a claim; "p95 went from 2.4s to 610ms" is evidence. The checker cannot tell whether your number is true — only whether you offered one.
Filler density, not filler count
Counting "um" and "like" per answer is useless: short answers look clean and long answers look terrible. Divide instead:
const density = fillers.length / words; // flag when > 1 per 40 words One filler every 40 words is invisible to a listener. One every 8 words is the thing they remember about you.
Ownership: "I" versus "we"
const owned = (text.match(/\bI\b/g) || []).length; const team = (text.match(/\bwe\b/gi) || []).length; This one needs care. "We" is correct when the team genuinely did the work, and forcing "I" into a team story reads as credit-taking. The signal worth acting on is only the asymmetric case: we > I in an answer that is supposed to be about your decisions.
Where the heuristics fail
- Paraphrase beats vocabulary. "My remit was to..." never matches
my job. A structurally perfect answer can score 2/4. - False positives from context words. "Our release was late" matches the situation regex even when it is the result.
- No semantics. The tool cannot tell that your action was copying a file and someone else's was redesigning a schema. Both are "I changed".
- Speaking time is an estimate. words ÷ 140 wpm assumes a conversational pace; a nervous speaker at 180 wpm gets a materially shorter answer than the number suggests.
I left all of these in rather than adding an LLM call, because the constraint is what makes it useful: it runs in a browser tab with the network off, on an answer you would never paste into a service.
What the tool does with the text
Beyond flags, it reconstructs a skeleton from your own sentences — picking the first situation-shaped sentence, the task sentence, then two action sentences and the result — so your next attempt starts from your words, not a template:
SITUATION - Last quarter our checkout error rate doubled after a release. TASK - My job was to find the cause before the peak weekend. ACTION - I pulled the logs for the three biggest failures... RESULT - Error rate went from 4.1% to 0.6% in two days. Source and live demo (MIT, single file): star-answer-checker · repo
The honest limit of this approach is that structure is the cheap half. The expensive half is delivery under follow-up questions — someone interrupting your result to ask "why was the retry loop there?" and watching whether the story survives. I practice that with MockLive, which records the answer, asks adaptive follow-ups, and quotes your own words back with the audio.

