Engine map — Steps 1 to 9

Scoring-flow synthesis · complete phase inventory · what each phase does · tasks needed to ship it · status · open decisions
Calibre · session 2026-06-12 · rev 2 — 5.3 renumbered into Step 6 (6.0 entry gates / 6.2 overlay), sub-function → hard gate, 4-dim L1 weights, boost/penalty net cap ±0.5 · derived from SOURCE-OF-TRUTH.md §4 + matching-engine design + 2026-06-11 D5 mine completion
Built — shipped or mine fired
Design done — build not started
Plan drafted — awaiting approval
Blocked
Not started — no plan
Deferred
22sub-steps mapped 2built/shipped 1built/mine fired · awaiting ratification 3content drafted · awaiting ratification 6design done 5plan drafted 1blocked 4deferred (Step 4) 0no plan
Scoring synthesis

How the gates (6.0), L1 (6.1), overlay (6.2), L2 (7.2), verdict (8.1) combine

Six engine stages (entry gates, Layer 1 rubric, rules overlay, adjacency, Layer 2 LLM, verdict) each contribute differently to the final candidate verdict. This table shows what each stage produces and how it feeds the next. Renumbered 2026-06-12: the old "Step 5.3 industry filter" is split into 6.0 (drop gates, before L1) and 6.2 (boost/penalty + L2 flags, after L1).

Phase Components Purpose Score weightage How it contributes to the final composite / verdict
Entry gates
(Step 6.0)
sub_function_match gate (JD vs CV sub-function).
Ratified drop-type hard gates from the rules config (mined ex-5.3 content).
Eliminate non-starters before any scoring or rubric load.
Is the CV even applying to the right role?
None — absolute gates, pass/drop. Not part of any composite. Sub-function mismatch → drop with reason sub_function_mismatch (adjacency may salvage).
Any drop rule fires → eliminated with rule id as reason.
Survivors proceed to 6.1 scoring.
Layer 1
(Step 6.1)
4 deterministic dimensions: tag_overlap, seniority_delta, years_experience, red_flag_penalty.
Weights load per (sub_function, sub_vertical) cell.
Weights sum to 1.0.
Cheap quantitative filter.
Drops obvious misses before any LLM cost.
tag_overlap 0.40
seniority_delta 0.20
years_experience 0.20
red_flag_penalty 0.20
Dan tunes per sub-vertical at ratification.
Each dim scored 0–5 deterministically.
L1 composite = Σ (dim_score × weight) → 0–5.
Cutoff: ≥ 4.0 applied after the 6.2 overlay.
Rules overlay
(Step 6.2)
L2 flags (block_at_l2 / require_evidence).
Boost-penalty rules (±delta on composite).
(drop gates moved to 6.0.)
Recruiter heuristics layered on top of L1.
Catches geography, employer, language, certs that the rubric math can't.
No weights — boost/penalty deltas sum, then clip to net ±0.5 on the L1 composite (locked 2026-06-12).
By design a nudge for borderline candidates, never strong enough to overturn the rubric.
block_at_l2 → flagged but continues.
require_evidence → L2 must find quote for specified dim or that dim collapses to 1.
boost-penalty → net-capped ±0.5 on L1 composite; then threshold re-check ≥ 4.0.
Step 7.1 Adjacency LLM proposes adjacent skill matches (LPG → LNG).
Ratified config + live inference hybrid.
Expands positive-signal set before L2 fires.
Catches cross-sub-vertical fit (Tanker → LNG cargo).
Per-adjacency weight 0–1 (e.g. Tanker→LNG = 0.8).
Recomputes L1 tag_overlap: weight × 5 replaces the 0 the missed tag would score.
Adjacency evidence feeds L2 prompt.
Not a separate composite score — informs L2's skills_adjacency dim.
Layer 2
(Step 7.2)
5 LLM-scored dims: experience_alignment, skills_adjacency, technical_knowledge, tools_and_systems, education_certifications.
Plus 2 list axes: red_flags + strengths.
Plus 1 narrative: executive_summary.
Qualitative deep assessment with verbatim CV quotes.
Primary hallucination defence: every dim score must cite a quote.
Per-sub-function weights (config/rubric-weights.json), SEPARATE from L1 rubric weights.
Numeric defaults not locked yet — design intent: cargo-ops raises technical + tools; finance-fo raises education + certs.
Each scored dim 1–5 + quotes.
L2 composite = Σ (L2 dim × per-sub-function L2 weight) → 0–5.
L2 weights are SEPARATE from L1 rubric weights.
Step 8.1 Layer 3 verdict Threshold matching across L1 composite + L2 composite + red_flag severity. Final verdict band.
Both composites must clear thresholds independently — not summed.
No weights — fixed thresholds per composite (L1: 4.5 / 4.0; L2: 4.2 / 3.5 / 2.5) + red-flag severity gate. Strong yes: L1 ≥ 4.5 AND L2 ≥ 4.2 AND no red flag ≥ 4 severity.
Soft yes: L1 ≥ 4.0 AND L2 ≥ 3.5.
Soft no: L1 ≥ 4.0 AND L2 < 3.5.
Strong no: L1 dropped OR L2 < 2.5.
End-to-end flow

