Coding

JD - Triage

Try it

Score any job posting on what you want and whether you can get it, then return one concrete action.

What it does

JD - Triage evaluates a pasted job description on two independent dimensions — desirability across five weighted axes against your stored criteria, and candidacy against the posting's must-have requirements — and returns a single action such as apply, stretch, backup, or skip. It bootstraps your criteria profile on first run, hard-gates comp, location, intensity, and red lines with semantic matching, logs every evaluation, and supports history, side-by-side compare, market analysis, and skill-gap planning. Defaults to English, follows your language, works across markets and professions.

When to use it

  • Deciding whether to apply after a recruiter message lands
  • Comparing two offers side-by-side before negotiating
  • Reviewing the last ten roles you evaluated to spot patterns
  • Planning what to learn next for a target role

The skill document

jd-triage · v1.1.1

A JD is evaluated on two independent dimensions, never collapsed into one number:

  • Desirability — do you want this? Five weighted axes scored against the user's stored criteria.
  • Candidacy — can you get this? The JD's stated must-have requirements vs the user's skills inventory.

A role you want but can't get is a stretch, not a skip. A role you can get but don't want is a backup, not an apply. Collapsing both into one star rating destroys that distinction — which is the whole decision.

Responsibilities:

  1. Bootstrap & maintain a criteria profile at ~/.openclaw/workspace/jd_criteria.md.
  2. Evaluate a JD → two-dimension verdict + one action.
  3. Log every evaluation to ~/.openclaw/workspace/jd_history.md.
  4. Analyze / Plan over accumulated history (see references/analysis-commands.md).

Language

Default to English. If the user writes in another language, respond entirely in that language for the rest of the session.

SurfaceLanguage
All conversational output — bootstrap Q&A, verdicts, tables, and the verdict tier as displayedUser's language (English by default)
jd_criteria.md field keys, jd_history.md structural labels, verdict tier names in storageAlways English — grep-friendly, stable across language switches
Stored free-text values (org names, summaries, red-line rationales, JD quotes)The language the user wrote them in, at write time

Never auto-translate stored content. When listing or comparing entries written in different languages, render structural labels in the current language and show stored free text verbatim. Quotes pulled from a JD keep the JD's original language even when the surrounding output is translated — a translated quote is no longer evidence.

State machine

On every invocation, read ~/.openclaw/workspace/jd_criteria.md and branch:

StateConditionAction
S1: MissingFile does not existQuick Start (5 questions) → proceed to the requested command
S2: Schema gapFile exists, schema_version < 3Migrate silently where possible, ask only for fields that cannot be inferred (see references/bootstrap.md § Migration)
S3: FreshComplete, last_updated ≤ 30 days agoProceed directly. Note "Using criteria from ``" in one line
S4: StaleComplete, last_updated > 30 days agoAsk once: "Anything changed — comp, location, red lines, what you're learning? (y/n)". n → refresh timestamp only. y → user names the fields, patch those
S5: Explicit update"update criteria" / /jd-triage update / /jd-triage resetFull bootstrap, current values pre-filled

criteria_version increments on every S1 / S2 / S5 write. S4 "nothing changed" bumps last_updated only.

If the invocation included a JD, continue to Evaluation after the criteria are settled. Otherwise execute the requested command and stop.

Commands

InputAction
A pasted JD, or /jd-triageEvaluate
/jd-triage update | resetS5 bootstrap
/jd-triage quickstartForce the 5-question Quick Start
/jd-triage learnDerive criteria from example JDs (references/bootstrap.md § Derive from examples)
/jd-triage historyLast 10 entries, one line each
/jd-triage compare []Side-by-side table
/jd-triage analyzeMarket signals across history → references/analysis-commands.md
/jd-triage planSkill-gap plan per target role → references/analysis-commands.md

A past role referred to by org name resolves to an ID by grepping jd_history.md.

Evaluation

1. Parse

Extract: title, org name, responsibilities, must-have requirements, preferred requirements (keep these separate — the split drives Candidacy), comp, location and remote policy, intensity signals, reporting line, team size.

If the input has a title but no responsibilities or requirements, stop and ask for the full posting. Do not evaluate a title.

2. Hard gates

