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.
Command Syntax
Section titled “Command Syntax”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).
Options
Section titled “Options”| 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.
What the scorer computes
Section titled “What the scorer computes”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.
Verdict thresholds
Section titled “Verdict thresholds”| 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 |
The --json result
Section titled “The --json result”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. |
Examples
Section titled “Examples”# 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 parsescoutica evaluate ./alice-card roles/senior-engineer.json --json
# Point at a specific profile.json and include the employer profilescoutica evaluate ./alice-card/profile.json --role roles/senior-engineer.json \ --recruiter ./acme-card/recruiter_profile.json
# Gate an apply decision from a scriptif [ "$(scoutica evaluate ./alice-card role.json --json | jq -r '.decision')" = "pass" ]; then echo "Candidate rules accept — proceed to apply."fiHuman-readable output
Section titled “Human-readable output” ╔═══════════════════════════════════════════╗ ║ 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-acceptDeterminism and privacy posture
Section titled “Determinism and privacy posture”- 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.
evaluatereads local files only. Evidence URLs recorded inevidence.jsonare 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.