Skip to content

scoutica evaluate

evaluate answers one question with a number: how well does this candidate fit this role, and would the candidate’s own rules accept it? It shells out to a deterministic scoring engine (scoring.py, shipped alongside the CLI) that produces the same score every time for the same inputs — no AI, no randomness, no network.

Terminal window
scoutica evaluate <card> <role.json> [options]

<card> is either a card directory containing both profile.json and rules.yaml, or a direct path to a profile.json with a rules.yaml sibling. Missing, empty, or malformed rules fail closed; evaluate never substitutes an empty policy. When <card> is omitted, it defaults to the current directory (.).

An evidence.json sitting next to the profile is picked up automatically and can contribute a bonus (see below).

Option Behavior
--json Emit a clean, parseable JSON result to stdout and suppress the decorative header. Use this when an agent (e.g. an apply-to-role gate) needs to parse the verdict.
--role <path> Explicitly specify the role file instead of passing it positionally.
--recruiter <path> Provide the employer’s recruiter_profile.json so industry-based filters can be checked. A missing file is treated as absent context; when blocked industries are configured, the result is needs_context, never a pass.

The card and role may also be supplied positionally: the first bare argument is the card, the second is the role.

The engine runs a fixed four-step pipeline:

Hard filters (tri-state)

Employer-side gates check engagement type, an engagement-derived compensation unit (annual for permanent, daily for contract, monthly for fractional, hourly for advisory), remote/onsite policy, blocked industries, and required-language overlap. A confirmed mismatch produces HARD_REJECT; missing or malformed required context produces NEEDS_CONTEXT. Both stop autonomous action with a score of 0.

Skill score (0–100)

hard_ratio × 70 + preferred_ratio × 30. Hard and preferred skills from the role are matched against the candidate’s skills, tools_and_platforms, specializations, and primary_domains. When a role lists no preferred skills, the preferred component is treated as neutral (0.5).

Bonuses

+10 when the candidate’s evidence.json demonstrates at least 50% of the role’s hard skills, and +5 for an exact seniority match. The final score is capped at 100.

Verdict + candidate-side check

The score maps to a verdict, and the candidate’s rules.yaml is replayed to produce a tri-state decision: pass, reject, or needs_context.

Score Verdict
Any hard filter failed HARD_REJECT
Required filter context missing or malformed NEEDS_CONTEXT
≥ 80 STRONG_MATCH
60–79 MODERATE_MATCH
40–59 WEAK_MATCH
< 40 NO_MATCH

In --json mode, stdout is pure JSON — nothing else is printed — so it’s safe to pipe into jq or an agent gate.

{
"score": 78,
"verdict": "MODERATE_MATCH",
"hard_filters_passed": true,
"rejection_reasons": [],
"skill_breakdown": {
"hard_match": 0.83,
"hard_matched": 5,
"hard_total": 6,
"preferred_match": 0.5,
"preferred_matched": 2,
"preferred_total": 4
},
"bonuses": ["seniority_match"],
"decision": "pass",
"candidate_decision": "pass",
"candidate_accepts": true,
"candidate_reasons": ["all_rules_passed"]
}
Field Meaning
score Final fit score, 0–100.
verdict One of the thresholds above (or HARD_REJECT).
hard_filters_passed false when a mandatory employer filter blocked the candidate.
rejection_reasons Human-readable strings for each failed hard filter (empty when passed).
skill_breakdown Hard/preferred match ratios and counts (null on a hard reject).
bonuses List of applied bonuses, e.g. evidence, seniority_match.
decision Authoritative combined gate: pass, reject, or needs_context.
candidate_decision Candidate-policy side of the tri-state gate.
candidate_accepts Compatibility boolean; true only when candidate_decision is pass.
candidate_reasons Why the candidate side accepted or rejected — including opt-in manual_review: signals.
Terminal window
# Evaluate a card directory against a role (human-readable output)
scoutica evaluate ./alice-card roles/senior-engineer.json
# Same, but emit clean JSON for an agent to parse
scoutica evaluate ./alice-card roles/senior-engineer.json --json
# Point at a specific profile.json and include the employer profile
scoutica evaluate ./alice-card/profile.json --role roles/senior-engineer.json \
--recruiter ./acme-card/recruiter_profile.json
# Gate an apply decision from a script
if [ "$(scoutica evaluate ./alice-card role.json --json | jq -r '.decision')" = "pass" ]; then
echo "Candidate rules accept — proceed to apply."
fi
╔═══════════════════════════════════════════╗
║ Scoutica Fit Score: 78/100 ║
║ Verdict: MODERATE_MATCH ║
╚═══════════════════════════════════════════╝
📊 Skill Match:
Hard skills: 5/6 (83%)
Preferred skills: 2/4 (50%)
🎁 Bonuses: seniority_match
🤖 Candidate-side evaluation:
✅ Rules PASSED — agent would auto-accept
  • Deterministic. The same inputs always yield the same score and verdict. There is no model call and no non-deterministic clock in the scoring path.
  • Offline. evaluate reads local files only. Evidence URLs recorded in evidence.json are never fetched during scoring.
  • The candidate’s rules are law. The candidate side of the result is derived solely from the candidate’s rules.yaml; the scorer’s decision — not the employer’s preference — is authoritative for whether an autonomous agent proceeds.