Failures produce ❌ OUT and stop — except red lines, which are weighted (below).

  • comp_floor — compare like for like: the JD's basis (base / total / hourly) against the floor's basis, in the same currency and period. If the bases differ or the currency is different, convert only if the user supplied a rate; otherwise mark unknown and raise an open question. If the JD states no comp at all, mark unknown and continue — never auto-fail on missing comp.

  • locations / remote_ok — JD location must be in the list, or the JD must be remote-eligible under the user's remote_ok setting.

  • intensity_tier — the JD's implied tier must not exceed the user's. Signal patterns are per-language in references/intensity-signals.md. When the JD gives no signal either way, assume the user's own tier (no penalty) and say so.

  • red_lines — match semantically, not by substring. A red line is {pattern, why}; use the why to decide whether a phrase in a different language or different wording is the same thing. Then weight by position:

    Where the matched responsibility sitsVerdict
    In the title, in the first 1–2 responsibility bullets, or plausibly >30% of the role❌ OUT — core
    Only in tail bullets, framed as support/assist/partner-with⚠️ CONDITIONAL — score normally, flag it, raise an open question
    Only in the requirements/preferred section, not in the dutiesNote as an open question, do not gate

    Always cite the matched phrase and its location ("bullet 6 of 7, framed as 'support'"). A bare keyword is not a citation — the user needs the framing to judge.

    Semantic matching cuts both ways: it must not fire on incidental use. A red line of "growth" does not match "growth mindset" in a values paragraph. State what you matched and let the user correct you.

3. Desirability — 5 weighted axes

Score each 1–5 per references/scoring.md. Weights come from axis_weights in the criteria file (defaults there too).

AxisScored againstDefault weight
Role fittarget_title_keywords, seniority and scope of the role25%
Domain fittarget_domains — what the org actually does20%
Org fitorg_traits — user-described traits with their own weights15%
Vibe fitvibe_anchors_positive / _negative, each carrying a why25%
Comp fitcomp_floor and profile.current_comp15%

Never pad. If the JD lacks the information for an axis, output (info insufficient) and exclude that axis from the weighted average, renormalizing the remaining weights. A 3★ placeholder is a fabricated data point that silently moves the verdict.

Vibe must cite. Every vibe score names at least one anchor and quotes the JD phrase that triggered it, and reasons from the anchor's why — not from what the model happens to know about that organization. Anchors may be small or local; if the model has no knowledge of the named org, the why is the only valid basis. Adjective-only judgments ("feels corporate") are forbidden.

4. Candidacy — can you get it?

If skills is empty — the normal state after Quick Start — ask for it once, inline, before scoring: "To score whether you can get this, I need your skills: what could you be interviewed on today, and what are you actively learning?" Save the answer so this is never asked twice.

Compare the JD's must-have requirements against skills and profile:

  • Each requirement scores 1.0 if it matches skills.mastered, 0.5 if it matches skills.learning, 0 otherwise.
  • Years-of-experience counts as one requirement; met if profile.years_of_experience is within one year of the stated minimum.
  • Preferred / nice-to-have requirements are excluded from the denominator and reported separately.
  • Hit rate → tier: Likely ≥75%, Plausible 40–74%, Stretch <40%.
  • Fewer than 3 stated must-haves → Unknown; do not guess, raise an open question instead.

Do not invent requirements the JD does not state. Do not map seniority labels across markets (an L5 and a P7 and a 主管 are not comparable) — count stated requirements only.

Report gaps honestly in both directions: no inflation ("you basically have this"), no catastrophizing. A gap is a fact plus how it is usually probed in an interview, not a disqualification. Items in skills.learning are real partial credit — say so.

5. Decide

Apply in this order. The first rule that fires wins; stop there.

  1. Hard gate failed → ❌ OUT

  2. Red line in a non-core responsibility, or an unknown that could flip the verdict (comp, location, or scope) → ⚠️ CONDITIONAL

    Test whether the unknown can actually flip anything before invoking this. An unknown that is already bounded on the deciding side is an open question, not a conditional: a posting quoting €95,000 base against a €90,000 total floor leaves comp unscoreable, but the gate is settled — base alone cannot make the total fall below the floor. Say so and carry on to the matrix. Reserve CONDITIONAL for unknowns whose resolution genuinely changes the answer.

  3. Otherwise → look up the matrix

Desirability tier from the weighted average — checked top to bottom, first match wins, so a high average with one collapsed axis falls through rather than qualifying:

  • Strong — ≥ 4.0 and no axis ≤ 2
  • Good — ≥ 3.2 and no axis = 1
  • Weak — ≥ 2.4
  • Poor — < 2.4

One override: vibe ≤ 2★ caps desirability at Weak, whatever the average says. Vibe is the axis people rationalize away and regret.

LikelyPlausibleStretch
Strong🔥 Apply now🔥 Apply now🎯 Stretch apply
Good✅ Apply✅ Apply🎯 Stretch apply
Weak🗄️ Backup🗄️ Backup❌ Skip
Poor❌ Skip❌ Skip❌ Skip

Candidacy Unknown → read the Plausible column, mark the result provisional (✅ Apply (provisional)), and put a question about the actual requirements first under Open questions. Never silently drop to a one-dimensional verdict.

Desirability too thin to score (three or more axes insufficient, per references/scoring.md) → the verdict is ⚠️ CONDITIONAL, written as Desirability: (too thin to score). There is no tier to report and no matrix to read; what the posting is missing goes under Open questions.

6. Log

Append to ~/.openclaw/workspace/jd_history.md — format and ID scheme in references/history.md. Create the file if missing.

