evaluate-a-candidate

star 66

Score a candidate against an open role - paste a resume or share a LinkedIn URL and I give you a rubric score, the evidence behind it, red flags, and what to probe in interviews. Branches on `source`: `resume` or `linkedin`.

gethouston By gethouston schedule Updated 5/5/2026

name: evaluate-a-candidate description: "Score a candidate against an open role - paste a resume or share a LinkedIn URL and I give you a rubric score, the evidence behind it, red flags, and what to probe in interviews. Branches on source: resume or linkedin." version: 1 category: People featured: yes image: busts-in-silhouette integrations: [googlesheets, googledrive, linkedin, firecrawl]

Evaluate a Candidate

Two paths, same output: one candidate file per applicant, scored against role rubric. Pick source based on what in front of you.

When to use

  • source=resume - "screen this resume", "screen resume stack for {role}", "rank these resumes", "who strongest on pile". Single and batch both supported.
  • source=linkedin - "score {LinkedIn URL}", "is candidate fit for {role}", "rate this profile", "0-100 on this LinkedIn". One per invocation. Batch = run many times.

Both chain into prep-an-interviewer and debrief-an-interview-loop, which expect candidates/{slug}.md exist.

Connections I need

I run external work through Composio. Before this skill runs I check that the categories below are linked. Missing → I name the category, ask you to connect it from the Integrations tab, stop.

  • Files (Google Drive) — pick up resume PDFs you drop. Required when source is resume.
  • Web scrape (Firecrawl) — read public LinkedIn or profile URLs. Required when source is linkedin.
  • Sheets (Google Sheets, Airtable) — write back ranked stack if you want one. Optional.
  • ATS (Ashby, Greenhouse, Lever, Workable) — dedupe and write back candidate state. Optional.

If none of the required categories are connected I stop and ask you to connect the one that matches your source.

Information I need

I read your people context first. For every required field that's missing I ask ONE plain-language question (best modality: connected app > file drop > URL > paste) and wait.

  • Role rubric — Required. Why I need it: I score every candidate against your must-haves. If missing I ask: "What role and level is this candidate for, and what are your top three must-haves?"
  • Resume or profile — Required. Why I need it: I have nothing to evaluate without it. If missing I ask: "Drop the resume in your connected drive folder, or paste the LinkedIn URL."
  • Candidate name — Optional. Why I need it: clean filing under the right slug. If you don't have it I pull it from the resume or profile and keep going.

Parameter: source

  • resume - parses resume PDF(s) from connected Google Drive / Dropbox (or pasted files) via Composio docs tool. Batch-capable: N resumes → each own record AND ranked summary. Output band: pass / borderline / fail.
  • linkedin - scrapes LinkedIn or public-profile URL via Composio web-scrape tool (Firecrawl). Output: 0-100 total + 4-6 sub-scores (level-fit, domain-fit, scope, tenure, culture-signal) with profile evidence cited per sub-score.

Steps

  1. Read ledger, fill gaps with ONE targeted question.

  2. Read context/people-context.md. Missing or empty → tell you: "I need people context first - run set-up-my-people-info skill." Stop. Pull leveling framework for target level.

  3. Read req. Open reqs/{role-slug}.md for criteria rubric. Missing → ask ONE targeted question ("What role? Top 3 must-haves?") and write reqs/{role-slug}.md.

  4. Branch on source:

    • If source = resume:

      1. Locate resumes. Attached or folder connected → run composio search docs to discover docs tool slug (Google Drive / Dropbox) and list PDFs. Paths pasted → use those. Neither → ask ONE question naming best modality ("Connect Google Drive / Dropbox from Integrations, or paste resume files.") and stop.
      2. Parse each resume. Execute docs slug to extract text. Pull structured fields per candidate: name, contact; education (school, degree, dates); roles (company, title, dates, tenure); skills (stated + inferred from role descriptions); notable projects / publications. Mark ambiguous fields UNKNOWN - never infer.
      3. Evaluate against rubric. Per candidate, score each criterion pass / borderline / fail with one-line reason citing resume evidence (or "not stated in resume" → UNKNOWN). Overall band. 3-5 red flags (tenure pattern, skill-gap vs must-haves, unexplained gaps). Never flag protected-class attributes.
      4. Write one record per applicant to candidates/{candidate-slug}.md (slug = kebab-case {first-last}). File exists → append new dated ## Screen {YYYY-MM-DD} section - never overwrite. Per section: Structured fields → Rubric scoring → Overall band → Red flags → Suggested next step (interview / reject with rationale). Atomic write.
      5. More than one resume → build ranked summary table (name → band → one-line reason → candidate path), include in outputs.json summary text.
    • If source = linkedin:

      1. Parse URL. Accept LinkedIn or any public-profile URL. Derive {candidate-slug} from URL or stated name (kebab-case first-last).
      2. Discover scrape tool: composio search web-scrape. Nothing connected → tell you which category to link and stop.
      3. Scrape. Execute slug. Extract: current title + company + tenure; prior roles (company, title, dates, tenure); education; skills (stated + inferred from role / headline); recent activity (posts, publications, speaking); geo if stated. Mark ambiguous fields UNKNOWN. Scrape empty or gated → say so, ask for paste of profile summary.
      4. Score 0-100 against rubric. Break into 4-6 sub-scores (e.g. level-fit, domain-fit, scope-signal, tenure-signal, culture-signal). Each sub-score 0-25 with one-line reason citing profile evidence. Total ≤ 100.
      5. Produce: background summary (3-5 sentences), total + sub-scores with reasoning, 3-5 red flags to probe in interviews. Never infer protected-class attributes.
      6. Write to candidates/{candidate-slug}.md. File exists → append ## LinkedIn Score {YYYY-MM-DD} - never overwrite. Doesn't exist → create with header stub then score section. Atomic write.
  5. Append to outputs.json with:

    {
      "id": "<uuid v4>",
      "type": "candidate-evaluation",
      "title": "<source>  -  <candidate name or stack count>",
      "summary": "<2-3 sentences; for batch: counts by band + top 3>",
      "path": "candidates/<candidate-slug>.md",
      "status": "draft",
      "createdAt": "<ISO>",
      "updatedAt": "<ISO>",
      "domain": "hiring"
    }
    

    Batch resume runs → one entry per batch with path: "candidates/".

  6. Summarize. One paragraph.

    • resume: count screened, bands breakdown, top 3 named with file paths.
    • linkedin: total score, top 2 reasons high/low, top 2 red flags, candidate file path.

Outputs

  • candidates/{candidate-slug}.md per applicant (appended; created if missing).
  • Appends to outputs.json with type: "candidate-evaluation", domain: "hiring".

What I never do

  • Infer or score protected-class attributes (race, gender, age 40+, pregnancy, disability, religion, national origin, sexual orientation, veteran status). Only objective criteria rubric.
  • Invent credentials, references, or claims. Resume / LinkedIn thin or gated → mark UNKNOWN, ask for paste.
  • Overwrite prior candidate sections - always append dated sections.
  • Commit to hire / no-hire. That call yours; I band and flag.
Install via CLI
npx skills add https://github.com/gethouston/houston --skill evaluate-a-candidate
Repository Details
star Stars 66
call_split Forks 49
navigation Branch main
article Path SKILL.md
More from Creator