Full step-by-step flow — JD/CV scoring, end-to-end

Every action in the matching pipeline, sequenced by phase (prep → entry gates 6.0 → Layer 1 6.1 → rules overlay 6.2 → adjacency 7.1 → Layer 2 7.2 → verdict 8.1). Each row lists what consumes the output downstream and the open tasks to ship it.

# Step Action Input Output Used where Plan(s) Tasks needed to ship
PHASE A — pre-matching prep
1 1.1 JD ingested. Raw JD file (PDF/DOCX). Raw text. Consumed by Step 2 parser only.
Not used downstream.
matching-engine + matching-engine-impl. Decide code location (reuse cv-parser/extractor.ts or clone to jd-parser/extractor.ts).
Document shared dependency or extract to lib/extractor/.
Write extraction tests against 41-JD corpus.
2 2.1 JD parsed. Raw JD text. ParsedJD: must_have_skills, technical_knowledge, tools_and_systems, certifications, education, years_floor, seniority_floor, experience_description. L1: years_floor → years_experience dim.
L1: seniority_floor → seniority_delta dim.
L2: technical_knowledge → technical_knowledge dim.
L2: tools_and_systems → tools_and_systems dim.
L2: certifications + education → education_certifications dim.
L2: experience_description → experience_alignment dim.
6.0/6.2 rules: hsse_compliance + shift_or_24_7 → hard-gate triggers.
matching-engine + matching-engine-impl. Build jd-parser/types.ts (ParsedJD interface).
Build jd-parser/parser.ts (Claude tool-use call).
Build jd-parser/index.ts (public API).
Build scripts/batch-parse-jds.ts.
Write parser tests against fixture JDs.
3 3.2 JD normalised. ParsedJD + locked tag vocab. NormalisedJD: sub_function, sub_vertical, tags[], seniority_tier, must/nice tags. Rubric load (6.1): sub_function + sub_vertical → picks 22-cell rubric.
6.0 gate: sub_function → sub-function match gate.
L1: tags[] + must/nice → tag_overlap dim.
L1: seniority_tier → seniority_delta dim.
6.0/6.2 rules: sub_function + sub_vertical → rule scope matching.
L2: entire NormalisedJD passed to LLM as prompt context.
matching-engine + taxonomy-3level. Wait for 3.1 vocab lock BLOCKED — Dan + Jon ratification session pending.
Amend impl plan Tasks 4–6 with locked vocab + 3-level taxonomy.
Build jd-normaliser/normaliser.ts.
Build jd-normaliser/taxonomy-extension.ts (finance-fo + ops-back-office).
Test all 41 JDs round-trip.
4 5.1 CV ingested + parsed. Raw CV file. ParsedCV: work history, skills, education, certifications, years, employer history, location. L1: total years → years_experience dim.
L2: work history → experience_alignment + technical_knowledge + tools_and_systems quote source.
L2: education + certifications → education_certifications dim.
L2: full text → red_flags + strengths list candidates.
5.3: employer history → cv_employer_in / cv_employer_matches predicates.
5.3: location → cv_location_not_in predicate.
5.3: certifications → cv_missing_certification predicate.
5.1.1-cv-parser + 5.1.1-cv-parser-impl. Re-batch over full 107-CV set (queued).
OCR pass for Chloe Wong (Market Risk, Metals).pdf.
5 5.1.2 CV hardening. ParsedCV. Red-flag signals: career_gaps, short_tenure_clusters, stale_cv, role_downgrades. L1: all signals → red_flag_penalty dim.
6.0/6.2 rules: short_tenure_clusters → cv_consecutive_short_tenures predicate.
L2: role_downgrades → red_flags list candidates.
5.1.2-cv-parser-hardening + 5.1.2-cv-parser-hardening-impl. No open tasks (shipped).
6 5.2 CV normalised. ParsedCV + locked tag vocab. NormalisedCV: sub_function, tags[], inferred seniority, language. 6.0 gate: sub_function → sub-function match gate.
L1: tags[] → tag_overlap dim.
L1: inferred seniority → seniority_delta dim.
6.0/6.2 rules: sub_function → rule scope matching.
6.0/6.2 rules: tags[] → cv_tag_present predicate.
5.3: language → cv_missing_language predicate.
L2: entire NormalisedCV passed to LLM as prompt context.
taxonomy-3level (schema). Wait for 3.1 vocab lock BLOCKED.
Write impl plan for cv-normaliser/.
Build cv-normaliser/normaliser.ts.
Test all 107 CVs round-trip.
PHASE B — entry gates (Step 6.0)
7 6.0 Sub-function hard gate. JD.sub_function + CV.sub_function. Pass if equal; else drop. Mismatch → drop with reason sub_function_mismatch, before any scoring or rubric load.
Adjacency may salvage.
Not weighted — removed from the L1 composite.
matching-engine + matching-engine-impl. Build gate check in matching/entry-gates.ts.
Wire adjacency salvage path.
8 6.0 Ratified drop gates fire. NormalisedJD + NormalisedCV + ratified drop-type rules. drop per fired rule. Fires BEFORE L1 scoring (moved 2026-06-12 — was post-L1 as part of old 5.3).
drop → candidate eliminated with rule id as reason.
Survivors proceed to 6.1.
5.3-industry-filter + bootstrap-rubric-ext (content). Get hard-rules content from bootstrap mine DONE — xlsx awaiting Dan + Jon ratification.
Re-split xlsx hard-gate sheet: drop rules → 6.0 config; block_at_l2 / require_evidence → 6.2 config.
Run Dan + Jon scaffold-review session (pending since 2026-05-25).
Build matching/entry-gates.ts rule evaluation.
PHASE C — Layer 1 (Step 6.1)
9 6.1 Load rubric for JD's cell. NormalisedJD.sub_function + sub_vertical. 4 dimension weights (sum to 1.0) + must/nice signals + disqualifying signals. Drives the 4 dimension scorings + composite below. matching-engine (formulas) + bootstrap-rubric-ext (content). Get rubric content from bootstrap mine — 22 sub-vertical configs DONE — xlsx awaiting Dan + Jon ratification.
Re-emit xlsx weights for 4-dim scheme (sub_function_match moved to hard gate) before ratification.
Delete 4 wrong-taxonomy stubs in reference/rubric-configs/ (done).
Lock red-flag input source.
Amend impl plan Tasks 9–10 to key on (sub_function, sub_vertical).
10 6.1 Score tag_overlap. JD.tags[] + CV.tags[]. (overlap count ÷ JD required count) × 5. L1 composite via 0.40 default weight. same as 9. Build matching/layer1-hard-fit.ts.
Build config/rubric-weights.json (per-sub-vertical weights).
Test composite scoring across 107 × 41 = 4,387 pairs.
11 6.1 Score seniority_delta. JD.seniority_tier + CV.inferred_seniority. 0–5 by categorical match. L1 composite via 0.20 default weight. same as 9. same as 10.
12 6.1 Score years_experience. JD.years_floor + CV.total_years. 0–5 numeric. L1 composite via 0.20 default weight. same as 9. same as 10.
13 6.1 Score red_flag_penalty. Step 5.1.2 hardening signals. 5 if clean, lower with flags present. L1 composite via 0.20 default weight.
Counterintuitive: high score = no penalty applied.
same as 9. same as 10.
14 6.1 Compute L1 composite. All 4 dim scores + rubric weights. Composite 0–5. Σ (score × weight).
Cutoff 4.0 applied at next stage (after the 6.2 overlay).
same as 9. same as 10.
PHASE D — rules overlay (Step 6.2)
15 6.2 Boost-penalty fire. NormalisedJD + NormalisedCV + L1 composite + ratified boost-penalty rules. Net ±delta on L1 composite, clipped to ±0.5. Sum all firing deltas → clip to net ±0.5 → adjusted composite.
Cap locked 2026-06-12: rules are a nudge for borderline candidates, never strong enough to overturn the 4-dim rubric.
5.3-industry-filter + bootstrap-rubric-ext (content). Get boost-penalty content from bootstrap mine DONE — xlsx awaiting Dan + Jon ratification.
Update mined xlsx delta values for the ±0.5 net cap (was ±0.3–1.5) before ratification.
Build matching/rules-overlay.ts (6.2 module) with net-cap enforcement.
Wire threshold re-check after overlay.
16 6.2 Attach L2 flags. NormalisedJD + NormalisedCV + ratified block_at_l2 / require_evidence rules. Flags attached to the match record. block_at_l2 → flagged but candidate continues to L2.
require_evidence → L2 must find quote for specified dim or that dim collapses to 1 (enforced at the L2 evidence check).
same as 15. same as 15.
17 6.2 Threshold re-check. Adjusted L1 composite. Pass if ≥ 4.0. Below 4.0 → drop with reason composite_below_4_after_rules. same as 15. same as 15.
PHASE E — adjacency (Step 7.1)
18 7.1 Adjacency resolver. NormalisedJD + NormalisedCV + ratified adjacency config. Expanded match evidence + proposed adjacencies. Catches LPG→LNG style cross-skill fit.
Proposed adjacencies need ratification to count.
Output feeds L2 prompt as adjacency evidence.
matching-engine. Build matching/adjacency/config.ts (ratified adjacency loader).
Build matching/adjacency/resolver.ts (two-tier resolution).
Build matching/adjacency/live-inference.ts (Claude API call).
Build config/adjacency-ratified.json (seeded from bootstrap mine).
Test adjacency proposals against CV corpus.
PHASE F — Layer 2 (Step 7.2)
19 7.2 Single Claude API call. NormalisedJD + ParsedCV (full text) + adjacency evidence. Layer2Result struct. Only fires on Phase D survivors (~5–10% of pairs). matching-engine. Build matching/layer2-deep-assessment.ts.
Define prompt: read Step 6 rubric dimensions, find quotes per dimension.
Define per-dimension 1–5 scoring anchors in the L2 prompt (currently undefined).
Enforce verbatim quote citing.
Run only on Step 6 survivors to cap LLM cost.
Amend impl plan to collapse old 5.3 (deleted) + 7.2 into single LLM call.
20 7.2 Score experience_alignment. JD.experience_description + CV work history. 1–5 + evidence quotes. L2 composite weighting. same as 19. same as 19.
21 7.2 Score skills_adjacency. Adjacency evidence (from 18) + JD requirements. 1–5 + evidence quotes OR null. L2 composite weighting (null if adjacency didn't fire). same as 19. same as 19.
22 7.2 Score technical_knowledge. JD.technical_knowledge field + CV narrative. 1–5 + quotes. L2 composite weighting. same as 19. same as 19.
23 7.2 Score tools_and_systems. JD.tools_and_systems field + CV narrative. 1–5 + quotes. L2 composite weighting. same as 19. same as 19.
24 7.2 Score education_certifications. JD edu + cert fields + CV edu + cert fields. 1–5 + quotes. L2 composite weighting.
NOT a hard gate.
Hard cert requirements live in the 6.0/6.2 rules config.
same as 19. same as 19.
25 7.2 Generate red_flags list. Full CV narrative + JD context. List of { signal, evidence, severity 1–5 }. Feeds Step 8.1 verdict (Strong yes requires no red flag ≥ 4 severity).
Includes subjective patterns parser hardening missed.
same as 19. same as 19.
26 7.2 Generate strengths list. Full CV narrative + JD context. List of { signal, evidence }. Feeds executive_summary + recruiter UI. same as 19. same as 19.
27 7.2 Generate executive_summary. All of the above. 2–3 sentence narrative. Recruiter-facing summary.
Not scored.
same as 19. same as 19.
28 7.2 Evidence requirement check. Per-dimension quotes. Any dim without quote → force score 1 + low_evidence flag. Primary hallucination defence. same as 19. same as 19.
29 7.2 Compute L2 composite. 5 scored dims × per-sub-function L2 weights. composite_l2_score 0–5. L2 weights are SEPARATE from L1 rubric weights.
Configured per sub_function, not per cell.
same as 19. same as 19.
PHASE G — verdict synthesis (Step 8.1)
30 8.1 Strong yes check. L1 ≥ 4.5 AND L2 ≥ 4.2 AND no red flag ≥ 4 severity. Verdict band: Strong yes. All three conditions must hold. matching-engine + matching-engine-impl. Build matching/layer3-verdict.ts.
Wire to recruiter override UI (Step 10 surface).
Log overrides with reason → feeds back to rubric weight tuning.
Decide: Soft no (3.5–3.99) → skip Step 7 LLM, or run it?
Update design doc with locked score bands.
31 8.1 Soft yes check. L1 ≥ 4.0 AND L2 ≥ 3.5. Verdict band: Soft yes. If Strong yes failed. same as 30. same as 30.
32 8.1 Soft no check. L1 ≥ 4.0 AND L2 < 3.5. Verdict band: Soft no. L1 passed but L2 evidence weak. same as 30. same as 30.
33 8.1 Strong no check. L1 dropped OR L2 < 2.5. Verdict band: Strong no. Catch-all for failures. same as 30. same as 30.
34 10.x Recruiter override. Recruiter UI input. Verdict overridden + reason logged. Optional.
Feeds rubric weight tuning loop.
Step 10 productisation surface (not yet planned). TBD — Step 10 design pending.
Phase inventory

Engine map — phases, status, tasks to ship

The 19-sub-step phase inventory across Steps 1–9. Each sub-step lists the plan(s) covering it and the open tasks needed to ship it. Statuses honour the 2026-06-11 D5 mine completion.

Step + Phase
Plan(s) covering it
Tasks needed to ship
Step 1 — JD ingested
1.1JD upload + text extraction
Reads PDF/DOCX/DOC JD file → raw text string. Inherits CV extractor pattern.
Design done
  • Decide code location — reuse cv-parser/extractor.ts or clone to jd-parser/extractor.ts
  • If reuse: document the shared dependency
  • If clone: extract shared extractor to lib/extractor/ first
  • Write extraction tests against the 41-JD corpus
Step 2 — JD parsed
2.1JD parser build
Claude API call: raw JD text → ParsedJD (role, responsibilities, requirements, seniority, must/nice tags).
Design done
  • Build jd-parser/types.ts — ParsedJD interface (impl plan Task 1)
  • Build jd-parser/parser.ts — Claude tool-use call (Task 2)
  • Build jd-parser/index.ts — public API parseJD(file) (Task 3)
  • Build scripts/batch-parse-jds.ts — parse all 41 JDs in reference/JD/
  • Write parser tests against fixture JDs
Step 3 — JD normalised + tagged
3.1Tag-vocab bootstrap (extended)
Mines 107 CVs × 41 JDs → emits locked tag vocab + hard rules (6.0/6.2 input) + rubric content (Step 6 input). 2026-06-11: mine fired, xlsx generated, awaiting Dan + Jon ratification.
Built — mine fired, awaiting ratification
  • Draft plans/amendment-3.1+5.3+6.1-bootstrap-rubric-extension.md — combined amendment plan
  • Extend existing harness src/lib/vocab-bootstrap/ to mine 3 artefacts
  • Fire live run (~USD 20–40 Claude API)
  • Run Dan + Jon ratification session (~2h, 3 worksheets) NEXT
  • Lock all 3 artefacts: tag vocab + hard rules + rubric content
3.2JD normaliser build
ParsedJD → NormalisedJD (sub-function, sub-vertical, tag list, seniority tier, must/nice-to-have tags).
Design done
  • Wait for 3.1 ratification + vocab lock BLOCKED on 3.1 ratification
  • Amend impl plan Tasks 4–6 with locked vocab + 3-level taxonomy
  • Build jd-normaliser/normaliser.ts — Claude tool-use call
  • Build jd-normaliser/taxonomy-extension.ts — sub-functions finance-fo + ops-back-office
  • Test all 41 JDs round-trip parse → normalise
Step 4 — Sourcing
4.1Apify candidate scraping
Apify scrapes candidate profiles from LinkedIn + public sources.
Deferred
  • Write Step 4 design doc covering 4.1–4.4 end-to-end
  • Pick Apify actor(s) — LinkedIn profile + search-by-keyword
  • Define rate-limit + cost model (per-search + per-profile)
  • Build sourcing/apify-client.ts
  • Wire NormalisedJD → search-query construction
4.2Apollo enrichment
Apollo enriches with company + decision-maker data.
Deferred
  • Get Apollo API access (Calibre opex)
  • Define enrichment fields needed (employer history, education, current role)
  • Build sourcing/apollo-client.ts
  • Cache enrichment results in DB to avoid re-billing
4.3Anymail Finder
Anymail Finder produces verified email addresses.
Deferred
  • Get Anymail Finder API access
  • Build sourcing/email-finder.ts
  • Define verification confidence threshold (only ≥ 0.8 confidence)
  • Cache verified emails to avoid re-billing
4.4Employer-side BD scraping
Sources hiring agencies (not candidates). Grant-required per Annex 3 AD1.
Deferred
  • Write design doc — fully undesigned at present
  • Define sourcing targets — LinkedIn Jobs, careers pages, agency directories
  • Build sourcing/bd-scraper.ts
  • Define output schema — hiring employer candidates with rationale
Step 5 — CV parsed + normalised + filtered
5.0Candidate-record merge (bridge between Step 4 and Step 5.1.1)
Merges scraped LinkedIn / Apollo profile JSON + uploaded CV file blob into one CandidateRecord. Without this, Step 5.1.1 only knows how to parse file blobs — scraped JSON has no defined ingestion path. Design: plans/step-5.0-candidate-merge-design.md.
Plan drafted
  • Greenlight design (precedence cascade + union strategy + schema shape)
  • Write impl plan step-5.0-candidate-merge-impl.md
  • Build calibre-app/src/lib/candidate-merge/
5.1.1CV parser core
Reads CV file → ParsedCV structured fields. 66+ CVs batch-tested.
Built — shipped
  • Re-batch over full 107-CV set (queued — runs after normalisers + Layer 1 ship)
  • OCR pass for Chloe Wong (Market Risk, Metals).pdf — only known unreadable
5.1.2CV parser hardening
BOM handling, stale-CV detection, career-gap signals, short-tenure clusters. Produces the red-flag-penalty inputs that feed the Step 6.1 rubric.
Built — shipped
  • No open tasks — merged on feat/parser-hardening
5.2CV normaliser
ParsedCV → NormalisedCV using same vocab as 3.2 — symmetric tagging.
Blocked
  • Wait for 3.1 vocab lock BLOCKED on 3.1 ratification
  • Write impl plan for cv-normaliser/
  • Build cv-normaliser/normaliser.ts
  • Test all 107 CVs round-trip parse → normalise
5.3LLM inference match
Redundant with Step 7.2. Merged into 7.2 this session.
Deleted
Planretired
  • Edit SOT §2 + §4 — remove Step 5.3 references
  • Amend pipeline-1.1+2.1+3.2+6.1+7.1+7.2+8.1-matching-engine-impl.md — collapse 5.3 tasks into 7.2
  • Add change-log entry to SOT §8 explaining the merge
5.3Industry-expert filter
Renumbered 2026-06-12 → Step 6.0 (drop gates, before L1) + Step 6.2 (boost/penalty + L2 flags, after L1). The numeric ID 5.3 is retired — it described execution order incorrectly (fired after Step 6 despite the Step 5 number). Content and tasks now tracked under Step 6. Plan filenames keep the 5.3 prefix.
Renumbered → 6.0 + 6.2
  • All open tasks moved to the 6.0 / 6.2 rows under Step 6
Step 6 — Hard-fit filter (gates + Layer 1 + overlay, deterministic)
6.0Entry gates new 2026-06-12
Fires BEFORE L1 scoring. Sub-function gate (JD vs CV must match; adjacency may salvage) + ratified drop-type rules from the mined ruleset (ex-5.3). Eliminates non-starters with rule id as reason before any rubric math runs.
Content drafted, awaiting ratification
  • Re-split xlsx hard-gate sheet: drop → 6.0 config; block_at_l2 / require_evidence → 6.2 config
  • Run Dan + Jon scaffold-review session — combine with 3.1 ratification
  • Build matching/entry-gates.ts — sub-function gate + drop-rule evaluation
  • Wire adjacency salvage path for sub-function mismatches
6.1Rubric loader + composite scorer
Loads sub-vertical rubric → computes 4-dim composite (tag overlap 0.40, seniority delta 0.20, years 0.20, red-flag 0.20) — sub-function match moved to the 6.0 entry gate 2026-06-12. Composite ≥ 4 passes (checked after 6.2 overlay). 2026-06-11: 22-cell rubric content drafted in bootstrap xlsx, awaiting ratification.
Content drafted, awaiting ratification
  • Delete 4 wrong-taxonomy stub configs in reference/rubric-configs/
  • Get rubric content from bootstrap mine (3.1 extension) — 22 sub-vertical configs
  • Re-emit xlsx weights for the 4-dim scheme (0.40/0.20/0.20/0.20) before ratification
  • Lock red-flag input source — parser hardening output (current) vs 6.2 rules
  • Amend impl plan Tasks 9–10 to key on (sub-function, sub-vertical)
  • Build matching/layer1-hard-fit.ts
  • Build config/rubric-weights.json — per-sub-vertical weights
  • Test composite scoring across 107 × 41 = 4,387 pairs
6.2Rules overlay new 2026-06-12
Fires AFTER L1 composite. Boost/penalty rules — deltas sum then clip to net ±0.5 (locked 2026-06-12) — plus block_at_l2 / require_evidence flag attachment for L2, then threshold re-check ≥ 4.0.
Content drafted, awaiting ratification
  • Update mined xlsx delta values for the ±0.5 net cap (was ±0.3–1.5) before ratification
  • Build matching/rules-overlay.ts — boost/penalty + net-cap enforcement + flag attachment
  • Wire threshold re-check → drop reason composite_below_4_after_rules
Step 7 — Deep assessment (Layer 2, qualitative LLM)
7.1Adjacency resolver
Finds non-obvious skill matches via LLM proposal + ratified config (hybrid pattern).
Design done
  • Build matching/adjacency/config.ts — ratified adjacency loader
  • Build matching/adjacency/resolver.ts — two-tier resolution (config → live inference)
  • Build matching/adjacency/live-inference.ts — Claude API call for proposals
  • Build config/adjacency-ratified.json — seeded from bootstrap mine
  • Test adjacency proposals against the CV corpus
7.2Evidence-grounded LLM assessment
Per-rubric-dimension score with verbatim CV quotes. Now absorbs old Step 5.3.
Design done
  • Amend impl plan — collapse 5.3 + 7.2 into single LLM call
  • Build matching/layer2-deep-assessment.ts
  • Define prompt: read Step 6 rubric dimensions, find quotes per dimension
  • Enforce: every score MUST cite a verbatim CV quote
  • Run only on Step 6 survivors (composite ≥ 4) to cap LLM cost
Step 8 — Shortlist (4 fit bands)
8.1Layer 3 verdict synthesis
Composite of L1 + L2 → verdict band. Locked this session: 4.5+ Strong yes / 4.0–4.49 Soft yes / 3.5–3.99 Soft no / <3.5 Strong no.
Design done
  • Decide: Soft no (3.5–3.99) → skip Step 7 LLM, or run it?
  • Update design doc with locked score bands
  • Build matching/layer3-verdict.ts
  • Wire to recruiter override UI (Step 10 surface)
  • Log overrides with reason → feeds back into rubric weight tuning
Step 9 — Personalised outreach
9.1Personalisation engine
LLM generates personalised email subject + body per shortlisted candidate.
Plan drafted
  • Write Step 9 design doc covering 9.1–9.4 end-to-end
  • Define deep-research input (CV evidence + JD pitch + Calibre tone guide)
  • Build prompt templates per vertical (Calibre brand voice)
  • Build outreach/personalisation.ts
9.2Email send
Sends email via existing infra.
Plan drafted
  • Decide: Instantly vs Smartlead
  • Get API access for chosen tool
  • Build outreach/email-send.ts — delivery + tracking
  • Define warm-up + domain rotation strategy
9.3LinkedIn sequencing
LinkedIn connection request + InMail sequencing.
Plan drafted
  • Decide tool — LinkedHelper, Phantombuster, or manual outreach queue
  • Build outreach/linkedin-sequence.ts
  • Define touchpoint cadence (connect → wait → InMail → follow-up)
  • Build copy templates for connection request + InMail
9.4Response handling
Auto-enrich DB from response signals (open / replied / disinterested).
Plan drafted
  • Define response signal taxonomy (interested, deferred, declined, unreachable)
  • Build webhook handler for email-tool callbacks
  • Build outreach/response-handler.ts
  • Auto-tag candidate records based on response
  • Wire to recruiter notification (digest or inbox)

Open decisions — your call

10 open · 7 resolved
D1
Done 2026-06-12. 5.3 split locked: drop gates → 6.0 (before L1), boost/penalty + block_at_l2 / require_evidence → 6.2 (after L1). Boost/penalty net-capped at ±0.5. Step ID 5.3 retired.
ResolvedRenumbered across engine map + SOT + plan amendments.
D3
Open. Soft no (3.5–3.99) candidates → skip Step 7 LLM verification, or run it?
Rec: skipDoubles LLM cost otherwise
D5
Open. Schedule Dan + Jon ratification session for the bootstrap xlsx (3.1 vocab + 6.0/6.2 rules (ex-5.3) + 6.1 rubric content). Mine fired 2026-06-11. Note 2026-06-12: xlsx needs re-emit first — 4-dim weights, drop/flag rule split, ±0.5 delta cap.
Rec: yesBiggest single unblock — gates 3.2 / 5.2 / 6.0 / 6.1 / 6.2
D6
Open. Schedule Daniel + Jon scaffold-review session for the rules config (ex-5.3, now 6.0/6.2) (pending since 2026-05-25 per STATE-LOG D8).
Needs Dan's calendar. Combine with D5 ratification session.
D11
Open. New 2026-06-11. Greenlight step-5.0-candidate-merge-design → write impl plan.
Precedence cascade (cv_file > linkedin > apollo > anymail) + union strategy + schema shape need approval.
D12
Deferred 2026-06-11. Step 4 sourcing build deferred — orchestrate against existing 41 JDs × 107 CVs first. Design plan stays in workspace (audit-defensible) but no impl plan / no build until matching engine proves itself end-to-end.
DeferredRevisit after Step 7 + 8 ship.
D13
Open. New 2026-06-11. Greenlight pipeline-9-outreach-design → write impl plan.
Locks email-first cadence + Instantly vs Smartlead (TBD) + LinkedIn tool (TBD).
D14
Open. New 2026-06-11. Lock Step 6.1 red-flag-penalty input source — parser hardening output (current) vs 6.2 boost/penalty rules.
D1 resolved 2026-06-12 (split locked as 6.0/6.2). If red-flag moves to 6.2 it leaves the L1 composite and the 4-dim weights need re-tuning — rec: keep parser hardening as the source.
D15
Open. New 2026-06-11. Write Step 5.2 impl plan after Step 3.1 vocab locks.
No design plan; schema lives in amendment-3.2+5.2+6.1-taxonomy-3level. Needs separate impl plan.
D16
Open. New 2026-06-11. Split pipeline-1.1+2.1+3.2+6.1+7.1+7.2+8.1-matching-engine-* into 14 per-sub-step files?
Rec: don't splitCoupled flow reads better as one document. Covers steps: header makes scope greppable.
D17
Open. New 2026-06-11. Verify calibredocs.3nm.io live deploy reflects this session's projection logic fix (slugify) + engine map publication.
Wait for next Vercel deploy (auto on git push to main). Spot-check 3 plan URLs + index page engine-map link.
D2
Done 2026-06-11. Combined bootstrap amendment shipped as amendment-3.1+5.3+6.1-bootstrap-rubric-extension — single plan covering vocab + 5.3 rules + 22 rubrics.
ResolvedGreenlight tracked under D5.
D4
Done 2026-06-11. SOT §2 + §4 + §6 + §8 updated. 5.3 (LLM match) retired, 5.3 (industry filter) position confirmed (between Step 6 and 7), Step 8 bands locked.
ResolvedCommit fca2e81.
D7
Done 2026-06-11. 33-file working tree committed + 7 more commits this session. No more uncommitted work.
ResolvedCommits fca2e81 → 0af6f45.
D8
Done 2026-06-11. Step 5.0 registered in SOT §4. Design plan drafted at step-5.0-candidate-merge-design.
ResolvedGreenlight tracked under D11.
D9
Done 2026-06-11. Step 4 sourcing design plan drafted at pipeline-4-sourcing-design covering 4.1–4.4.
ResolvedGreenlight tracked under D12.
D10
Done 2026-06-11. Step 9 outreach design plan drafted at pipeline-9-outreach-design covering 9.1–9.4.
ResolvedGreenlight tracked under D13.