The evaluation is not complete until this write succeeds. Confirm it on the last line of the output (Logged: JD-…). If the write fails, say so explicitly — never let it fail silently.

7. Output

Two surfaces, two rules. Do not let the second one leak into the first.

On screen — the user's language. Everything in the template below is a label, not a literal: the section headings, the axis names, and the tier itself are all translated into the language of the conversation. Keep the shape — line order, star glyphs, arrows, emoji, the / figures. An evaluation written for a Chinese speaker reads in Chinese throughout; the only fragments that stay in another language are quotes lifted from the posting, which are evidence and are never translated.

In jd_history.md — always English. The stored Action field carries the tier token exactly as spelled here, in this casing, because history and compare parse it:

Apply now · Apply · Stretch apply · Backup · Skip · OUT · CONDITIONAL

Never store the action wording in place of the tier: a conditional evaluation stores CONDITIONAL, not Confirm before applying. What the reader saw on screen may match neither string — it was that tier, in their language.

If the criteria were reused rather than collected, put Using criteria from on its own line above the verdict line, never below or inside it — translated like everything else on screen.

           Desirability:    Candidacy: 

Want it
  Role fit     ★★★★☆
  Domain       ★★★★★
  Org          ★★★☆☆
  Vibe         ★★☆☆☆   anchor "" — ""
  Comp         ★★★★☆
  → weighted /5

Can get it
  Meets / stated must-haves
  ✅             ⚠️             ❌ 


Open questions
- 

Logged: JD-YYYYMMDD-NNN

CONDITIONAL — same shape, but the one-liner must carry the conditional explicitly: "Fits if is confirmed; OUT if turns out to be core." Action is always "Confirm before applying", and the deciding question goes first under Open questions.

OUT — short form, but never empty-handed:

❌ OUT
Triggered: 


Matched anyway
- 

Logged: JD-YYYYMMDD-NNN

The "Matched anyway" block exists so that rejections still accumulate signal about what to look for.

Behavioral constraints

  • OUT means OUT. Do not soften because the user is already emotionally invested in the role.
  • Never pad a score. Missing information is (info insufficient) and drops out of the average — not 3★.
  • Never pre-fill from training data. Criteria values come only from the user. Do not infer comp norms, city tiers, org reputations, or what a company is "known to be like".
  • Reason from the user's why, not from fame. An anchor the model has never heard of must work exactly as well as a famous one.
  • Red lines are weighted, not literal, and semantic, not substring.
  • The output template is a shape, not a script. Its English wording stands in for the user's language — headings, axis names and the tier itself are all translated. Only two things resist translation: values stored in jd_history.md, and quotes taken from the posting.
  • Keep the two dimensions apart. Never average desirability and candidacy together; never let "hard to get" lower the desirability score or vice versa.
  • Open questions are output, not internal state. An unknown that could flip the verdict gets written down as a question to ask, phrased so it can be sent to a recruiter as-is.
  • One JD at a time. Multiple pasted JDs are evaluated separately, then optionally compared.
  • No hollow encouragement. No "good luck", no "hope this helps".
  • Trust hand edits. If the user edited jd_criteria.md, parse what is there. If a field is malformed, quote the line and ask — never silently overwrite.
  • Analyze and plan need real data. Below the thresholds in references/analysis-commands.md, say so and stop.

Detection triggers

  • A pasted block that reads like a job posting: a title-like line plus responsibilities or requirements. Length alone is not a trigger — do not claim any long paste.
  • /jd-triage and its subcommands.
  • "should I apply", "is this role worth it", "evaluate this JD", "what should I learn next", "what patterns do you see in the roles I've looked at", and their equivalents in the user's language.

Files

FileLoaded when
assets/criteria-template.yamlWriting the criteria file
assets/presets/*.yamlQuick Start, to pre-fill structure
references/bootstrap.mdS1 / S2 / S5, or /jd-triage learn
references/scoring.mdEvery evaluation
references/intensity-signals.mdIntensity gate, when the JD is not in English
references/history.mdLogging, history, compare
references/analysis-commands.mdanalyze, plan

Load a reference only when its flow runs.

Questions people ask

Does it collapse the verdict into one star rating?
No. Desirability and candidacy are scored and reported separately, then mapped to one action through an explicit matrix — a stretch is not the same as a skip.
What if the JD omits the salary?
Comp is marked unknown and listed as an open question; it does not auto-fail the evaluation. The evaluation continues unless resolving that unknown would actually change the decision.
What does it need from me on first run?
A five-question Quick Start builds your criteria profile, saved at ~/.openclaw/workspace/jd_criteria.md. Subsequent evaluations reuse it until you update or reset it.

Related skills

Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.

by Iván2 installs

Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.

by fly0pants

Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.

by johnpatternai21 installs8 stars

Run Git operations — commits, branches, merges, rebases, conflict resolution, and recovery — with safety rules enforced.

by Iván532 installs31 stars

Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.

by nssa.io1.0k installs47 stars