Ctrl K
ComunidadGuías…

src/lib/resume-evidence.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { bulletSimilarity } from "@/lib/resume-similarity";

/**
 * Which of a person's own material stands behind a resume bullet.
 *
 * Pure and free of Prisma, because the same question is asked in two places and
 * has to get the same answer: `trace_resume_evidence` asks it on the server
 * about a saved document, and the editor asks it in the browser about the
 * sentence being typed right now. A bullet the panel calls backed and the
 * editor calls unbacked would make both useless.
 *
 * Note what this is NOT: proof. It is word overlap. A bullet that says
 * something true you never wrote down scores zero, and that is the honest
 * answer — the material is missing, not the achievement.
 */

/** A piece of the person's own material a bullet can be traced to. */
export type EvidenceSource = {
  id: string;
  text: string;
  /** Where it came from: "Staff Engineer — Stripe". */
  role: string;
  /** The Role it belongs to, for narrowing to one job's material. */
  roleId: string | null;
  /** A saved highlight, or a line of a role's evidence. Highlights when absent. */
  kind?: "highlight" | "background";
};

export type Backing = {
  /** Best matches first, strongest three at most. */
  sources: (EvidenceSource & { similarity: number })[];
  /** The strongest similarity found, 0 when nothing matched. */
  best: number;
};

/**
 * Below this, an overlap is two sentences sharing ordinary words rather than
 * one standing behind the other. The same threshold the tool has always used;
 * moving it moves both.
 */
export const BACKED = 0.3;

export function backingFor(
  bullet: string,
  sources: EvidenceSource[],
  /**
   * When the resume entry names the Role it was written from, only that job's
   * material can back it — otherwise a bullet about one employer gets
   * "evidence" from a similar bullet about another, which is worse than none.
   */
  roleId?: string,
): Backing {
  const text = bullet.trim();
  if (!text) return { sources: [], best: 0 };
  const pool = roleId ? sources.filter((source) => source.roleId === roleId) : sources;
  const scored = pool
    .map((source) => ({
      ...source,
      similarity: Math.round(bulletSimilarity(source.text, text) * 100) / 100,
    }))
    .filter((source) => source.similarity >= BACKED)
    .sort((a, b) => b.similarity - a.similarity)
    .slice(0, 3);
  return { sources: scored, best: scored[0]?.similarity ?? 0 };
}