Journal
3 July 2026·6 min readProductEngineeringGrounding

Grounded plans, and a nudge that shows up: what we shipped this week

Two problems, one week. First: our plans were smart, but they were floating — no anchor, no way to know whether a mentee was being measured against a real role or a plausible sentence. Second: too many people booked a slot, uploaded nothing, and vanished into inbox purgatory. Here's what we shipped, why it matters, and how it works under the hood.

The problem with unanchored plans

A plan that reads well is not a plan that helps. If two mentees say they want to be a "Cloud Engineer" and the model quietly interprets that as two different jobs, the milestones diverge, the skill checklist drifts, and the standard we hold everyone to becomes whatever the LLM felt like that afternoon. That is not mentorship. That is autocomplete with confidence.

So we grounded the plan generator in a benchmark catalogue — a small, curated set of role profiles (skills, tasks, aliases) modelled on the O*NET taxonomy. Every plan now resolves the mentee's target to a canonical position before the model writes a single milestone.

How grounding actually resolves

The resolver runs three tiers, in order, and stops at the first hit:

  • Exact name match against the benchmark's display name.
  • Alias match — the same role expressed differently ("SRE", "Site Reliability Engineer", "Reliability Engineer").
  • Semantic match — a pgvector cosine search over role embeddings, gated at a 0.65 similarity threshold. This catches near-neighbours the alias list will never cover ("Senior Cloud Platform Engineer" landing on cloud_engineer).

If all three miss, we say so — the plan is generated ungrounded and we flag it. Silent fallback is how you end up with plans that look aligned and aren't.

Measuring whether grounding is doing anything

Grounding is only worth the complexity if we can prove it fires. Every generated plan now writes a sources record — which tier matched, which position, at what similarity, how many skills and tasks came from the benchmark. It's one JSON column on the plans table and it answers the only question that matters:

For plans generated this week, what share hit an exact or alias match, what share leaned on the semantic fallback, and what share fell through entirely?

Now we can tune the threshold, expand the alias list where the semantic tier is doing too much work, and add benchmark rows where we're falling through repeatedly — with evidence, not vibes.

CV and LinkedIn: same path, no special-casing

Grounding tells us the target. The mentee's own profile tells us where they're starting. We already accept both a CV upload and a LinkedIn profile exported as PDF — both are stored, both are text-extracted, and both feed into the same block of the generation prompt. There's no LinkedIn OAuth path and there doesn't need to be one; the PDF export contains everything the plan needs to reason about experience.

The 24-hour reminder — sent once, and only when needed

The second thing we shipped is much smaller and, honestly, overdue. A meaningful number of signups book their slot, then never come back to complete the workflow — no CV, no target role, no context to plan against. A single reminder, timed right, is enough to bring most of them back.

The rules are deliberately narrow:

  • Trigger: 24 hours after signup.
  • Condition: a booking exists but the workflow is incomplete — no plan, no CV, no assessment data.
  • Frequency: once. Ever. We do not chase people through their inbox.

If the mentee has already completed the workflow, the reminder never sends. If they've already received it, it never sends again. It exists to remove one specific piece of friction, not to become a drip campaign.

For the engineers reading

The moving parts, briefly:

  • position_catalog table with an embedding column (pgvector, 1536 dims) and a match_positions RPC gated at 0.65 cosine similarity.
  • plan.functions.ts runs the three-tier resolver (name → alias → semantic) and persists the outcome to plans.sources as { tier, position_id, position_label, similarity, skill_count, task_count }.
  • Existing admin action back-fills embeddings for benchmark rows; no runtime dependency on the Gateway during ingest.
  • cv_uploads.extracted_text is the single source of profile context — CVs and LinkedIn PDFs both land here.
  • assessment-reminder webhook + email template, guarded server-side by a sent-once check keyed on user id.

What this changes for you

If you are a mentee: your plan is measured against a real role now, not a guess. If you booked and never finished, you'll hear from us once — and only once — with a nudge that tells you exactly what's left to do.

If you are a fellow builder reading this for the pattern: ground your generative output, measure whether the grounding is doing work, and be ruthless about one-shot notifications. Every extra email is a tax on trust.

More next week.

Want the plan we're describing here? Start with a free assessment — same three-tier grounding, same standard, no shortcuts.

MORE FROM THE JOURNAL
Esperance Brooks

A private mentoring atelier for Solutions Architecture, MLOps, Software Engineering, DevOps, AI Engineering and Cloud.

Practice
  • 1:1 personalised plans
  • Real, sellable portfolio projects
  • Theory + practical drills
Contact

esperancebrooks@buildandship.academy

Terms & ConditionsPrivacy Policy

© 2026 Build & Ship Academy