The cascade
Pick a posting. Step it through.
Ten postings, run through the real pipeline on this machine. The companies are invented and the posting text is adapted from real listings, because the system's output is a structural assessment of an employer and publishing one about a named company would be indefensible. The mechanism and the output shape are unchanged.
Claim 1 · The cascade
Three gears, because reading is the expensive part
The system reads job postings with a language model, and a language model charges by the word. A posting file averages about 1,800 tokens, which is roughly 1,300 words. Reading all of a 150-posting batch up front costs around 270,000 tokens before a single judgment has been made, and most of those postings are going to be rejected on something visible in the first forty lines.
So the pipeline never runs a slower gear than it needs. Each stage is allowed to read only as much as it needs to make its own decision, and a posting that dies early never gets read in full at all. Reading headers first brings that 270,000 down to roughly a tenth.
| Gear | Runs on | Reads | Costs | Produces |
|---|---|---|---|---|
| Gate | Every posting | Header plus ~40 lines | Seconds | Pass, or a kill code |
| Triage | Gate survivors | Full text | ~30 seconds | Provisional score, one line |
| Forensic | Only postings a human names | Full text, ten lenses | Full analysis | Verdict card and eight sections |
A posting killed at the gate is not a posting the system failed to analyse. It is the system declining to spend money proving something already visible in six words.
The gate is deliberately mechanical. Eight codes, no prose, no reasoning paragraph. A killed posting gets one line and nothing more, because writing an explanation for a rejection nobody will read is the exact cost the cascade exists to avoid.
Where this shows up in the demo. Three of the ten postings stop at the gate. The greyed-out stages are not missing data. They are the cost argument working. Open the demo.
The rubric
Six weighted components, four bands, eight codes
A score exists so that a pile of postings can be ranked against each other. It is not a quality rating. Six components sum to 100, then three structural penalties subtract.
The bands
The eight gate codes
The two override rules
These are what separate the gate from keyword matching, and both exist because the gate was wrong first. Each was added after a real miss, and each now lives in the skill file rather than in a notes document, because a rule that only exists in an incident log does not fire.
- NOT-PM fires on distance from the money, never on verb form. Coordination verbs are how every job description writes its responsibilities. The qualifications are what tell two jobs apart. Before killing, the gate reads the required qualifications and asks one question. If this person is bad at their job, do the company's unit economics move? If yes, it survives and the score decides. The system learned this by killing a marketplace operations role that a later full analysis scored 43, with economic proximity as its highest component.
- DOMAIN-LOCK reads grammar, not an industry list. The original rule was a list of industries, and a list of industries will never be complete. It now fires on the difference between "you have done the work yourself" stated as a minimum qualification and "is a strong plus" stated as a preference. Required against preferred is the test. Three senior roles got through the list-based version because none of them were in a named industry, and all three carried a required lived credential.
Both overrides are visible in the demo. One posting is killed on DOMAIN-LOCK by a single sentence of grammar. Another reads like pure coordination work and survives, because its qualifications name the company's margin lever. See both.
Claim 2 · Evaluation
The stage that checks the work cannot be the stage that did it
Later in the pipeline, once a role is worth applying to, the system tailors a résumé against that posting. Then it reviews it. The problem is that the agent that did the tailoring is the worst possible reviewer. It read the forensic report, it knows what it was optimising for, and it remembers which lines it moved. That is exactly the context a real hiring manager does not have, so a reviewer holding it will confirm the tailoring rather than test it.
So the review runs in a separate agent with no access to the conversation, holding only what a real screener holds, which is the job description and the résumé. Nothing else. It is not told the résumé was tailored. Contamination invalidates the run rather than degrading it. If the reviewer's output mentions the forensic report or the pipeline at all, the prompt leaked and the round is discarded and relaunched.
Three rules that make its output usable
A blind reviewer is confidently wrong in specific, repeatable ways. Each rule below was written after it was wrong in that exact way.
The reviewer is a fresh reader, not a fresh oracle. Its value is in how it reads the document, not in its facts about the document.
- Recompute every date and tenure claim before acting on it. A reviewer once reported "eleven months" for a tenure the résumé dates as twenty-three, because it had assumed a current date about a year stale and done the arithmetic from there. Acting on that finding would have produced a factually wrong edit to a correct résumé.
- Safe fixes apply automatically, and then get re-checked anyway. A fix is safe only if it reorders, emphasises, or rewords material already on the page. After applying, two checks run again. The résumé must still render to one page, and a diff against the baseline must show no changed number, date, title, employer, or scope. A safe fix that breaks either one was not safe.
- "Not evidenced" means unwritten, not absent. The reviewer can only see what survived the compression of a career into two pages. Every not-evidenced finding is routed to a question for the human, never resolved by inference. A reviewer finding a hole is not permission to fill it.
The correction that matters most flows backwards. The human review is the only point where ground truth enters the system, because every other stage reasons from the résumé and cannot know when the résumé itself is wrong. On one pass, human comments caught two factual errors that had been in the baseline for every application ever sent. Four blind reviewers and the tailoring pass had all missed them, because all five were reading the same wrong document.
Claims 3 and 5 · Architecture
Files are the record. The database is the intent.
Everything the pipeline produces lands on disk as a file. Posting text, triage tables, forensic reports, the skill's own memory. That is the system of record and nothing else is allowed to be.
A browser console reads those files through a scanner and renders them. The console physically cannot change pipeline data. Its only writes are decisions that originate in the browser, and those go to a separate store. The seam is deliberate. A reading surface that can also mutate the thing it reads eventually will.
Why the queue looks like this
The console runs in a sandbox that forbids executing anything. A button cannot run the pipeline. So a button writes a request, and something else drains it. Today that drain is a human starting a session. Tomorrow it can be a service on a timer, and the interface does not change, because the interface was never calling the pipeline in the first place. A constraint pushed the design toward a queue, and the queue turned out to be the right shape anyway.
State is derived, never stored
A status field that says a stage is complete is a claim. A file on disk is evidence. The scanner never reads a status column to decide whether a stage ran. It looks for the artifact that stage would have produced.
# Stage completion is read from artifacts on disk, never from a status field. # A row claiming "triaged" with no triage file is not triaged, it is a lie # the crack detector is supposed to find. def _application_stages(app_dir, row): return { "tailored": (app_dir / "resume.md").exists(), "hm_review": (app_dir / "hm-review.md").exists(), "outreach": (app_dir / "outreach.md").exists(), }
Claim 4 · Observability
The crack detector looks for things that fell out
Most monitoring watches whether a system is running. This one watches whether items are falling out of decision stages, which is a different question and the one that actually costs something here. A pipeline that drops a good role loses an opportunity silently, and nothing about that failure looks like an error.
So a scanner runs across every file and hunts for five specific shapes of loss.
| Crack | What it means | Why it happens |
|---|---|---|
| Sourced, never triaged | A posting was saved and no row in any triage file points at it | Collapsed into a group, or skipped in a batch |
| Scored high, never audited | Above the forensic threshold with no report | Nobody named it, and nothing chased it |
| Audited, no outcome | A full report exists and nothing was ever decided | The most expensive stage, wasted |
| Parked in the middle band | Conditional, with the named unknown never resolved | Conditional is where decisions go to wait |
| Invisible in the hand-off file | Real on disk, absent from the summary a human reads | The summary file is overwritten each run, not appended |
The last one is the sharpest, because it is the only crack that is invisible from inside the thing a person actually reads. The detector finds it by comparing the row count in the summary against the number of posting files with a matching first-seen date. The gap is the number of roles a cold read would never know existed.
Semantic observability. Not CPU and latency, but whether an item that entered a decision stage ever left it.
The part most demos leave out
Where this breaks
Four weaknesses, each with the mechanism, how it fails, and why it was an acceptable trade at this size. None of them is hypothetical and none of them is fixed.
Identity resolution is regex string-matching
There is no stable identifier for a role across the pipeline. The state file has a real composite key. The markdown reports refer to roles by whatever human label the model wrote. The two are joined by normalising case, deleting parentheticals, and stripping a stopword list, then checking whether one eighteen-character substring appears inside the other.
def norm(s):
s = s.lower()
s = re.sub(r"\(.*?\)", " ", s) # drop "(2 locations)", "(Labs)"
s = re.sub(r"[^a-z0-9]+", " ", s)
for word in ("senior","sr","staff","principal","product",
"manager","pm","director","of","the","and"):
s = re.sub(rf"\b{word}\b", " ", s)
return " ".join(s.split())
# and the join itself, in the scoreboard:
a.includes(r.role.split('(')[0].trim().slice(0, 18))
- Fails when
- A posting reappears as "Principal PM" where the report said "Principal Product Manager". The stopword list strips both to the same thing often enough to feel reliable, and then does not.
- How it lands
- A silent join failure. Not an error, not a warning. A role simply stops being matched to its own audit.
- The trade
- At a hundred and fifty roles a human notices. Stable identifiers mean a schema, a migration, and an identity service for a single-user system, and the normaliser was twenty minutes. At ten times the volume this is the first thing that has to change.
Markdown is the schema
The reports are prose written by a language model and parsed back out with regular expressions. The producer and the parser are coupled by nothing but convention. A verdict block is found by matching a fenced code block containing a word in capitals, an em dash, and a number over one hundred.
- Fails when
- The model formats a verdict slightly differently. The regex returns nothing, the card is skipped, and the role vanishes from the console.
- How it lands
- Absence and parse failure are indistinguishable. A missing card looks exactly like a role that was never audited. This is the worst of the four, because it corrupts the observability layer that is supposed to catch the others.
- The trade
- Structured output would fix it and would also make the reports unreadable as documents. The reports are read by a human far more often than by the parser, and optimising the parser at the reader's expense would have been the wrong call. The right fix is emitting both, and it is not built.
The queue has no idempotency
A run carries a stage string and nothing else. There is no claim, no completion marker, no event log, and no lease.
- Fails when
- Two sessions drain the same request, or one crashes mid-stage.
- How it lands
- Duplicated work in the first case. In the second, a stage that nobody is working and nothing reports as stalled.
- The trade
- One operator, one machine, work started deliberately. The concurrency this protects against does not currently exist. It starts existing the moment the drain becomes a service on a timer, which is the stated next step, so this is a known prerequisite rather than a discovered bug.
The scoring model is unvalidated
Six weighted components, four calibrated bands, three penalties. The weights were reasoned about carefully and have never been tested against an outcome.
- Fails when
- It does not fail. It produces a confident number with no evidence that the number predicts anything, which is worse than failing.
- How it lands
- Every entry in the prediction log still reads
PENDING. The loop that was supposed to calibrate the rubric against what actually happened has never closed once. The zero on the hero counter is this. - The trade
- There is no trade to claim here. Outcomes arrive on a hiring timeline, which is months, and the system has not been running long enough. What is worth saying is that all the real learning so far came from a different loop entirely. Manual correction, written back as calibration rules, is what has actually made the auditor sharper. Two of those corrections are now hard rules in the gate, and both came from a human noticing a miss, not from a scoreboard.
What is mine
Stated plainly, because the alternative discounts everything
Claude Code generated most of the implementation. I designed the pipeline, specified every stage, wrote the rubric and the gate rules, made every architectural call, and corrected the output continuously. The judgment layer is a skill definition I wrote and have revised against its own misses. The code underneath it was largely written to my direction.
A reviewer who suspects hidden assistance discounts the whole artifact. A reviewer who sees it stated reads it as fluency with the tool, which is what these roles are asking for. So here is the sentence, ready to paste into a form field.
I designed and directed this pipeline end to end, wrote the rubric and gate rules, and made every architectural call, while Claude Code generated most of the implementation under my direction.
The specific division
| Mine | Generated to my direction |
|---|---|
| The three-gear cascade and the cost argument for it | The poller, the scanner, the console rendering |
| The six components, their weights, the four bands, the three penalties | The parsers that read the reports back out |
| The eight gate codes and both override rules | The build scripts and the rendering pipeline |
| The decision to run review in an uncontaminated agent | The agent plumbing that launches it |
| The files-are-record, database-is-intent seam | The intent store wiring |
| Every calibration rule, each written after a specific miss | This page |